Browse Source

Bugfixes:
- Fixing AES-CMAC algorithm
- Using SHA512 to hash file stubs (much faster than SHA256 and AES-CMAC)
- Hardening enclave interface (still work-in-progress); delt with issue #28
- Handling socket/pipe polling better
- Allowing setting the lowest heap address in enclaves ('sgx.heap_min' in manifest)
- Fixing the pipe between processes (for SGX)
- Fixing race condition in futex handling in LibOS
- Inheriting epoll handles in forked chilren
- Assigning signal code (now only hard-coding FPE_INTDIV, BUS_ADRERR, SEGV_ACCERR, SEGV_MAPERR)
- Fixing race condition in vma allocation (likely to fix bug() in bookkeep/shim_vma.c)
- Clearer debug message in LibOS
- Fixing calling convention in LibOS and glibc
- Fixing futex behavior (FUTEX_WAIT takes relative time, FUTEX_WAIT_BITSET takes absolute time)
- More system call implemented
- More application working (NGINX)

Chia-Che Tsai 7 years ago
parent
commit
76967f39d3
100 changed files with 13410 additions and 209 deletions
  1. 1 0
      LibOS/shim/include/shim_handle.h
  2. 1 1
      LibOS/shim/include/shim_internal.h
  3. 2 1
      LibOS/shim/include/shim_signal.h
  4. 3 2
      LibOS/shim/include/shim_table.h
  5. 4 0
      LibOS/shim/include/shim_thread.h
  6. 8 0
      LibOS/shim/src/bookkeep/shim_handle.c
  7. 26 8
      LibOS/shim/src/bookkeep/shim_signal.c
  8. 51 7
      LibOS/shim/src/bookkeep/shim_vma.c
  9. 2 2
      LibOS/shim/src/fs/chroot/fs.c
  10. 1 1
      LibOS/shim/src/fs/proc/fs.c
  11. 2 1
      LibOS/shim/src/fs/proc/thread.c
  12. 5 45
      LibOS/shim/src/shim_async.c
  13. 2 2
      LibOS/shim/src/shim_init.c
  14. 213 8
      LibOS/shim/src/shim_parser.c
  15. 5 5
      LibOS/shim/src/shim_syscalls.c
  16. 1 1
      LibOS/shim/src/sys/shim_clone.c
  17. 72 3
      LibOS/shim/src/sys/shim_epoll.c
  18. 13 0
      LibOS/shim/src/sys/shim_fcntl.c
  19. 51 34
      LibOS/shim/src/sys/shim_futex.c
  20. 5 7
      LibOS/shim/src/sys/shim_mmap.c
  21. 21 0
      LibOS/shim/src/sys/shim_sigaction.c
  22. 1 1
      LibOS/shim/src/sys/shim_stat.c
  23. 3 0
      LibOS/shim/src/syscallas.S
  24. 10 0
      LibOS/shim/test/Makefile
  25. 5 4
      LibOS/shim/test/apps/apache/Makefile
  26. 1 1
      LibOS/shim/test/apps/apache/httpd.manifest.template
  27. 12 0
      LibOS/shim/test/apps/curl/Makefile
  28. 91 0
      LibOS/shim/test/apps/curl/curl.manifest.template
  29. 65 0
      LibOS/shim/test/apps/curl/gai.conf
  30. 8 0
      LibOS/shim/test/apps/curl/hosts
  31. 2 0
      LibOS/shim/test/apps/curl/resolv.conf
  32. 2 0
      LibOS/shim/test/apps/gcc/gcc.manifest.template
  33. 6998 0
      LibOS/shim/test/apps/gcc/test_files/bzip2.c
  34. 6 35
      LibOS/shim/test/apps/lighttpd/Makefile
  35. 1 1
      LibOS/shim/test/apps/lighttpd/lighttpd-angel.manifest.template
  36. 1 1
      LibOS/shim/test/apps/lighttpd/lighttpd.manifest.template
  37. 1 4
      LibOS/shim/test/apps/lmbench/Makefile.lmbench
  38. 6 0
      LibOS/shim/test/apps/lmbench/manifest.template
  39. 8 2
      LibOS/shim/test/apps/ltp/Makefile
  40. 11 0
      LibOS/shim/test/apps/ltp/Makefile.testcases
  41. 6 4
      LibOS/shim/test/apps/ltp/manifest.template
  42. 63 0
      LibOS/shim/test/apps/nginx/Makefile
  43. 62 0
      LibOS/shim/test/apps/nginx/etc/group
  44. 19 0
      LibOS/shim/test/apps/nginx/etc/nsswitch.conf
  45. 36 0
      LibOS/shim/test/apps/nginx/etc/passwd
  46. 35 0
      LibOS/shim/test/apps/nginx/http.t.template
  47. BIN
      LibOS/shim/test/apps/nginx/nginx-1.10.1.tar.gz
  48. 24 0
      LibOS/shim/test/apps/nginx/nginx-tests/LICENSE
  49. 39 0
      LibOS/shim/test/apps/nginx/nginx-tests/README
  50. 110 0
      LibOS/shim/test/apps/nginx/nginx-tests/auth_basic.t
  51. 127 0
      LibOS/shim/test/apps/nginx/nginx-tests/autoindex.t
  52. 146 0
      LibOS/shim/test/apps/nginx/nginx-tests/dav.t
  53. 94 0
      LibOS/shim/test/apps/nginx/nginx-tests/fastcgi.t
  54. 100 0
      LibOS/shim/test/apps/nginx/nginx-tests/fastcgi_cache.t
  55. 122 0
      LibOS/shim/test/apps/nginx/nginx-tests/fastcgi_header_params.t
  56. 98 0
      LibOS/shim/test/apps/nginx/nginx-tests/gzip.t
  57. 78 0
      LibOS/shim/test/apps/nginx/nginx-tests/gzip_flush.t
  58. 137 0
      LibOS/shim/test/apps/nginx/nginx-tests/http_error_page.t
  59. 82 0
      LibOS/shim/test/apps/nginx/nginx-tests/http_expect_100_continue.t
  60. 85 0
      LibOS/shim/test/apps/nginx/nginx-tests/http_location.t
  61. 130 0
      LibOS/shim/test/apps/nginx/nginx-tests/http_server_name.t
  62. 446 0
      LibOS/shim/test/apps/nginx/nginx-tests/lib/Test/Nginx.pm
  63. 109 0
      LibOS/shim/test/apps/nginx/nginx-tests/lib/Test/Nginx/IMAP.pm
  64. 106 0
      LibOS/shim/test/apps/nginx/nginx-tests/lib/Test/Nginx/POP3.pm
  65. 122 0
      LibOS/shim/test/apps/nginx/nginx-tests/lib/Test/Nginx/SMTP.pm
  66. 92 0
      LibOS/shim/test/apps/nginx/nginx-tests/limit_req.t
  67. 127 0
      LibOS/shim/test/apps/nginx/nginx-tests/mail_imap.t
  68. 122 0
      LibOS/shim/test/apps/nginx/nginx-tests/mail_pop3.t
  69. 236 0
      LibOS/shim/test/apps/nginx/nginx-tests/mail_smtp.t
  70. 70 0
      LibOS/shim/test/apps/nginx/nginx-tests/mail_smtp_greeting_delay.t
  71. 141 0
      LibOS/shim/test/apps/nginx/nginx-tests/mail_smtp_xclient.t
  72. 93 0
      LibOS/shim/test/apps/nginx/nginx-tests/memcached.t
  73. 99 0
      LibOS/shim/test/apps/nginx/nginx-tests/memcached_fake.t
  74. 82 0
      LibOS/shim/test/apps/nginx/nginx-tests/not_modified.t
  75. 72 0
      LibOS/shim/test/apps/nginx/nginx-tests/perl.t
  76. 90 0
      LibOS/shim/test/apps/nginx/nginx-tests/perl_gzip.t
  77. 121 0
      LibOS/shim/test/apps/nginx/nginx-tests/proxy.t
  78. 174 0
      LibOS/shim/test/apps/nginx/nginx-tests/proxy_cache.t
  79. 116 0
      LibOS/shim/test/apps/nginx/nginx-tests/proxy_chunked.t
  80. 149 0
      LibOS/shim/test/apps/nginx/nginx-tests/proxy_noclose.t
  81. 103 0
      LibOS/shim/test/apps/nginx/nginx-tests/proxy_store.t
  82. 84 0
      LibOS/shim/test/apps/nginx/nginx-tests/proxy_xar.t
  83. 65 0
      LibOS/shim/test/apps/nginx/nginx-tests/random_index.t
  84. 120 0
      LibOS/shim/test/apps/nginx/nginx-tests/range.t
  85. 93 0
      LibOS/shim/test/apps/nginx/nginx-tests/range_flv.t
  86. 226 0
      LibOS/shim/test/apps/nginx/nginx-tests/rewrite.t
  87. 164 0
      LibOS/shim/test/apps/nginx/nginx-tests/rewrite_unescape.t
  88. 94 0
      LibOS/shim/test/apps/nginx/nginx-tests/scgi.t
  89. 147 0
      LibOS/shim/test/apps/nginx/nginx-tests/secure_link.t
  90. 129 0
      LibOS/shim/test/apps/nginx/nginx-tests/ssi.t
  91. 90 0
      LibOS/shim/test/apps/nginx/nginx-tests/ssi_include_big.t
  92. 65 0
      LibOS/shim/test/apps/nginx/nginx-tests/ssi_waited.t
  93. 54 0
      LibOS/shim/test/apps/nginx/nginx.manifest.template
  94. 8 5
      LibOS/shim/test/apps/openjdk/Makefile
  95. 46 0
      LibOS/shim/test/apps/openjdk/java-local.manifest.template
  96. 30 10
      LibOS/shim/test/apps/openjdk/java.manifest.template
  97. 9 5
      LibOS/shim/test/apps/openjdk/run-java
  98. 0 6
      LibOS/shim/test/apps/python/.gitignore
  99. 1 1
      LibOS/shim/test/apps/python/Makefile
  100. 1 1
      LibOS/shim/test/apps/r/Makefile

+ 1 - 0
LibOS/shim/include/shim_handle.h

@@ -325,6 +325,7 @@ struct shim_handle {
     struct shim_mount *     fs;
     struct shim_qstr        path;
     struct shim_dentry *    dentry;
+    struct list_head        epolls;
 
     struct shim_qstr        uri;    /* URI representing this handle, it is not
                                      * necessary to be set. */

+ 1 - 1
LibOS/shim/include/shim_internal.h

@@ -141,7 +141,7 @@ static inline void do_pause (void);
 #if USE_PAUSE == 1
 # define pause() do { do_pause(); } while (0)
 #else
-# define pause() do {} while (0)
+# define pause() do { asm volatile ("int $3"); } while (0)
 #endif
 
 #define bug()                                                               \

+ 2 - 1
LibOS/shim/include/shim_signal.h

@@ -14,7 +14,7 @@ struct shim_signal_handle {
 };
 
 #define NUM_SIGS            64
-#define NUM_KNOWN_SIGS      22
+#define NUM_KNOWN_SIGS      32
 
 # define BITS_PER_WORD sizeof(unsigned long)
 /* The standard def of this macro is dumb */
@@ -102,6 +102,7 @@ struct shim_signal {
     siginfo_t   info;
     bool        context_stored;
     ucontext_t  context;
+    PAL_CONTEXT * pal_context;
 };
 
 #define MAX_SIGNAL_LOG      32

+ 3 - 2
LibOS/shim/include/shim_table.h

@@ -390,7 +390,7 @@ int shim_do_setsockopt (int fd, int level, int optname, char * optval,
 int shim_do_getsockopt (int fd, int level, int optname, char * optval,
                         int * optlen);
 int shim_do_clone (int flags, void * user_stack_addr, int * parent_tidptr,
-                   void * tls, int * child_tidptr);
+                   int * child_tidptr, void * tls);
 int shim_do_fork (void);
 int shim_do_vfork (void);
 int shim_do_execve (const char * file, const char ** argv, const char ** envp);
@@ -442,6 +442,7 @@ pid_t shim_do_getpgrp (void);
 int shim_do_setsid (void);
 int shim_do_getpgid (pid_t pid);
 int shim_do_getsid (pid_t pid);
+int shim_do_sigaltstack (const stack_t * ss, stack_t * oss);
 int shim_do_sigsuspend (const __sigset_t * mask);
 void * shim_do_arch_prctl (int code, void * addr);
 int shim_do_setrlimit (int resource, struct __kernel_rlimit * rlim);
@@ -575,7 +576,7 @@ int shim_setsockopt (int fd, int level, int optname, char * optval, int optlen);
 int shim_getsockopt (int fd, int level, int optname, char * optval,
                      int * optlen);
 int shim_clone (int flags, void * user_stack_addr, int * parent_tidptr,
-                void * tls, int * child_tidptr);
+                int * child_tidptr, void * tls);
 int shim_fork (void);
 int shim_vfork (void);
 int shim_execve (const char * file, const char ** argv, const char ** envp);

+ 4 - 0
LibOS/shim/include/shim_thread.h

@@ -58,6 +58,7 @@ struct shim_thread {
     struct shim_atomic has_signal;
     struct shim_signal_log * signal_logs;
     bool suspend_on_signal;
+    stack_t signal_altstack;
 
     /* futex robust list */
     void * robust_list;
@@ -83,6 +84,9 @@ struct shim_thread {
     bool user_tcb; /* is tcb assigned by user? */
     void * frameptr;
 
+    /* to save vma bookkeeping */
+    struct { void * addr; uint64_t length; } delayed_bkeep_mmap;
+
     REFTYPE ref_count;
     LOCKTYPE lock;
 

+ 8 - 0
LibOS/shim/src/bookkeep/shim_handle.c

@@ -285,6 +285,7 @@ struct shim_handle * get_new_handle (void)
     REF_SET(new_handle->ref_count, 1);
     create_lock(new_handle->lock);
     new_handle->owner = cur_process.vmid;
+    INIT_LIST_HEAD(&new_handle->epolls);
     return new_handle;
 }
 
@@ -454,6 +455,8 @@ void open_handle (struct shim_handle * hdl)
 #endif
 }
 
+extern int delete_from_epoll_handles (struct shim_handle * handle);
+
 void close_handle (struct shim_handle * hdl)
 {
     int opened = REF_DEC(hdl->opened);
@@ -487,6 +490,8 @@ void close_handle (struct shim_handle * hdl)
                 hdl->fs->fs_ops->close)
                 hdl->fs->fs_ops->close(hdl);
         }
+
+        delete_from_epoll_handles(hdl);
     }
 
     put_handle(hdl);
@@ -807,6 +812,8 @@ BEGIN_CP_FUNC(handle)
         if (hdl->type == TYPE_EPOLL)
             DO_CP(epoll_fd, &hdl->info.epoll.fds, &new_hdl->info.epoll.fds);
 
+        INIT_LIST_HEAD(&new_hdl->epolls);
+
         unlock(hdl->lock);
         ADD_CP_FUNC_ENTRY(off);
     } else {
@@ -825,6 +832,7 @@ BEGIN_RS_FUNC(handle)
 
     CP_REBASE(hdl->fs);
     CP_REBASE(hdl->dentry);
+    CP_REBASE(hdl->epolls);
 
     create_lock(hdl->lock);
 

+ 26 - 8
LibOS/shim/src/bookkeep/shim_signal.c

@@ -155,6 +155,7 @@ void deliver_signal (siginfo_t * info, PAL_CONTEXT * context)
     memset(signal, 0, sizeof(struct shim_signal));
     __store_info(info, signal);
     __store_context(tcb, context, signal);
+    signal->pal_context = context;
 
     if ((tcb->context.preempt & ~SIGNAL_DELAYED) > 1)
         goto delay;
@@ -187,11 +188,12 @@ out:
     __enable_preempt(tcb);
 }
 
-#define ALLOC_SIGINFO(signo, member, value)                 \
+#define ALLOC_SIGINFO(signo, code, member, value)           \
     ({                                                      \
         siginfo_t * _info = __alloca(sizeof(siginfo_t));    \
         memset(_info, 0, sizeof(siginfo_t));                \
         _info->si_signo = (signo);                          \
+        _info->si_code = (code);                            \
         _info->member = (value);                            \
         _info;                                              \
     })
@@ -232,7 +234,7 @@ static void divzero_upcall (PAL_PTR event, PAL_NUM arg, PAL_CONTEXT * context)
     if (context)
         debug("arithmetic fault at %p\n", context->IP);
 
-    deliver_signal(ALLOC_SIGINFO(SIGFPE, si_addr, (void *) arg), context);
+    deliver_signal(ALLOC_SIGINFO(SIGFPE, FPE_INTDIV, si_addr, (void *) arg), context);
 
 ret_exception:
     DkExceptionReturn(event);
@@ -251,16 +253,28 @@ internal:
         debug("memory fault at %p (IP = %p)\n", arg, context->IP);
 
     struct shim_vma * vma = NULL;
-    if (!lookup_supervma((void *) arg, 0, &vma)) {
+    int signo = SIGSEGV;
+    int code;
+    if (!arg) {
+        code = SEGV_MAPERR;
+    } else if (!lookup_supervma((void *) arg, 0, &vma)) {
         if (vma->flags & VMA_INTERNAL) {
             put_vma(vma);
             goto internal;
         }
+        if (vma->file) {
+            /* XXX: need more sophisticated judgement */
+            signo = SIGBUS;
+            code = BUS_ADRERR;
+        } else {
+            code = SEGV_ACCERR;
+        }
         put_vma(vma);
+    } else {
+        code = SEGV_MAPERR;
     }
 
-    int signo = SIGSEGV;
-    deliver_signal(ALLOC_SIGINFO(signo, si_addr, (void *) arg), context);
+    deliver_signal(ALLOC_SIGINFO(signo, code, si_addr, (void *) arg), context);
 
 ret_exception:
     DkExceptionReturn(event);
@@ -285,7 +299,7 @@ internal:
         if (vma)
             put_vma(vma);
 
-        deliver_signal(ALLOC_SIGINFO(SIGILL, si_addr, (void *) arg), context);
+        deliver_signal(ALLOC_SIGINFO(SIGILL, ILL_ILLOPC, si_addr, (void *) arg), context);
     } else {
         if (vma)
             put_vma(vma);
@@ -302,7 +316,7 @@ static void quit_upcall (PAL_PTR event, PAL_NUM arg, PAL_CONTEXT * context)
     if (IS_INTERNAL_TID(get_cur_tid()))
         goto ret_exception;
 
-    deliver_signal(ALLOC_SIGINFO(SIGTERM, si_pid, 0), NULL);
+    deliver_signal(ALLOC_SIGINFO(SIGTERM, SI_USER, si_pid, 0), NULL);
 
 ret_exception:
     DkExceptionReturn(event);
@@ -313,7 +327,7 @@ static void suspend_upcall (PAL_PTR event, PAL_NUM arg, PAL_CONTEXT * context)
     if (IS_INTERNAL_TID(get_cur_tid()))
         goto ret_exception;
 
-    deliver_signal(ALLOC_SIGINFO(SIGINT, si_pid, 0), NULL);
+    deliver_signal(ALLOC_SIGINFO(SIGINT, SI_USER, si_pid, 0), NULL);
 
 ret_exception:
     DkExceptionReturn(event);
@@ -444,6 +458,10 @@ __handle_one_signal (shim_tcb_t * tcb, int sig, struct shim_signal * signal)
 
     if (context)
         memcpy(&tcb->context, context, sizeof(struct shim_context));
+
+    if (signal->pal_context)
+        memcpy(signal->pal_context, signal->context.uc_mcontext.gregs,
+               sizeof(PAL_CONTEXT));
 }
 
 void __handle_signal (shim_tcb_t * tcb, int sig, ucontext_t * uc)

+ 51 - 7
LibOS/shim/src/bookkeep/shim_vma.c

@@ -48,6 +48,26 @@ static LOCKTYPE vma_mgr_lock;
 #define system_lock()       lock(vma_mgr_lock)
 #define system_unlock()     unlock(vma_mgr_lock)
 
+static inline void * __vma_malloc (size_t size)
+{
+    struct shim_thread * thread = get_cur_thread();
+
+    if (!thread)
+        return system_malloc(size);
+
+    size = ALIGN_UP(size);
+    void * addr = (void *) DkVirtualMemoryAlloc(NULL, size, 0,
+                                                PAL_PROT_WRITE|PAL_PROT_READ);
+
+    debug("allocate %p-%p for vmas\n", addr, addr + size);
+    thread->delayed_bkeep_mmap.addr = addr;
+    thread->delayed_bkeep_mmap.length = size;
+    return addr;
+}
+
+#undef system_malloc
+#define system_malloc(size) __vma_malloc(size)
+
 #define OBJ_TYPE struct shim_vma
 #include <memmgr.h>
 
@@ -181,6 +201,26 @@ static int __bkeep_mmap (void * addr, uint64_t length, int prot, int flags,
 static int __bkeep_mprotect (void * addr, uint64_t length, int prot,
                              const int * flags);
 
+static void __check_delayed_bkeep (void)
+{
+    struct shim_thread * thread = get_cur_thread();
+
+    if (!thread)
+        return;
+    if (!thread->delayed_bkeep_mmap.addr)
+        return;
+
+    void * bkeep_addr = thread->delayed_bkeep_mmap.addr;
+    uint64_t bkeep_length = thread->delayed_bkeep_mmap.length;
+    thread->delayed_bkeep_mmap.addr = NULL;
+    thread->delayed_bkeep_mmap.length = 0;
+
+    __bkeep_mmap(bkeep_addr, bkeep_length,
+                 PROT_READ|PROT_WRITE,
+                 MAP_PRIVATE|MAP_ANONYMOUS|VMA_INTERNAL,
+                 NULL, 0, NULL);
+}
+
 static struct shim_vma * get_new_vma (void)
 {
     struct shim_vma * tmp =
@@ -258,12 +298,9 @@ static int __bkeep_mmap (void * addr, uint64_t length,
             prev->length <= length) { /* find a vma at the same addr */
             cont = tmp = prev;
         } else { /* need to add a new vma */
-            unlock(vma_list_lock);
-
             if (!(tmp = get_new_vma()))
                 return -ENOMEM;
 
-            lock(vma_list_lock);
             if (prev) { /* has a precendent vma */
                 if (test_vma_endin(prev, addr, length)) {
                     if (!check_vma_flags(prev, &flags)) {
@@ -340,9 +377,9 @@ int bkeep_mmap (void * addr, uint64_t length, int prot, int flags,
     lock(vma_list_lock);
     int ret = __bkeep_mmap(addr, length, prot, flags, file, offset,
                            comment);
-    assert_vma();
+    //assert_vma();
+    __check_delayed_bkeep();
     unlock(vma_list_lock);
-
     return ret;
 }
 
@@ -430,7 +467,8 @@ int bkeep_munmap (void * addr, uint64_t length, const int * flags)
 
     lock(vma_list_lock);
     int ret = __bkeep_munmap(addr, length, flags);
-    assert_vma();
+    //assert_vma();
+    __check_delayed_bkeep();
     unlock(vma_list_lock);
 
     return ret;
@@ -611,7 +649,7 @@ int bkeep_mprotect (void * addr, uint64_t length, int prot, const int * flags)
 
     lock(vma_list_lock);
     int ret = __bkeep_mprotect(addr, length, prot, flags);
-    assert_vma();
+    //assert_vma();
     unlock(vma_list_lock);
 
     return ret;
@@ -642,6 +680,8 @@ void * get_unmapped_vma (uint64_t length, int flags)
 
     lock(vma_list_lock);
 
+    __check_delayed_bkeep();
+
     if (heap_top - heap_bottom < length) {
         unlock(vma_list_lock);
         put_vma(new);
@@ -706,6 +746,8 @@ void * get_unmapped_vma_for_cp (uint64_t length)
 
     lock(vma_list_lock);
 
+    __check_delayed_bkeep();
+
     unsigned long top = (unsigned long) PAL_CB(user_address.end) - length;
     unsigned long bottom = (unsigned long) heap_top;
     int flags = MAP_ANONYMOUS|VMA_UNMAPPED|VMA_INTERNAL;
@@ -1170,6 +1212,8 @@ BEGIN_RS_FUNC(vma)
     assert_vma();
     SAVE_PROFILE_INTERVAL(vma_add_bookkeep);
 
+    __check_delayed_bkeep();
+
     unlock(vma_list_lock);
 
     debug("vma: %p-%p flags %x prot %p\n", vma->addr, vma->addr + vma->length,

+ 2 - 2
LibOS/shim/src/fs/chroot/fs.c

@@ -590,12 +590,12 @@ static inline int __map_buffer (struct shim_handle * hdl, int size)
 
     /* second, reallocate the buffer */
     int bufsize = file->mapsize ? : FILE_BUFMAP_SIZE;
-    int prot = PROT_READ;
+    int prot = PAL_PROT_READ;
     unsigned long mapoff = file->marker & ~(bufsize - 1);
     unsigned long maplen = bufsize;
 
     if (hdl->acc_mode & MAY_WRITE)
-        prot |= PROT_WRITE;
+        prot |= PAL_PROT_WRITE;
 
     while (mapoff + maplen < file->marker + size)
         maplen *= 2;

+ 1 - 1
LibOS/shim/src/fs/proc/fs.c

@@ -287,7 +287,7 @@ retry:
             if (ret < 0)
                 ptr = d;
             else
-                for ( ; d != ptr ; d = d->next)
+                for ( ; d && d != ptr ; d = d->next)
                     last = &d->next;
             continue;
         }

+ 2 - 1
LibOS/shim/src/fs/proc/thread.c

@@ -301,7 +301,7 @@ static int proc_list_thread_each_fd (const char * name,
 
     lock(handle_map->lock);
 
-    for (int i = 0 ; i < handle_map->fd_size ; i++, dirent = dirent->next)
+    for (int i = 0 ; i < handle_map->fd_size ; i++)
         if (handle_map->map[i] &&
             handle_map->map[i]->handle) {
             int d = i, l = 0;
@@ -322,6 +322,7 @@ static int proc_list_thread_each_fd (const char * name,
             for (d = i ; d ; d /= 10)
                 dirent->name[l--] = '0' + d % 10;
             last = &dirent->next;
+            dirent = dirent->next;
         }
 
     unlock(handle_map->lock);

+ 5 - 45
LibOS/shim/src/shim_async.c

@@ -59,6 +59,8 @@ int install_async_event (PAL_HANDLE object, unsigned long time,
 
     unsigned long install_time = DkSystemTimeQuery();
 
+    debug("install async event at %llu\n", install_time);
+
     event->callback     = callback;
     event->arg          = arg;
     event->caller       = get_cur_tid();
@@ -77,58 +79,16 @@ int install_async_event (PAL_HANDLE object, unsigned long time,
         prev = &tmp->list;
     }
 
-    /* 
-     * man page of alarm system call :
-     * DESCRIPTION
-     * alarm() arranges for a SIGALRM signal to be delivered to the 
-	 * calling process in seconds seconds.
-     * If seconds is zero, any pending alarm is canceled.
-     * In any event any previously set alarm() is canceled.
-     */
-    if (!list_empty(&async_list)) {
-        tmp = list_entry((&async_list)->prev, struct async_event, list);
-        /*
-         * any previously set alarm() is canceled.
-         * There should be exactly only one timer pending
-         */
-		list_del(&tmp->list);
-        free(tmp);
-    } else
-	   tmp = NULL;
-    
     INIT_LIST_HEAD(&event->list);
-    if (!time)    // If seconds is zero, any pending alarm is canceled.
-        free(event);
-    else
-        list_add_tail(&event->list, &async_list);   
-    
+    list_add(&event->list, prev);
+
     unlock(async_helper_lock);
 
     if (atomic_read(&async_helper_state) == HELPER_NOTALIVE)
         create_async_helper();
 
     set_event(&async_helper_event, 1);
-
-    /* 
-     * man page of alarm system call :
-     * RETURN VALUE
-     * alarm()  returns the number of seconds remaining until 
-     * any previously scheduled alarm was due to be delivered,
-     * or zero if there was no previously scheduled alarm.
-     * reference : lxr.free-electrons.com/source/kernel/itimer.c?v=2.6.35#L272
-     */
-    unsigned long ret;
-    unsigned long sec = 0;
-    unsigned long usec = 0;
-    
-    if (tmp) {
-    	ret = tmp->expire_time - install_time;
-    	sec = ret / 1000000;
-    	usec = ret % 1000000;
-    	if ((!sec && usec) || usec >=500000)
-    		sec++;
-    }
-    return sec;
+    return 0;
 }
 
 int init_async (void)

+ 2 - 2
LibOS/shim/src/shim_init.c

@@ -266,7 +266,7 @@ void * allocate_stack (size_t size, size_t protect_size, bool user)
 
     if (user) {
         if (bkeep_mmap(stack, size, PROT_READ|PROT_WRITE,
-                       STACK_FLAGS, NULL, 0, "[stack]") < 0)
+                       STACK_FLAGS, NULL, 0, "stack") < 0)
             return NULL;
 
         if (protect_size &&
@@ -461,7 +461,7 @@ int init_manifest (PAL_HANDLE manifest_handle)
 
     bkeep_mmap(addr, ALIGN_UP(size), PROT_READ,
                MAP_PRIVATE|MAP_ANONYMOUS|VMA_INTERNAL, NULL, 0,
-               "[manifest]");
+               "manifest");
 
     root_config = malloc(sizeof(struct config_store));
     root_config->raw_data = addr;

+ 213 - 8
LibOS/shim/src/shim_parser.c

@@ -64,8 +64,11 @@ static void parse_sigmask       (const char *, va_list *);
 static void parse_sigprocmask_how (const char *, va_list *);
 static void parse_timespec      (const char *, va_list *);
 static void parse_sockaddr      (const char *, va_list *);
+static void parse_domain        (const char *, va_list *);
+static void parse_socktype      (const char *, va_list *);
 static void parse_futexop       (const char *, va_list *);
 static void parse_ioctlop       (const char *, va_list *);
+static void parse_fcntlop       (const char *, va_list *);
 static void parse_seek          (const char *, va_list *);
 static void parse_at_fdcwd      (const char *, va_list *);
 static void parse_wait_option   (const char *, va_list *);
@@ -123,7 +126,8 @@ struct parser_table {
     { .slow = 0, .parser = { NULL } }, /* setitimer */
     { .slow = 0, .parser = { NULL } }, /* getpid */
     { .slow = 0, .parser = { NULL } }, /* sendfile */
-    { .slow = 0, .parser = { NULL } }, /* socket */
+    { .slow = 0, .parser = { &parse_domain, &parse_socktype } }, /* socket */
+
     { .slow = 1, .parser = { NULL, &parse_sockaddr } }, /* connect */
     { .slow = 1, .parser = { NULL } }, /* accept */
     { .slow = 0, .parser = { NULL } }, /* sendto */
@@ -136,7 +140,7 @@ struct parser_table {
     { .slow = 0, .parser = { NULL } }, /* getsockname */
     { .slow = 0, .parser = { NULL } }, /* getpeername */
     { .slow = 0, .stop = 3,            /* socketpair */
-      .parser = { NULL, NULL, NULL, &parse_pipe_fds } },
+      .parser = { &parse_domain, &parse_socktype, NULL, &parse_pipe_fds } },
     { .slow = 0, .parser = { NULL } }, /* setsockopt */
     { .slow = 0, .parser = { NULL } }, /* getsockopt */
     { .slow = 1, .parser = { &parse_clone_flags } }, /* clone */
@@ -158,7 +162,7 @@ struct parser_table {
     { .slow = 1, .parser = { NULL } }, /* msgsnd */
     { .slow = 1, .parser = { NULL } }, /* msgrcv */
     { .slow = 1, .parser = { NULL } }, /* msgctl */
-    { .slow = 0, .parser = { NULL } }, /* fcntl */
+    { .slow = 0, .parser = { NULL, &parse_fcntlop } }, /* fcntl */
     { .slow = 0, .parser = { NULL } }, /* flock */
     { .slow = 0, .parser = { NULL } }, /* fsync */
     { .slow = 0, .parser = { NULL } }, /* fdatasync */
@@ -405,7 +409,9 @@ struct parser_table {
 
 static inline int is_pointer (const char * type)
 {
-    return type[strlen(type) - 1] == '*' || strcmp_static(type, "long");
+    return type[strlen(type) - 1] == '*'
+           || strcmp_static(type, "long")
+           || strcmp_static(type, "unsigned long");
 }
 
 #define PRINTF(fmt, ...)                            \
@@ -437,7 +443,7 @@ static inline void parse_pointer_arg (va_list * ap)
 
 static inline void parse_integer_arg (va_list * ap)
 {
-    VPRINTF("%ld", ap);
+    VPRINTF("%d", ap);
 }
 
 static inline void parse_syscall_args (va_list * ap)
@@ -769,7 +775,8 @@ static void parse_pipe_fds (const char * type, va_list * ap)
 #define S(sig) #sig
 
 const char *const siglist[NUM_KNOWN_SIGS + 1] =
-    {   NULL,
+    {
+        S(SIGUNUSED),
         S(SIGHUP),
         S(SIGINT),
         S(SIGQUIT),
@@ -791,14 +798,27 @@ const char *const siglist[NUM_KNOWN_SIGS + 1] =
         S(SIGSTOP),
         S(SIGTSTP),
         S(SIGTTIN),
-        S(SIGTTOU),  };
+        S(SIGTTOU),
+        S(SIGURG),
+        S(SIGXCPU),
+        S(SIGXFSZ),
+        S(SIGVTALRM),
+        S(SIGPROF),
+        S(SIGWINCH),
+        S(SIGIO),
+        S(SIGPWR),
+        S(SIGSYS),
+        S(SIGRTMIN),
+    };
 
 static void parse_signum (const char * type, va_list * ap)
 {
     unsigned int signum = va_arg(*ap, unsigned int);
 
-    if (signum > 0 && signum <= NUM_KNOWN_SIGS)
+    if (signum >= 0 && signum <= NUM_KNOWN_SIGS)
         PUTS(signal_name(signum));
+    else
+        PRINTF("[SIG %d]", signum);
 }
 
 static void parse_sigmask (const char * type, va_list * ap)
@@ -896,6 +916,106 @@ static void parse_sockaddr (const char * type, va_list *ap)
     }
 }
 
+static void parse_domain (const char * type, va_list * ap)
+{
+    int domain = va_arg(*ap, int);
+
+#define PF_UNSPEC   0   /* Unspecified.  */
+#define PF_INET     2   /* IP protocol family.  */
+#define PF_AX25     3   /* Amateur Radio AX.25.  */
+#define PF_IPX      4   /* Novell Internet Protocol.  */
+#define PF_APPLETALK    5   /* Appletalk DDP.  */
+#define PF_ATMPVC   8   /* ATM PVCs.  */
+#define PF_X25      9   /* Reserved for X.25 project.  */
+#define PF_INET6    10  /* IP version 6.  */
+#define PF_NETLINK  16
+#define PF_PACKET   17  /* Packet family.  */
+
+    switch (domain) {
+        case PF_UNSPEC:
+            PUTS("UNSPEC");
+            break;
+        case PF_UNIX:
+            PUTS("UNIX");
+            break;
+        case PF_INET:
+            PUTS("INET");
+            break;
+        case PF_INET6:
+            PUTS("INET6");
+            break;
+        case PF_IPX:
+            PUTS("IPX");
+            break;
+        case PF_NETLINK:
+            PUTS("NETLINK");
+            break;
+        case PF_X25:
+            PUTS("X25");
+            break;
+        case PF_AX25:
+            PUTS("AX25");
+            break;
+        case PF_ATMPVC:
+            PUTS("ATMPVC");
+            break;
+        case PF_APPLETALK:
+            PUTS("APPLETALK");
+            break;
+        case PF_PACKET:
+            PUTS("PACKET");
+            break;
+        default:
+            PUTS("UNKNOWN");
+            break;
+    }
+}
+
+static void parse_socktype (const char * type, va_list * ap)
+{
+    int socktype = va_arg(*ap, int);
+
+    if (socktype & SOCK_NONBLOCK) {
+        socktype &= ~SOCK_NONBLOCK;
+        PUTS("SOCK_NONBLOCK|");
+    }
+
+    if (socktype & SOCK_CLOEXEC) {
+        socktype &= ~SOCK_CLOEXEC;
+        PUTS("SOCK_CLOEXEC|");
+    }
+
+#define SOCK_RAW    3   /* Raw protocol interface.  */
+#define SOCK_RDM    4   /* Reliably-delivered messages.  */
+#define SOCK_SEQPACKET  5   /* Sequenced, reliable, connection-based, */
+#define SOCK_DCCP   6   /* Datagram Congestion Control Protocol.  */
+#define SOCK_PACKET 10  /* Linux specific way of getting packets */
+
+    switch (socktype) {
+        case SOCK_STREAM:
+            PUTS("STREAM");
+            break;
+        case SOCK_DGRAM:
+            PUTS("DGRAM");
+            break;
+        case SOCK_SEQPACKET:
+            PUTS("SEQPACKET");
+            break;
+        case SOCK_RAW:
+            PUTS("RAW");
+            break;
+        case SOCK_RDM:
+            PUTS("RDM");
+            break;
+        case SOCK_PACKET:
+            PUTS("PACKET");
+            break;
+        default:
+            PUTS("UNKNOWN");
+            break;
+    }
+}
+
 static void parse_futexop (const char * type, va_list * ap)
 {
     int op = va_arg(*ap, int);
@@ -947,6 +1067,71 @@ static void parse_futexop (const char * type, va_list * ap)
     }
 }
 
+static void parse_fcntlop (const char * type, va_list * ap)
+{
+    int op = va_arg(*ap, int);
+
+    switch (op) {
+        case F_DUPFD:
+            PUTS("F_DUPFD");
+            break;
+        case F_GETFD:
+            PUTS("F_GETFD");
+            break;
+        case F_SETFD:
+            PUTS("F_SETFD");
+            break;
+        case F_GETFL:
+            PUTS("F_GETFL");
+            break;
+        case F_SETFL:
+            PUTS("F_SETFL");
+            break;
+        case F_GETLK:
+            PUTS("F_GETLK");
+            break;
+        case F_SETLK:
+            PUTS("F_SETLK");
+            break;
+        case F_SETLKW:
+            PUTS("F_SETLKW");
+            break;
+        case F_SETOWN:
+            PUTS("F_SETOWN");
+            break;
+        case F_GETOWN:
+            PUTS("F_GETOWN");
+            break;
+        case F_SETSIG:
+            PUTS("F_SETSIG");
+            break;
+        case F_GETSIG:
+            PUTS("F_GETSIG");
+            break;
+        case F_GETLK64:
+            PUTS("F_GETLK64");
+            break;
+        case F_SETLK64:
+            PUTS("F_SETLK64");
+            break;
+        case F_SETLKW64:
+            PUTS("F_SETLKW64");
+            break;
+        case F_SETOWN_EX:
+            PUTS("F_SETOWN_EX");
+            break;
+        case F_GETOWN_EX:
+            PUTS("F_GETOWN_EX");
+            break;
+        case F_GETOWNER_UIDS:
+            PUTS("F_GETOWNER_UIDS");
+            break;
+        default:
+            PRINTF("OP %d", op);
+            break;
+    }
+}
+
 static void parse_ioctlop (const char * type, va_list * ap)
 {
     int op = va_arg(*ap, int);
@@ -985,6 +1170,26 @@ static void parse_ioctlop (const char * type, va_list * ap)
         PUTS(opnames[op - TCGETS]);
         return;
     }
+
+    if (op >= FIONCLEX && op <= TIOCSERSETMULTI) {
+        const char * opnames[] = {
+            "FIONCLEX",         /* 0x5450 */    "FIOCLEX",          /* 0x5451 */
+            "FIOASYNC",         /* 0x5452 */    "TIOCSERCONFIG",    /* 0x5453 */
+            "TIOCSERGWILD",     /* 0x5454 */    "TIOCSERSWILD",     /* 0x5455 */
+            "TIOCGLCKTRMIOS",   /* 0x5456 */    "TIOCSLCKTRMIOS",   /* 0x5457 */
+            "TIOCSERGSTRUCT",   /* 0x5458 */    "TIOCSERGETLSR",    /* 0x5459 */
+            "TIOCSERGETMULTI",  /* 0x545A */    "TIOCSERSETMULTI",  /* 0x545B */
+        };
+        PUTS(opnames[op - FIONCLEX]);
+        return;
+    }
+
+
+
+#define TIOCMIWAIT	0x545C	/* wait for a change on serial input line(s) */
+#define TIOCGICOUNT	0x545D	/* read serial port __inline__ interrupt counts */
+
+
     PRINTF("OP 0x%04x", op);
 }
 

+ 5 - 5
LibOS/shim/src/shim_syscalls.c

@@ -355,9 +355,9 @@ DEFINE_SHIM_SYSCALL (getsockopt, 5, shim_do_getsockopt, int, int, fd,
                      int, level, int, optname, char *, optval, int *, optlen)
 
 /* clone: sys/shim_clone.c */
-DEFINE_SHIM_SYSCALL (clone, 5, shim_do_clone, int, int, flags, void *,
-                     user_stack_addr, int *, parent_tidptr, void *, tls,
-                     int *, child_tidptr)
+DEFINE_SHIM_SYSCALL (clone, 5, shim_do_clone, int, int, flags,
+                     void *, user_stack_addr, int *, parent_tidptr,
+                     int *, child_tidptr, void *, tls)
 
 /* fork: sys/shim_fork.c */
 DEFINE_SHIM_SYSCALL (fork, 0, shim_do_fork, int)
@@ -590,8 +590,8 @@ SHIM_SYSCALL_PASSTHROUGH (rt_sigqueueinfo, 3, int, int, pid, int, sig,
 
 DEFINE_SHIM_SYSCALL (rt_sigsuspend, 1, shim_do_sigsuspend, int, const __sigset_t *, mask)
 
-SHIM_SYSCALL_PASSTHROUGH (sigaltstack, 2, int, const stack_t *, ss, stack_t *,
-                          oss)
+DEFINE_SHIM_SYSCALL (sigaltstack, 2, shim_do_sigaltstack, int, const stack_t *, ss,
+                     stack_t *, oss)
 
 SHIM_SYSCALL_PASSTHROUGH (utime, 2, int, char *, filename, struct utimbuf *,
                           times)

+ 1 - 1
LibOS/shim/src/sys/shim_clone.c

@@ -158,7 +158,7 @@ int migrate_fork (struct shim_cp_store * cpstore,
  *                    by the parent     */
 
 int shim_do_clone (int flags, void * user_stack_addr, int * parent_tidptr,
-                   void * tls, int * child_tidptr)
+                   int * child_tidptr, void * tls)
 {
     //The Clone Implementation in glibc has setup the child's stack
     //with the function pointer and the argument to the funciton.

+ 72 - 3
LibOS/shim/src/sys/shim_epoll.c

@@ -59,8 +59,10 @@ struct shim_epoll_fd {
     __u64                       data;
     unsigned int                revents;
     struct shim_handle *        handle;
+    struct shim_handle *        epoll;
     PAL_HANDLE                  pal_handle;
     struct list_head            list;
+    struct list_head            back;
 };
 
 int shim_do_epoll_create1 (int flags)
@@ -104,9 +106,15 @@ static void update_epoll (struct shim_epoll_handle * epoll)
     int npals = 0;
     epoll->nread = 0;
 
+    debug("update pal handles of epoll handle %p\n", epoll);
+
     list_for_each_entry(tmp, &epoll->fds, list) {
         if (!tmp->pal_handle)
             continue;
+
+        debug("found handle %p (pal handle %p) from epoll handle %p\n",
+              tmp->handle, tmp->pal_handle, epoll);
+
         epoll->pal_fds[npals] = tmp->fd;
         epoll->pal_handles[npals] = tmp->pal_handle;
         npals++;
@@ -120,6 +128,42 @@ static void update_epoll (struct shim_epoll_handle * epoll)
         set_event(&epoll->event, epoll->nwaiters);
 }
 
+int delete_from_epoll_handles (struct shim_handle * handle)
+{
+    while (1) {
+        lock(handle->lock);
+
+        if (list_empty(&handle->epolls)) {
+            unlock(handle->lock);
+            break;
+        }
+
+        struct shim_epoll_fd * epoll_fd = list_first_entry(&handle->epolls,
+                                 struct shim_epoll_fd, back);
+
+        list_del(&epoll_fd->back);
+        unlock(handle->lock);
+        put_handle(handle);
+
+        struct shim_handle * epoll_hdl = epoll_fd->epoll;
+
+        debug("delete handle %p from epoll handle %p\n", handle,
+              &epoll_hdl->info.epoll);
+
+        lock(epoll_hdl->lock);
+
+        list_del(&epoll_fd->list);
+        free(epoll_fd);
+
+        epoll_hdl->info.epoll.nfds--;
+        update_epoll(&epoll_hdl->info.epoll);
+        unlock(epoll_hdl->lock);
+        put_handle(epoll_hdl);
+    }
+
+    return 0;
+}
+
 int shim_do_epoll_ctl (int epfd, int op, int fd,
                        struct __kernel_epoll_event * event)
 {
@@ -164,14 +208,23 @@ int shim_do_epoll_ctl (int epfd, int op, int fd,
                 goto out;
             }
 
+            debug("add handle %p to epoll handle %p\n", hdl, epoll);
+
             epoll_fd = malloc(sizeof(struct shim_epoll_fd));
             epoll_fd->fd = fd;
             epoll_fd->events = event->events;
             epoll_fd->data = event->data;
             epoll_fd->revents = 0;
             epoll_fd->handle = hdl;
+            epoll_fd->epoll = epoll_hdl;
             epoll_fd->pal_handle = hdl->pal_handle;
 
+            lock(hdl->lock);
+            INIT_LIST_HEAD(&epoll_fd->back);
+            list_add_tail(&epoll_fd->back, &hdl->epolls);
+            unlock(hdl->lock);
+
+            get_handle(epoll_hdl);
             INIT_LIST_HEAD(&epoll_fd->list);
             list_add_tail(&epoll_fd->list, &epoll->fds);
             epoll->nfds++;
@@ -193,10 +246,20 @@ int shim_do_epoll_ctl (int epfd, int op, int fd,
         case EPOLL_CTL_DEL: {
             list_for_each_entry(epoll_fd, &epoll->fds, list)
                 if (epoll_fd->fd == fd) {
+                    struct shim_handle * hdl = epoll_fd->handle;
+                    lock(hdl->lock);
+                    list_del(&epoll_fd->back);
+                    put_handle(hdl);
+                    unlock(hdl->lock);
+
+                    debug("delete handle %p from epoll handle %p\n",
+                          hdl, epoll);
+
                     list_del(&epoll_fd->list);
-                    put_handle(epoll_fd->handle);
-                    free(epoll_fd);
+                    put_handle(epoll_hdl);
                     epoll->nfds--;
+
+                    free(epoll_fd);
                     goto update;
                 }
 
@@ -273,7 +336,10 @@ retry:
 
     list_for_each_entry(epoll_fd, &epoll->fds, list)
         if (polled == epoll_fd->pal_handle) {
-            debug("epoll: fd %d polled\n", epoll_fd->fd);
+
+            debug("epoll: fd %d (handle %p) polled\n", epoll_fd->fd,
+                  epoll_fd->handle);
+
             if (attr.disconnected) {
                 epoll_fd->revents |= EPOLLERR|EPOLLHUP|EPOLLRDHUP;
                 epoll_fd->pal_handle = NULL;
@@ -354,6 +420,7 @@ BEGIN_CP_FUNC(epoll_fd)
         new_epoll_fd->revents = epoll_fd->revents;
         new_epoll_fd->pal_handle = NULL;
         list_add(new_list, &new_epoll_fd->list);
+        INIT_LIST_HEAD(&new_epoll_fd->back);
 
         DO_CP(handle, epoll_fd->handle, &new_epoll_fd->handle);
     }
@@ -374,7 +441,9 @@ BEGIN_RS_FUNC(epoll_fd)
                 list_entry(e, struct shim_epoll_fd, list);
 
         CP_REBASE(epoll_fd->handle);
+        CP_REBASE(epoll_fd->back);
         epoll_fd->pal_handle = epoll_fd->handle->pal_handle;
+        list_add_tail(&epoll_fd->back, &epoll_fd->handle->epolls);
         CP_REBASE(*e);
 
         DEBUG_RS("fd=%d,path=%s,type=%s,uri=%s",

+ 13 - 0
LibOS/shim/src/sys/shim_fcntl.c

@@ -195,6 +195,19 @@ int shim_do_fcntl (int fd, int cmd, unsigned long arg)
         case F_GETLK:
             ret = -ENOSYS;
             break;
+
+        /* F_SETOWN (int)
+         *   Set  the process ID or process group ID that will receive SIGIO
+         *   and SIGURG signals for events on file descriptor fd to the ID given
+         *   in arg.  A process ID is specified as a positive value; a process
+         *   group ID is specified as a negative value.  Most commonly, the
+         *   calling process specifies itself as the owner (that is, arg is
+         *   specified as getpid(2)).
+         */
+        case F_SETOWN:
+            ret = 0;
+            /* XXX: DUMMY for now */
+            break;
     }
 
     put_handle(hdl);

+ 51 - 34
LibOS/shim/src/sys/shim_futex.c

@@ -92,7 +92,9 @@ int shim_do_futex (unsigned int * uaddr, int op, int val, void * utime,
         list_add_tail(&futex->list, &futex_list);
     }
 
-    if (futex_op == FUTEX_WAKE_OP || futex_op == FUTEX_REQUEUE) {
+    if (futex_op == FUTEX_WAKE_OP ||
+        futex_op == FUTEX_CMP_REQUEUE ||
+        futex_op == FUTEX_REQUEUE) {
         list_for_each_entry(tmp, &futex_list, list)
             if (tmp->uaddr == uaddr2) {
                 futex2 = tmp;
@@ -122,14 +124,46 @@ int shim_do_futex (unsigned int * uaddr, int op, int val, void * utime,
 
     unlock(futex_list_lock);
     lock(hdl->lock);
+    uint64_t timeout_us = NO_TIMEOUT;
 
     switch (futex_op) {
+        case FUTEX_WAIT_BITSET:
+            if (utime && timeout_us == NO_TIMEOUT) {
+                struct timespec *ts = (struct timespec*) utime;
+                // Round to microsecs
+                timeout_us = (ts->tv_sec * 1000000) + (ts->tv_nsec / 1000);
+                // Check for the CLOCK_REALTIME flag
+                if (futex_op == FUTEX_WAIT_BITSET)  {
+                    // DEP 1/28/17: Should really differentiate clocks, but
+                    // Graphene only has one for now.
+                    //&& 0 != (op & FUTEX_CLOCK_REALTIME)) {
+                    uint64_t current_time = DkSystemTimeQuery();
+                    if (current_time == 0) {
+                        ret = -EINVAL;
+                        break;
+                    }
+                    timeout_us -= current_time;
+                }
+            }
+
+        /* Note: for FUTEX_WAIT, timeout is interpreted as a relative
+         * value.  This differs from other futex operations, where
+         * timeout is interpreted as an absolute value.  To obtain the
+         * equivalent of FUTEX_WAIT with an absolute timeout, employ
+         * FUTEX_WAIT_BITSET with val3 specified as
+         * FUTEX_BITSET_MATCH_ANY. */
+
         case FUTEX_WAIT:
-        case FUTEX_WAIT_BITSET: {
+            if (utime && timeout_us == NO_TIMEOUT) {
+                struct timespec *ts = (struct timespec*) utime;
+                // Round to microsecs
+                timeout_us = (ts->tv_sec * 1000000) + (ts->tv_nsec / 1000);
+            }
+
+        {
             uint32_t bitset = (futex_op == FUTEX_WAIT_BITSET) ? val3 :
                               0xffffffff;
-            uint64_t timeout_us = NO_TIMEOUT;
-            
+
             debug("FUTEX_WAIT: %p (val = %d) vs %d mask = %08x, timeout ptr %p\n",
                   uaddr, *uaddr, val, bitset, utime);
 
@@ -145,55 +179,38 @@ int shim_do_futex (unsigned int * uaddr, int op, int val, void * utime,
             list_add_tail(&waiter.list, &futex->waiters);
 
             unlock(hdl->lock);
-            if (utime) {
-                struct timespec *ts = (struct timespec*) utime;
-                // Round to microsecs
-                timeout_us = (ts->tv_sec * 1000000) + (ts->tv_nsec / 1000);
-                // Check for the CLOCK_REALTIME flag
-                if (futex_op == FUTEX_WAIT_BITSET)  {
-                    // DEP 1/28/17: Should really differentiate clocks, but
-                    // Graphene only has one for now.
-                    //&& 0 != (op & FUTEX_CLOCK_REALTIME)) {
-                    uint64_t current_time = DkSystemTimeQuery();
-                    if (current_time == 0) {
-                        ret = -EINVAL;
-                        break;
-                    }
-                    timeout_us -= current_time;
-                }
-            }
             ret = thread_sleep(timeout_us);
             /* DEP 1/28/17: Should return ETIMEDOUT, not EAGAIN, on timeout. */
             if (ret == -EAGAIN)
                 ret = -ETIMEDOUT;
             lock(hdl->lock);
+            if (!list_empty(&waiter.list))
+                list_del(&waiter.list);
             break;
         }
 
         case FUTEX_WAKE:
         case FUTEX_WAKE_BITSET: {
+            struct futex_waiter * waiter;
+            int nwaken = 0;
             uint32_t bitset = (futex_op == FUTEX_WAKE_BITSET) ? val3 :
                               0xffffffff;
-            struct list_head *cursor;
+
             debug("FUTEX_WAKE: %p (val = %d) count = %d mask = %08x\n",
                   uaddr, *uaddr, val, bitset);
-            int cnt, nwaken = 0;
 
-            list_for_each(cursor, &futex->waiters) {
-                struct futex_waiter * waiter = list_entry(cursor,
-                                                          struct futex_waiter,
-                                                          list);
+            list_for_each_entry(waiter, &futex->waiters, list) {
                 if (!(bitset & waiter->bitset))
                     continue;
 
                 debug("FUTEX_WAKE wake thread %d: %p (val = %d)\n",
                       waiter->thread->tid, uaddr, *uaddr);
-                list_del(&waiter->list);
+                list_del_init(&waiter->list);
                 thread_wakeup(waiter->thread);
                 nwaken++;
                 if (nwaken >= val) break;
             }
-            
+
             ret = nwaken;
             debug("FUTEX_WAKE done: %p (val = %d) woke %d threads\n", uaddr, *uaddr, ret);
             break;
@@ -235,7 +252,7 @@ int shim_do_futex (unsigned int * uaddr, int op, int val, void * utime,
 
                 debug("FUTEX_WAKE wake thread %d: %p (val = %d)\n",
                       waiter->thread->tid, uaddr, *uaddr);
-                list_del(&waiter->list);
+                list_del_init(&waiter->list);
                 thread_wakeup(waiter->thread);
                 nwaken++;
             }
@@ -257,7 +274,7 @@ int shim_do_futex (unsigned int * uaddr, int op, int val, void * utime,
 
                     debug("FUTEX_WAKE wake thread %d: %p (val = %d)\n",
                           waiter->thread->tid, uaddr2, *uaddr2);
-                    list_del(&waiter->list);
+                    list_del_init(&waiter->list);
                     thread_wakeup(waiter->thread);
                     nwaken++;
                 }
@@ -283,7 +300,7 @@ int shim_do_futex (unsigned int * uaddr, int op, int val, void * utime,
                                                           struct futex_waiter,
                                                           list);
 
-                list_del(&waiter->list);
+                list_del_init(&waiter->list);
                 thread_wakeup(waiter->thread);
             }
 
@@ -380,7 +397,7 @@ void release_robust_list (struct robust_list_head * head)
                                                       struct futex_waiter,
                                                       list);
 
-            list_del(&waiter->list);
+            list_del_init(&waiter->list);
             thread_wakeup(waiter->thread);
         }
 
@@ -422,7 +439,7 @@ void release_clear_child_id (int * clear_child_tid)
                                                   struct futex_waiter,
                                                   list);
 
-        list_del(&waiter->list);
+        list_del_init(&waiter->list);
         thread_wakeup(waiter->thread);
     }
 

+ 5 - 7
LibOS/shim/src/sys/shim_mmap.c

@@ -56,18 +56,16 @@ void * shim_do_mmap (void * addr, size_t length, int prot, int flags, int fd,
 
     int pal_alloc_type = 0;
 
-    if ((flags & MAP_FIXED) && (addr != NULL)) {
+    if ((flags & MAP_FIXED) || addr) {
         struct shim_vma * tmp = NULL;
 
-        if (lookup_overlap_vma(addr, length, &tmp) == 0) {
+        if (!lookup_overlap_vma(addr, length, &tmp)) {
             debug("mmap: allowing overlapping MAP_FIXED allocation at %p with length %lu\n",
                   addr, length);
+
+            if (!(flags & MAP_FIXED))
+                addr = NULL;
         }
-    } else {
-        /* For calls without MAP_FIXED, don't even attempt to honor the
-         * caller's requested address. Such requests are likely to be assuming
-         * things about the address space that aren't valid in graphene. */
-        addr = NULL;
     }
 
     if (!addr) {

+ 21 - 0
LibOS/shim/src/sys/shim_sigaction.c

@@ -36,6 +36,8 @@
 
 #include <errno.h>
 
+#include <linux/signal.h>
+
 int shim_do_sigaction (int signum, const struct __kernel_sigaction * act,
                        struct __kernel_sigaction * oldact)
 {
@@ -137,6 +139,25 @@ out:
     return err;
 }
 
+int shim_do_sigaltstack (const stack_t * ss, stack_t * oss)
+{
+    if (ss && (ss->ss_flags & ~SS_DISABLE))
+        return -EINVAL;
+
+    struct shim_thread * cur = get_cur_thread();
+    int err = 0;
+
+    lock(cur->lock);
+
+    if (oss)
+        *oss = cur->signal_altstack;
+    if (ss)
+        cur->signal_altstack = *ss;
+
+    unlock(cur->lock);
+    return 0;
+}
+
 int shim_do_sigsuspend (const __sigset_t * mask)
 {
     __sigset_t * old, tmp;

+ 1 - 1
LibOS/shim/src/sys/shim_stat.c

@@ -115,7 +115,7 @@ int shim_do_readlink (const char * file, char * buf, int bufsize)
 
     int ret;
     struct shim_dentry * dent = NULL;
-    struct shim_qstr qstr;
+    struct shim_qstr qstr = QSTR_INIT;
 
     if ((ret = path_lookupat(NULL, file, LOOKUP_ACCESS, &dent)) < 0)
         return ret;

+ 3 - 0
LibOS/shim/src/syscallas.S

@@ -69,6 +69,9 @@ isdef:
         movq %rax, %fs:(SHIM_TCB_OFFSET + 40)
         movq %rsp, %fs:(SHIM_TCB_OFFSET + 48)
 
+        /* Translating x86_64 kernel calling convention to user-space
+         * calling convention */
+        movq %r10, %rcx
         call *%rbx
 
         movq $0, %fs:(SHIM_TCB_OFFSET + 24)

+ 10 - 0
LibOS/shim/test/Makefile

@@ -31,13 +31,23 @@ CXX += -g
 endif
 export DEBUG
 
+ifeq ($(ABSPATH_IN_MANIFEST),yes)
 manifest_rules = \
+	-e 's:\$$(PAL):$(abspath $(RUNTIME))/$(PAL_LOADER):g' \
+	-e 's:\$$(PWD):$(PWD):g' \
+	-e 's:\$$(BIN):$(subst .manifest,,$(notdir $@)):g' \
+	-e 's:\$$(SHIMPATH):$(abspath $(RUNTIME))/libsysdb.so:g' \
+	-e 's:\$$(LIBCDIR):$(abspath $(RUNTIME)):g' \
+	$(extra_rules)
+else
+manifest_rules= \
 	-e 's:\$$(PAL):$(abspath $(RUNTIME))/$(PAL_LOADER):g' \
 	-e 's:\$$(PWD):$(PWD):g' \
 	-e 's:\$$(BIN):$(subst .manifest,,$(notdir $@)):g' \
 	-e 's:\$$(SHIMPATH):'$$RELDIR'$(RUNTIME)/libsysdb.so:g' \
 	-e 's:\$$(LIBCDIR):'$$RELDIR'$(RUNTIME):g' \
 	$(extra_rules)
+endif
 
 pal_loader:
 	ln -sf $(RUNTIME)/pal_loader

+ 5 - 4
LibOS/shim/test/apps/apache/Makefile

@@ -7,6 +7,8 @@ ifeq ($(OS),Linux)
 	NPROCS := $(shell grep -c ^processor /proc/cpuinfo)
 endif
 
+PREFORK_WORKERS := $(shell expr $(NPROCS) + 1)
+
 HTTPD_DIR = httpd-2.4.3
 APR_DIR = apr-1.4.6
 APRUTIL_DIR = apr-util-1.5.1
@@ -87,9 +89,9 @@ build-conf:
 	$(INSTALL_DIR)/conf/httpd.conf.old > $(INSTALL_DIR)/conf/httpd.conf.new
 	echo "\n\
 <IfModule mpm_prefork_module>\n\
-    StartServers             4\n\
+    StartServers             $(PREFORK_WORKERS)\n\
     MinSpareServers          1\n\
-    MaxSpareServers          4\n\
+    MaxSpareServers          $(PREFORK_WORKERS)\n\
     MaxConnectionsPerChild   0\n\
 </IfModule>\n" >> $(INSTALL_DIR)/conf/httpd.conf.new
 	echo "\n\
@@ -98,7 +100,6 @@ build-conf:
 </IfModule>\n" >> $(INSTALL_DIR)/conf/httpd.conf.new
 	cd $(INSTALL_DIR)/conf && ln -sf httpd.conf.new httpd.conf
 
-
 start-native-server:
 	@echo "Listen on $(HOST):$(PORT)"
 	$(PREFIX) $(INSTALL_DIR)/bin/httpd -D FOREGROUND -C "ServerName $(HOST)" -C "Listen $(HOST):$(PORT)" -C "PidFile logs/httpd-$(HOST)-$(PORT).pid"
@@ -106,7 +107,7 @@ start-native-server:
 start-graphene-server:
 	rm -rf httpd-$(HOST)-$(PORT).pid
 	@echo "Listen on $(HOST):$(PORT)"
-	$(PREFIX) ../pal_loader httpd.manifest.sgx -D FOREGROUND -C "ServerName $(HOST)" -C "Listen $(HOST):$(PORT)" -C "PidFile logs/httpd-$(HOST)-$(PORT).pid"
+	$(PREFIX) ./httpd.manifest -D FOREGROUND -C "ServerName $(HOST)" -C "Listen $(HOST):$(PORT)" -C "PidFile logs/httpd-$(HOST)-$(PORT).pid"
 
 random-data = $(foreach n,1 2 3 4 5 6 7 8 9 10,2K.$n.html) \
 	      $(foreach n,1 2 3 4 5,10K.$n.html) \

+ 1 - 1
LibOS/shim/test/apps/apache/httpd.manifest.template

@@ -30,6 +30,7 @@ sgx.trusted_files.libc = file:$(LIBCDIR)/libc.so.6
 sgx.trusted_files.libdl = file:$(LIBCDIR)/libdl.so.2
 sgx.trusted_files.libm = file:$(LIBCDIR)/libm.so.6
 sgx.trusted_files.libpthread = file:$(LIBCDIR)/libpthread.so.0
+sgx.trusted_files.libresolv = file:$(LIBCDIR)/libresolv.so.2
 sgx.trusted_files.libnssdns = file:/lib/x86_64-linux-gnu/libnss_dns.so.2
 sgx.trusted_files.libnssfiles = file:/lib/x86_64-linux-gnu/libnss_files.so.2
 sgx.trusted_files.libnsscompact = file:/lib/x86_64-linux-gnu/libnss_compat.so.2
@@ -39,7 +40,6 @@ sgx.trusted_files.libapr = file:obj/lib/libapr-1.so.0
 sgx.trusted_files.libaprutil = file:obj/lib/libaprutil-1.so.0
 sgx.trusted_files.libexpat = file:/lib/x86_64-linux-gnu/libexpat.so.1
 sgx.trusted_files.libcrypt = file:/lib/x86_64-linux-gnu/libcrypt.so.1
-sgx.trusted_files.libresolv = file:/lib/x86_64-linux-gnu/libresolv.so.2
 sgx.trusted_files.libxml2 = file:/usr/lib/x86_64-linux-gnu/libxml2.so.2
 sgx.trusted_files.libz = file:/lib/x86_64-linux-gnu/libz.so.1
 sgx.trusted_files.liblzma = file:/lib/x86_64-linux-gnu/liblzma.so.5

+ 12 - 0
LibOS/shim/test/apps/curl/Makefile

@@ -0,0 +1,12 @@
+manifests = curl.manifest
+
+target =
+exec_target = $(manifests)
+
+clean-extra += clean-tmp
+
+level = ../../
+include ../../Makefile
+
+clean-tmp:
+	rm -f curl.manifest.sgx

+ 91 - 0
LibOS/shim/test/apps/curl/curl.manifest.template

@@ -0,0 +1,91 @@
+#!$(PAL)
+
+loader.preload = file:$(SHIMPATH)
+loader.exec = file:/usr/bin/curl
+loader.execname = /usr/bin/curl
+loader.env.LD_LIBRARY_PATH = /lib:/lib/x86_64-linux-gnu:/usr/lib:/usr/lib/x86_64-linux-gnu
+loader.env.PATH = /usr/bin:/bin
+loader.env.USERNAME =
+loader.env.HOME =
+loader.env.PWD =
+loader.debug_type = none
+
+fs.mount.lib1.type = chroot
+fs.mount.lib1.path = /lib
+fs.mount.lib1.uri = file:$(LIBCDIR)
+
+fs.mount.lib2.type = chroot
+fs.mount.lib2.path = /lib/x86_64-linux-gnu
+fs.mount.lib2.uri = file:/lib/x86_64-linux-gnu
+
+fs.mount.bin.type = chroot
+fs.mount.bin.path = /bin
+fs.mount.bin.uri = file:/bin
+
+fs.mount.usr.type = chroot
+fs.mount.usr.path = /usr
+fs.mount.usr.uri = file:/usr
+
+fs.mount.etc.type = chroot
+fs.mount.etc.path = /etc
+fs.mount.etc.uri = file:
+
+fs.mount.tmp.type = chroot
+fs.mount.tmp.path = /tmp
+fs.mount.tmp.uri = file:/tmp
+
+
+sys.stack.size = 256K
+sys.brk.size = 4M
+glibc.heap_size = 16M
+
+sgx.trusted_files.ld = file:$(LIBCDIR)/ld-linux-x86-64.so.2
+sgx.trusted_files.libc = file:$(LIBCDIR)/libc.so.6
+sgx.trusted_files.libdl = file:$(LIBCDIR)/libdl.so.2
+sgx.trusted_files.libm = file:$(LIBCDIR)/libm.so.6
+sgx.trusted_files.libpthread = file:$(LIBCDIR)/libpthread.so.0
+sgx.trusted_files.liburil = file:$(LIBCDIR)/libutil.so.1
+sgx.trusted_files.libz = file:/lib/x86_64-linux-gnu/libz.so.1
+sgx.trusted_files.libnss1 = file:/lib/x86_64-linux-gnu/libnss_compat.so.2
+sgx.trusted_files.libnss2 = file:/lib/x86_64-linux-gnu/libnss_files.so.2
+sgx.trusted_files.libnss3 = file:/lib/x86_64-linux-gnu/libnss_nis.so.2
+sgx.trusted_files.libnss4 = file:$(LIBCDIR)/libnss_dns.so.2
+sgx.trusted_files.libssl = file:/lib/x86_64-linux-gnu/libssl.so.1.0.0
+sgx.trusted_files.libcrypto = file:/lib/x86_64-linux-gnu/libcrypto.so.1.0.0
+sgx.trusted_files.libresolv = file:$(LIBCDIR)/libresolv.so.2
+sgx.trusted_files.libcurl = file:/usr/lib/x86_64-linux-gnu/libcurl.so.4
+sgx.trusted_files.libidn = file:/usr/lib/x86_64-linux-gnu/libidn.so.11
+sgx.trusted_files.librtmp = file:/usr/lib/x86_64-linux-gnu/librtmp.so.0
+sgx.trusted_files.libgssapi_krb5 = file:/usr/lib/x86_64-linux-gnu/libgssapi_krb5.so.2
+sgx.trusted_files.liblber = file:/usr/lib/x86_64-linux-gnu/liblber-2.4.so.2
+sgx.trusted_files.libldap_r = file:/usr/lib/x86_64-linux-gnu/libldap_r-2.4.so.2
+sgx.trusted_files.libgnutls = file:/usr/lib/x86_64-linux-gnu/libgnutls.so.26
+sgx.trusted_files.libgcrypt = file:/lib/x86_64-linux-gnu/libgcrypt.so.11
+sgx.trusted_files.libk5crypto = file:/usr/lib/x86_64-linux-gnu/libk5crypto.so.3
+sgx.trusted_files.libcom_err = file:/lib/x86_64-linux-gnu/libcom_err.so.2
+sgx.trusted_files.libkrb5support = file:/usr/lib/x86_64-linux-gnu/libkrb5support.so.0
+sgx.trusted_files.libsasl2 = file:/usr/lib/x86_64-linux-gnu/libsasl2.so.2
+sgx.trusted_files.libgssapi = file:/usr/lib/x86_64-linux-gnu/libgssapi.so.3
+sgx.trusted_files.libtasn1 = file:/usr/lib/x86_64-linux-gnu/libtasn1.so.6
+sgx.trusted_files.libp11kit = file:/usr/lib/x86_64-linux-gnu/libp11-kit.so.0
+sgx.trusted_files.libgpgerror = file:/lib/x86_64-linux-gnu/libgpg-error.so.0
+sgx.trusted_files.libkeyutils = file:/lib/x86_64-linux-gnu/libkeyutils.so.1
+sgx.trusted_files.libheimntlm = file:/usr/lib/x86_64-linux-gnu/libheimntlm.so.0
+sgx.trusted_files.libkrb5 = file:/usr/lib/x86_64-linux-gnu/libkrb5.so.3
+sgx.trusted_files.libkrb5_26 = file:/usr/lib/x86_64-linux-gnu/libkrb5.so.26
+sgx.trusted_files.libasn1 = file:/usr/lib/x86_64-linux-gnu/libasn1.so.8
+sgx.trusted_files.libroken = file:/usr/lib/x86_64-linux-gnu/libroken.so.18
+sgx.trusted_files.libffi = file:/usr/lib/x86_64-linux-gnu/libffi.so.6
+sgx.trusted_files.libwind = file:/usr/lib/x86_64-linux-gnu/libwind.so.0
+sgx.trusted_files.libheimbase = file:/usr/lib/x86_64-linux-gnu/libheimbase.so.1
+sgx.trusted_files.libhx509 = file:/usr/lib/x86_64-linux-gnu/libhx509.so.5
+sgx.trusted_files.libsqlite3 = file:/usr/lib/x86_64-linux-gnu/libsqlite3.so.0
+sgx.trusted_files.libcrypt = file:/lib/x86_64-linux-gnu/libcrypt.so.1
+sgx.trusted_files.libhcrypto = file:/usr/lib/x86_64-linux-gnu/libhcrypto.so.4
+sgx.trusted_files.libnsl = file:/lib/x86_64-linux-gnu/libnsl.so.1
+
+sgx.trusted_files.hosts = file:hosts
+sgx.trusted_files.resolv = file:resolv.conf
+sgx.trusted_files.gai = file:gai.conf
+
+sgx.allowed_files.tmp = file:/tmp

+ 65 - 0
LibOS/shim/test/apps/curl/gai.conf

@@ -0,0 +1,65 @@
+# Configuration for getaddrinfo(3).
+#
+# So far only configuration for the destination address sorting is needed.
+# RFC 3484 governs the sorting.  But the RFC also says that system
+# administrators should be able to overwrite the defaults.  This can be
+# achieved here.
+#
+# All lines have an initial identifier specifying the option followed by
+# up to two values.  Information specified in this file replaces the
+# default information.  Complete absence of data of one kind causes the
+# appropriate default information to be used.  The supported commands include:
+#
+# reload  <yes|no>
+#    If set to yes, each getaddrinfo(3) call will check whether this file
+#    changed and if necessary reload.  This option should not really be
+#    used.  There are possible runtime problems.  The default is no.
+#
+# label   <mask>   <value>
+#    Add another rule to the RFC 3484 label table.  See section 2.1 in
+#    RFC 3484.  The default is:
+#
+#label ::1/128       0
+#label ::/0          1
+#label 2002::/16     2
+#label ::/96         3
+#label ::ffff:0:0/96 4
+#label fec0::/10     5
+#label fc00::/7      6
+#label 2001:0::/32   7
+#
+#    This default differs from the tables given in RFC 3484 by handling
+#    (now obsolete) site-local IPv6 addresses and Unique Local Addresses.
+#    The reason for this difference is that these addresses are never
+#    NATed while IPv4 site-local addresses most probably are.  Given
+#    the precedence of IPv6 over IPv4 (see below) on machines having only
+#    site-local IPv4 and IPv6 addresses a lookup for a global address would
+#    see the IPv6 be preferred.  The result is a long delay because the
+#    site-local IPv6 addresses cannot be used while the IPv4 address is
+#    (at least for the foreseeable future) NATed.  We also treat Teredo
+#    tunnels special.
+#
+# precedence  <mask>   <value>
+#    Add another rule to the RFC 3484 precedence table.  See section 2.1
+#    and 10.3 in RFC 3484.  The default is:
+#
+#precedence  ::1/128       50
+#precedence  ::/0          40
+#precedence  2002::/16     30
+#precedence ::/96          20
+#precedence ::ffff:0:0/96  10
+#
+#    For sites which prefer IPv4 connections change the last line to
+#
+#precedence ::ffff:0:0/96  100
+
+#
+# scopev4  <mask>  <value>
+#    Add another rule to the RFC 6724 scope table for IPv4 addresses.
+#    By default the scope IDs described in section 3.2 in RFC 6724 are
+#    used.  Changing these defaults should hardly ever be necessary.
+#    The defaults are equivalent to:
+#
+#scopev4 ::ffff:169.254.0.0/112  2
+#scopev4 ::ffff:127.0.0.0/104    2
+#scopev4 ::ffff:0.0.0.0/96       14

+ 8 - 0
LibOS/shim/test/apps/curl/hosts

@@ -0,0 +1,8 @@
+127.0.0.1	localhost
+
+# The following lines are desirable for IPv6 capable hosts
+::1     ip6-localhost ip6-loopback
+fe00::0 ip6-localnet
+ff00::0 ip6-mcastprefix
+ff02::1 ip6-allnodes
+ff02::2 ip6-allrouters

+ 2 - 0
LibOS/shim/test/apps/curl/resolv.conf

@@ -0,0 +1,2 @@
+nameserver 8.8.8.8
+nameserver 8.8.4.4

+ 2 - 0
LibOS/shim/test/apps/gcc/gcc.manifest.template

@@ -23,6 +23,8 @@ fs.mount.tmp.type = chroot
 fs.mount.tmp.path = /tmp
 fs.mount.tmp.uri = file:/tmp
 
+sgx.enclave_size = 1G
+
 sgx.trusted_files.ld = file:$(LIBCDIR)/ld-linux-x86-64.so.2
 sgx.trusted_files.libc = file:$(LIBCDIR)/libc.so.6
 sgx.trusted_files.libdl = file:$(LIBCDIR)/libdl.so.2

+ 6998 - 0
LibOS/shim/test/apps/gcc/test_files/bzip2.c

@@ -0,0 +1,6998 @@
+/*-------------------------------------------------------------*/
+/*--- Public header file for the library.                   ---*/
+/*---                                               bzlib.h ---*/
+/*-------------------------------------------------------------*/
+
+/*--
+  This file is a part of bzip2 and/or libbzip2, a program and
+  library for lossless, block-sorting data compression.
+
+  Copyright (C) 1996-2002 Julian R Seward.  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. The origin of this software must not be misrepresented; you must 
+     not claim that you wrote the original software.  If you use this 
+     software in a product, an acknowledgment in the product 
+     documentation would be appreciated but is not required.
+
+  3. Altered source versions must be plainly marked as such, and must
+     not be misrepresented as being the original software.
+
+  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.
+
+  Julian Seward, Cambridge, UK.
+  jseward@acm.org
+  bzip2/libbzip2 version 1.0 of 21 March 2000
+
+  This program is based on (at least) the work of:
+     Mike Burrows
+     David Wheeler
+     Peter Fenwick
+     Alistair Moffat
+     Radford Neal
+     Ian H. Witten
+     Robert Sedgewick
+     Jon L. Bentley
+
+  For more information on these sources, see the manual.
+--*/
+
+
+#ifndef _BZLIB_H
+#define _BZLIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BZ_RUN               0
+#define BZ_FLUSH             1
+#define BZ_FINISH            2
+
+#define BZ_OK                0
+#define BZ_RUN_OK            1
+#define BZ_FLUSH_OK          2
+#define BZ_FINISH_OK         3
+#define BZ_STREAM_END        4
+#define BZ_SEQUENCE_ERROR    (-1)
+#define BZ_PARAM_ERROR       (-2)
+#define BZ_MEM_ERROR         (-3)
+#define BZ_DATA_ERROR        (-4)
+#define BZ_DATA_ERROR_MAGIC  (-5)
+#define BZ_IO_ERROR          (-6)
+#define BZ_UNEXPECTED_EOF    (-7)
+#define BZ_OUTBUFF_FULL      (-8)
+#define BZ_CONFIG_ERROR      (-9)
+
+typedef 
+   struct {
+      char *next_in;
+      unsigned int avail_in;
+      unsigned int total_in_lo32;
+      unsigned int total_in_hi32;
+
+      char *next_out;
+      unsigned int avail_out;
+      unsigned int total_out_lo32;
+      unsigned int total_out_hi32;
+
+      void *state;
+
+      void *(*bzalloc)(void *,int,int);
+      void (*bzfree)(void *,void *);
+      void *opaque;
+   } 
+   bz_stream;
+
+
+#ifndef BZ_IMPORT
+#define BZ_EXPORT
+#endif
+
+/* Need a definitition for FILE */
+#include <stdio.h>
+
+#ifdef _WIN32
+#   include <windows.h>
+#   ifdef small
+      /* windows.h define small to char */
+#      undef small
+#   endif
+#   ifdef BZ_EXPORT
+#   define BZ_API(func) WINAPI func
+#   define BZ_EXTERN extern
+#   else
+   /* import windows dll dynamically */
+#   define BZ_API(func) (WINAPI * func)
+#   define BZ_EXTERN
+#   endif
+#else
+#   define BZ_API(func) func
+#   define BZ_EXTERN extern
+#endif
+
+
+/*-- Core (low-level) library functions --*/
+
+BZ_EXTERN int BZ_API(BZ2_bzCompressInit) ( 
+      bz_stream* strm, 
+      int        blockSize100k, 
+      int        verbosity, 
+      int        workFactor 
+   );
+
+BZ_EXTERN int BZ_API(BZ2_bzCompress) ( 
+      bz_stream* strm, 
+      int action 
+   );
+
+BZ_EXTERN int BZ_API(BZ2_bzCompressEnd) ( 
+      bz_stream* strm 
+   );
+
+BZ_EXTERN int BZ_API(BZ2_bzDecompressInit) ( 
+      bz_stream *strm, 
+      int       verbosity, 
+      int       small
+   );
+
+BZ_EXTERN int BZ_API(BZ2_bzDecompress) ( 
+      bz_stream* strm 
+   );
+
+BZ_EXTERN int BZ_API(BZ2_bzDecompressEnd) ( 
+      bz_stream *strm 
+   );
+
+
+
+/*-- High(er) level library functions --*/
+
+#ifndef BZ_NO_STDIO
+#define BZ_MAX_UNUSED 5000
+
+typedef void BZFILE;
+
+BZ_EXTERN BZFILE* BZ_API(BZ2_bzReadOpen) ( 
+      int*  bzerror,   
+      FILE* f, 
+      int   verbosity, 
+      int   small,
+      void* unused,    
+      int   nUnused 
+   );
+
+BZ_EXTERN void BZ_API(BZ2_bzReadClose) ( 
+      int*    bzerror, 
+      BZFILE* b 
+   );
+
+BZ_EXTERN void BZ_API(BZ2_bzReadGetUnused) ( 
+      int*    bzerror, 
+      BZFILE* b, 
+      void**  unused,  
+      int*    nUnused 
+   );
+
+BZ_EXTERN int BZ_API(BZ2_bzRead) ( 
+      int*    bzerror, 
+      BZFILE* b, 
+      void*   buf, 
+      int     len 
+   );
+
+BZ_EXTERN BZFILE* BZ_API(BZ2_bzWriteOpen) ( 
+      int*  bzerror,      
+      FILE* f, 
+      int   blockSize100k, 
+      int   verbosity, 
+      int   workFactor 
+   );
+
+BZ_EXTERN void BZ_API(BZ2_bzWrite) ( 
+      int*    bzerror, 
+      BZFILE* b, 
+      void*   buf, 
+      int     len 
+   );
+
+BZ_EXTERN void BZ_API(BZ2_bzWriteClose) ( 
+      int*          bzerror, 
+      BZFILE*       b, 
+      int           abandon, 
+      unsigned int* nbytes_in, 
+      unsigned int* nbytes_out 
+   );
+
+BZ_EXTERN void BZ_API(BZ2_bzWriteClose64) ( 
+      int*          bzerror, 
+      BZFILE*       b, 
+      int           abandon, 
+      unsigned int* nbytes_in_lo32, 
+      unsigned int* nbytes_in_hi32, 
+      unsigned int* nbytes_out_lo32, 
+      unsigned int* nbytes_out_hi32
+   );
+#endif
+
+
+/*-- Utility functions --*/
+
+BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffCompress) ( 
+      char*         dest, 
+      unsigned int* destLen,
+      char*         source, 
+      unsigned int  sourceLen,
+      int           blockSize100k, 
+      int           verbosity, 
+      int           workFactor 
+   );
+
+BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffDecompress) ( 
+      char*         dest, 
+      unsigned int* destLen,
+      char*         source, 
+      unsigned int  sourceLen,
+      int           small, 
+      int           verbosity 
+   );
+
+
+/*--
+   Code contributed by Yoshioka Tsuneo
+   (QWF00133@niftyserve.or.jp/tsuneo-y@is.aist-nara.ac.jp),
+   to support better zlib compatibility.
+   This code is not _officially_ part of libbzip2 (yet);
+   I haven't tested it, documented it, or considered the
+   threading-safeness of it.
+   If this code breaks, please contact both Yoshioka and me.
+--*/
+
+BZ_EXTERN const char * BZ_API(BZ2_bzlibVersion) (
+      void
+   );
+
+#ifndef BZ_NO_STDIO
+BZ_EXTERN BZFILE * BZ_API(BZ2_bzopen) (
+      const char *path,
+      const char *mode
+   );
+
+BZ_EXTERN BZFILE * BZ_API(BZ2_bzdopen) (
+      int        fd,
+      const char *mode
+   );
+         
+BZ_EXTERN int BZ_API(BZ2_bzread) (
+      BZFILE* b, 
+      void* buf, 
+      int len 
+   );
+
+BZ_EXTERN int BZ_API(BZ2_bzwrite) (
+      BZFILE* b, 
+      void*   buf, 
+      int     len 
+   );
+
+BZ_EXTERN int BZ_API(BZ2_bzflush) (
+      BZFILE* b
+   );
+
+BZ_EXTERN void BZ_API(BZ2_bzclose) (
+      BZFILE* b
+   );
+
+BZ_EXTERN const char * BZ_API(BZ2_bzerror) (
+      BZFILE *b, 
+      int    *errnum
+   );
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/*-------------------------------------------------------------*/
+/*--- end                                           bzlib.h ---*/
+/*-------------------------------------------------------------*/
+/*-------------------------------------------------------------*/
+/*--- Private header file for the library.                  ---*/
+/*---                                       bzlib_private.h ---*/
+/*-------------------------------------------------------------*/
+
+#ifndef _BZLIB_PRIVATE_H
+#define _BZLIB_PRIVATE_H
+
+#include <stdlib.h>
+
+#ifndef BZ_NO_STDIO
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#endif
+
+
+/*-- General stuff. --*/
+
+#define BZ_VERSION  "1.0.2, 30-Dec-2001"
+
+typedef char            Char;
+typedef unsigned char   Bool;
+typedef unsigned char   UChar;
+typedef int             Int32;
+typedef unsigned int    UInt32;
+typedef short           Int16;
+typedef unsigned short  UInt16;
+
+#define True  ((Bool)1)
+#define False ((Bool)0)
+
+#ifndef __GNUC__
+#define __inline__  /* */
+#endif 
+
+#ifndef BZ_NO_STDIO
+extern void BZ2_bz__AssertH__fail ( int errcode );
+#define AssertH(cond,errcode) \
+   { if (!(cond)) BZ2_bz__AssertH__fail ( errcode ); }
+#if BZ_DEBUG
+#define AssertD(cond,msg) \
+   { if (!(cond)) {       \
+      fprintf ( stderr,   \
+        "\n\nlibbzip2(debug build): internal error\n\t%s\n", msg );\
+      exit(1); \
+   }}
+#else
+#define AssertD(cond,msg) /* */
+#endif
+#define VPrintf0(zf) \
+   fprintf(stderr,zf)
+#define VPrintf1(zf,za1) \
+   fprintf(stderr,zf,za1)
+#define VPrintf2(zf,za1,za2) \
+   fprintf(stderr,zf,za1,za2)
+#define VPrintf3(zf,za1,za2,za3) \
+   fprintf(stderr,zf,za1,za2,za3)
+#define VPrintf4(zf,za1,za2,za3,za4) \
+   fprintf(stderr,zf,za1,za2,za3,za4)
+#define VPrintf5(zf,za1,za2,za3,za4,za5) \
+   fprintf(stderr,zf,za1,za2,za3,za4,za5)
+#else
+extern void bz_internal_error ( int errcode );
+#define AssertH(cond,errcode) \
+   { if (!(cond)) bz_internal_error ( errcode ); }
+#define AssertD(cond,msg) /* */
+#define VPrintf0(zf) /* */
+#define VPrintf1(zf,za1) /* */
+#define VPrintf2(zf,za1,za2) /* */
+#define VPrintf3(zf,za1,za2,za3) /* */
+#define VPrintf4(zf,za1,za2,za3,za4) /* */
+#define VPrintf5(zf,za1,za2,za3,za4,za5) /* */
+#endif
+
+
+#define BZALLOC(nnn) (strm->bzalloc)(strm->opaque,(nnn),1)
+#define BZFREE(ppp)  (strm->bzfree)(strm->opaque,(ppp))
+
+
+/*-- Header bytes. --*/
+
+#define BZ_HDR_B 0x42   /* 'B' */
+#define BZ_HDR_Z 0x5a   /* 'Z' */
+#define BZ_HDR_h 0x68   /* 'h' */
+#define BZ_HDR_0 0x30   /* '0' */
+  
+/*-- Constants for the back end. --*/
+
+#define BZ_MAX_ALPHA_SIZE 258
+#define BZ_MAX_CODE_LEN    23
+
+#define BZ_RUNA 0
+#define BZ_RUNB 1
+
+#define BZ_N_GROUPS 6
+#define BZ_G_SIZE   50
+#define BZ_N_ITERS  4
+
+#define BZ_MAX_SELECTORS (2 + (900000 / BZ_G_SIZE))
+
+
+
+/*-- Stuff for randomising repetitive blocks. --*/
+
+extern Int32 BZ2_rNums[512];
+
+#define BZ_RAND_DECLS                          \
+   Int32 rNToGo;                               \
+   Int32 rTPos                                 \
+
+#define BZ_RAND_INIT_MASK                      \
+   s->rNToGo = 0;                              \
+   s->rTPos  = 0                               \
+
+#define BZ_RAND_MASK ((s->rNToGo == 1) ? 1 : 0)
+
+#define BZ_RAND_UPD_MASK                       \
+   if (s->rNToGo == 0) {                       \
+      s->rNToGo = BZ2_rNums[s->rTPos];         \
+      s->rTPos++;                              \
+      if (s->rTPos == 512) s->rTPos = 0;       \
+   }                                           \
+   s->rNToGo--;
+
+
+
+/*-- Stuff for doing CRCs. --*/
+
+extern UInt32 BZ2_crc32Table[256];
+
+#define BZ_INITIALISE_CRC(crcVar)              \
+{                                              \
+   crcVar = 0xffffffffL;                       \
+}
+
+#define BZ_FINALISE_CRC(crcVar)                \
+{                                              \
+   crcVar = ~(crcVar);                         \
+}
+
+#define BZ_UPDATE_CRC(crcVar,cha)              \
+{                                              \
+   crcVar = (crcVar << 8) ^                    \
+            BZ2_crc32Table[(crcVar >> 24) ^    \
+                           ((UChar)cha)];      \
+}
+
+
+
+/*-- States and modes for compression. --*/
+
+#define BZ_M_IDLE      1
+#define BZ_M_RUNNING   2
+#define BZ_M_FLUSHING  3
+#define BZ_M_FINISHING 4
+
+#define BZ_S_OUTPUT    1
+#define BZ_S_INPUT     2
+
+#define BZ_N_RADIX 2
+#define BZ_N_QSORT 12
+#define BZ_N_SHELL 18
+#define BZ_N_OVERSHOOT (BZ_N_RADIX + BZ_N_QSORT + BZ_N_SHELL + 2)
+
+
+
+
+/*-- Structure holding all the compression-side stuff. --*/
+
+typedef
+   struct {
+      /* pointer back to the struct bz_stream */
+      bz_stream* strm;
+
+      /* mode this stream is in, and whether inputting */
+      /* or outputting data */
+      Int32    mode;
+      Int32    state;
+
+      /* remembers avail_in when flush/finish requested */
+      UInt32   avail_in_expect;
+
+      /* for doing the block sorting */
+      UInt32*  arr1;
+      UInt32*  arr2;
+      UInt32*  ftab;
+      Int32    origPtr;
+
+      /* aliases for arr1 and arr2 */
+      UInt32*  ptr;
+      UChar*   block;
+      UInt16*  mtfv;
+      UChar*   zbits;
+
+      /* for deciding when to use the fallback sorting algorithm */
+      Int32    workFactor;
+
+      /* run-length-encoding of the input */
+      UInt32   state_in_ch;
+      Int32    state_in_len;
+      BZ_RAND_DECLS;
+
+      /* input and output limits and current posns */
+      Int32    nblock;
+      Int32    nblockMAX;
+      Int32    numZ;
+      Int32    state_out_pos;
+
+      /* map of bytes used in block */
+      Int32    nInUse;
+      Bool     inUse[256];
+      UChar    unseqToSeq[256];
+
+      /* the buffer for bit stream creation */
+      UInt32   bsBuff;
+      Int32    bsLive;
+
+      /* block and combined CRCs */
+      UInt32   blockCRC;
+      UInt32   combinedCRC;
+
+      /* misc administratium */
+      Int32    verbosity;
+      Int32    blockNo;
+      Int32    blockSize100k;
+
+      /* stuff for coding the MTF values */
+      Int32    nMTF;
+      Int32    mtfFreq    [BZ_MAX_ALPHA_SIZE];
+      UChar    selector   [BZ_MAX_SELECTORS];
+      UChar    selectorMtf[BZ_MAX_SELECTORS];
+
+      UChar    len     [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+      Int32    code    [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+      Int32    rfreq   [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+      /* second dimension: only 3 needed; 4 makes index calculations faster */
+      UInt32   len_pack[BZ_MAX_ALPHA_SIZE][4];
+
+   }
+   EState;
+
+
+
+/*-- externs for compression. --*/
+
+extern void 
+BZ2_blockSort ( EState* );
+
+extern void 
+BZ2_compressBlock ( EState*, Bool );
+
+extern void 
+BZ2_bsInitWrite ( EState* );
+
+extern void 
+BZ2_hbAssignCodes ( Int32*, UChar*, Int32, Int32, Int32 );
+
+extern void 
+BZ2_hbMakeCodeLengths ( UChar*, Int32*, Int32, Int32 );
+
+
+
+/*-- states for decompression. --*/
+
+#define BZ_X_IDLE        1
+#define BZ_X_OUTPUT      2
+
+#define BZ_X_MAGIC_1     10
+#define BZ_X_MAGIC_2     11
+#define BZ_X_MAGIC_3     12
+#define BZ_X_MAGIC_4     13
+#define BZ_X_BLKHDR_1    14
+#define BZ_X_BLKHDR_2    15
+#define BZ_X_BLKHDR_3    16
+#define BZ_X_BLKHDR_4    17
+#define BZ_X_BLKHDR_5    18
+#define BZ_X_BLKHDR_6    19
+#define BZ_X_BCRC_1      20
+#define BZ_X_BCRC_2      21
+#define BZ_X_BCRC_3      22
+#define BZ_X_BCRC_4      23
+#define BZ_X_RANDBIT     24
+#define BZ_X_ORIGPTR_1   25
+#define BZ_X_ORIGPTR_2   26
+#define BZ_X_ORIGPTR_3   27
+#define BZ_X_MAPPING_1   28
+#define BZ_X_MAPPING_2   29
+#define BZ_X_SELECTOR_1  30
+#define BZ_X_SELECTOR_2  31
+#define BZ_X_SELECTOR_3  32
+#define BZ_X_CODING_1    33
+#define BZ_X_CODING_2    34
+#define BZ_X_CODING_3    35
+#define BZ_X_MTF_1       36
+#define BZ_X_MTF_2       37
+#define BZ_X_MTF_3       38
+#define BZ_X_MTF_4       39
+#define BZ_X_MTF_5       40
+#define BZ_X_MTF_6       41
+#define BZ_X_ENDHDR_2    42
+#define BZ_X_ENDHDR_3    43
+#define BZ_X_ENDHDR_4    44
+#define BZ_X_ENDHDR_5    45
+#define BZ_X_ENDHDR_6    46
+#define BZ_X_CCRC_1      47
+#define BZ_X_CCRC_2      48
+#define BZ_X_CCRC_3      49
+#define BZ_X_CCRC_4      50
+
+
+
+/*-- Constants for the fast MTF decoder. --*/
+
+#define MTFA_SIZE 4096
+#define MTFL_SIZE 16
+
+
+
+/*-- Structure holding all the decompression-side stuff. --*/
+
+typedef
+   struct {
+      /* pointer back to the struct bz_stream */
+      bz_stream* strm;
+
+      /* state indicator for this stream */
+      Int32    state;
+
+      /* for doing the final run-length decoding */
+      UChar    state_out_ch;
+      Int32    state_out_len;
+      Bool     blockRandomised;
+      BZ_RAND_DECLS;
+
+      /* the buffer for bit stream reading */
+      UInt32   bsBuff;
+      Int32    bsLive;
+
+      /* misc administratium */
+      Int32    blockSize100k;
+      Bool     smallDecompress;
+      Int32    currBlockNo;
+      Int32    verbosity;
+
+      /* for undoing the Burrows-Wheeler transform */
+      Int32    origPtr;
+      UInt32   tPos;
+      Int32    k0;
+      Int32    unzftab[256];
+      Int32    nblock_used;
+      Int32    cftab[257];
+      Int32    cftabCopy[257];
+
+      /* for undoing the Burrows-Wheeler transform (FAST) */
+      UInt32   *tt;
+
+      /* for undoing the Burrows-Wheeler transform (SMALL) */
+      UInt16   *ll16;
+      UChar    *ll4;
+
+      /* stored and calculated CRCs */
+      UInt32   storedBlockCRC;
+      UInt32   storedCombinedCRC;
+      UInt32   calculatedBlockCRC;
+      UInt32   calculatedCombinedCRC;
+
+      /* map of bytes used in block */
+      Int32    nInUse;
+      Bool     inUse[256];
+      Bool     inUse16[16];
+      UChar    seqToUnseq[256];
+
+      /* for decoding the MTF values */
+      UChar    mtfa   [MTFA_SIZE];
+      Int32    mtfbase[256 / MTFL_SIZE];
+      UChar    selector   [BZ_MAX_SELECTORS];
+      UChar    selectorMtf[BZ_MAX_SELECTORS];
+      UChar    len  [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+
+      Int32    limit  [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+      Int32    base   [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+      Int32    perm   [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+      Int32    minLens[BZ_N_GROUPS];
+
+      /* save area for scalars in the main decompress code */
+      Int32    save_i;
+      Int32    save_j;
+      Int32    save_t;
+      Int32    save_alphaSize;
+      Int32    save_nGroups;
+      Int32    save_nSelectors;
+      Int32    save_EOB;
+      Int32    save_groupNo;
+      Int32    save_groupPos;
+      Int32    save_nextSym;
+      Int32    save_nblockMAX;
+      Int32    save_nblock;
+      Int32    save_es;
+      Int32    save_N;
+      Int32    save_curr;
+      Int32    save_zt;
+      Int32    save_zn; 
+      Int32    save_zvec;
+      Int32    save_zj;
+      Int32    save_gSel;
+      Int32    save_gMinlen;
+      Int32*   save_gLimit;
+      Int32*   save_gBase;
+      Int32*   save_gPerm;
+
+   }
+   DState;
+
+
+
+/*-- Macros for decompression. --*/
+
+#define BZ_GET_FAST(cccc)                     \
+    s->tPos = s->tt[s->tPos];                 \
+    cccc = (UChar)(s->tPos & 0xff);           \
+    s->tPos >>= 8;
+
+#define BZ_GET_FAST_C(cccc)                   \
+    c_tPos = c_tt[c_tPos];                    \
+    cccc = (UChar)(c_tPos & 0xff);            \
+    c_tPos >>= 8;
+
+#define SET_LL4(i,n)                                          \
+   { if (((i) & 0x1) == 0)                                    \
+        s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0xf0) | (n); else    \
+        s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0x0f) | ((n) << 4);  \
+   }
+
+#define GET_LL4(i)                             \
+   ((((UInt32)(s->ll4[(i) >> 1])) >> (((i) << 2) & 0x4)) & 0xF)
+
+#define SET_LL(i,n)                          \
+   { s->ll16[i] = (UInt16)(n & 0x0000ffff);  \
+     SET_LL4(i, n >> 16);                    \
+   }
+
+#define GET_LL(i) \
+   (((UInt32)s->ll16[i]) | (GET_LL4(i) << 16))
+
+#define BZ_GET_SMALL(cccc)                            \
+      cccc = BZ2_indexIntoF ( s->tPos, s->cftab );    \
+      s->tPos = GET_LL(s->tPos);
+
+
+/*-- externs for decompression. --*/
+
+extern Int32 
+BZ2_indexIntoF ( Int32, Int32* );
+
+extern Int32 
+BZ2_decompress ( DState* );
+
+extern void 
+BZ2_hbCreateDecodeTables ( Int32*, Int32*, Int32*, UChar*,
+                           Int32,  Int32, Int32 );
+
+
+#endif
+
+
+/*-- BZ_NO_STDIO seems to make NULL disappear on some platforms. --*/
+
+#ifdef BZ_NO_STDIO
+#ifndef NULL
+#define NULL 0
+#endif
+#endif
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                   bzlib_private.h ---*/
+/*-------------------------------------------------------------*/
+
+
+
+/*-------------------------------------------------------------*/
+/*--- Block sorting machinery                               ---*/
+/*---                                           blocksort.c ---*/
+/*-------------------------------------------------------------*/
+
+/*---------------------------------------------*/
+/*--- Fallback O(N log(N)^2) sorting        ---*/
+/*--- algorithm, for repetitive blocks      ---*/
+/*---------------------------------------------*/
+
+/*---------------------------------------------*/
+static 
+__inline__
+void fallbackSimpleSort ( UInt32* fmap, 
+                          UInt32* eclass, 
+                          Int32   lo, 
+                          Int32   hi )
+{
+   Int32 i, j, tmp;
+   UInt32 ec_tmp;
+
+   if (lo == hi) return;
+
+   if (hi - lo > 3) {
+      for ( i = hi-4; i >= lo; i-- ) {
+         tmp = fmap[i];
+         ec_tmp = eclass[tmp];
+         for ( j = i+4; j <= hi && ec_tmp > eclass[fmap[j]]; j += 4 )
+            fmap[j-4] = fmap[j];
+         fmap[j-4] = tmp;
+      }
+   }
+
+   for ( i = hi-1; i >= lo; i-- ) {
+      tmp = fmap[i];
+      ec_tmp = eclass[tmp];
+      for ( j = i+1; j <= hi && ec_tmp > eclass[fmap[j]]; j++ )
+         fmap[j-1] = fmap[j];
+      fmap[j-1] = tmp;
+   }
+}
+
+
+/*---------------------------------------------*/
+#define fswap(zz1, zz2) \
+   { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; }
+
+#define fvswap(zzp1, zzp2, zzn)       \
+{                                     \
+   Int32 yyp1 = (zzp1);               \
+   Int32 yyp2 = (zzp2);               \
+   Int32 yyn  = (zzn);                \
+   while (yyn > 0) {                  \
+      fswap(fmap[yyp1], fmap[yyp2]);  \
+      yyp1++; yyp2++; yyn--;          \
+   }                                  \
+}
+
+
+#define fmin(a,b) ((a) < (b)) ? (a) : (b)
+
+#define fpush(lz,hz) { stackLo[sp] = lz; \
+                       stackHi[sp] = hz; \
+                       sp++; }
+
+#define fpop(lz,hz) { sp--;              \
+                      lz = stackLo[sp];  \
+                      hz = stackHi[sp]; }
+
+#define FALLBACK_QSORT_SMALL_THRESH 10
+#define FALLBACK_QSORT_STACK_SIZE   100
+
+
+static
+void fallbackQSort3 ( UInt32* fmap, 
+                      UInt32* eclass,
+                      Int32   loSt, 
+                      Int32   hiSt )
+{
+   Int32 unLo, unHi, ltLo, gtHi, n, m;
+   Int32 sp, lo, hi;
+   UInt32 med, r, r3;
+   Int32 stackLo[FALLBACK_QSORT_STACK_SIZE];
+   Int32 stackHi[FALLBACK_QSORT_STACK_SIZE];
+
+   r = 0;
+
+   sp = 0;
+   fpush ( loSt, hiSt );
+
+   while (sp > 0) {
+
+      AssertH ( sp < FALLBACK_QSORT_STACK_SIZE, 1004 );
+
+      fpop ( lo, hi );
+      if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) {
+         fallbackSimpleSort ( fmap, eclass, lo, hi );
+         continue;
+      }
+
+      /* Random partitioning.  Median of 3 sometimes fails to
+         avoid bad cases.  Median of 9 seems to help but 
+         looks rather expensive.  This too seems to work but
+         is cheaper.  Guidance for the magic constants 
+         7621 and 32768 is taken from Sedgewick's algorithms
+         book, chapter 35.
+      */
+      r = ((r * 7621) + 1) % 32768;
+      r3 = r % 3;
+      if (r3 == 0) med = eclass[fmap[lo]]; else
+      if (r3 == 1) med = eclass[fmap[(lo+hi)>>1]]; else
+                   med = eclass[fmap[hi]];
+
+      unLo = ltLo = lo;
+      unHi = gtHi = hi;
+
+      while (1) {
+         while (1) {
+            if (unLo > unHi) break;
+            n = (Int32)eclass[fmap[unLo]] - (Int32)med;
+            if (n == 0) { 
+               fswap(fmap[unLo], fmap[ltLo]); 
+               ltLo++; unLo++; 
+               continue; 
+            };
+            if (n > 0) break;
+            unLo++;
+         }
+         while (1) {
+            if (unLo > unHi) break;
+            n = (Int32)eclass[fmap[unHi]] - (Int32)med;
+            if (n == 0) { 
+               fswap(fmap[unHi], fmap[gtHi]); 
+               gtHi--; unHi--; 
+               continue; 
+            };
+            if (n < 0) break;
+            unHi--;
+         }
+         if (unLo > unHi) break;
+         fswap(fmap[unLo], fmap[unHi]); unLo++; unHi--;
+      }
+
+      AssertD ( unHi == unLo-1, "fallbackQSort3(2)" );
+
+      if (gtHi < ltLo) continue;
+
+      n = fmin(ltLo-lo, unLo-ltLo); fvswap(lo, unLo-n, n);
+      m = fmin(hi-gtHi, gtHi-unHi); fvswap(unLo, hi-m+1, m);
+
+      n = lo + unLo - ltLo - 1;
+      m = hi - (gtHi - unHi) + 1;
+
+      if (n - lo > hi - m) {
+         fpush ( lo, n );
+         fpush ( m, hi );
+      } else {
+         fpush ( m, hi );
+         fpush ( lo, n );
+      }
+   }
+}
+
+#undef fmin
+#undef fpush
+#undef fpop
+#undef fswap
+#undef fvswap
+#undef FALLBACK_QSORT_SMALL_THRESH
+#undef FALLBACK_QSORT_STACK_SIZE
+
+
+/*---------------------------------------------*/
+/* Pre:
+      nblock > 0
+      eclass exists for [0 .. nblock-1]
+      ((UChar*)eclass) [0 .. nblock-1] holds block
+      ptr exists for [0 .. nblock-1]
+
+   Post:
+      ((UChar*)eclass) [0 .. nblock-1] holds block
+      All other areas of eclass destroyed
+      fmap [0 .. nblock-1] holds sorted order
+      bhtab [ 0 .. 2+(nblock/32) ] destroyed
+*/
+
+#define       SET_BH(zz)  bhtab[(zz) >> 5] |= (1 << ((zz) & 31))
+#define     CLEAR_BH(zz)  bhtab[(zz) >> 5] &= ~(1 << ((zz) & 31))
+#define     ISSET_BH(zz)  (bhtab[(zz) >> 5] & (1 << ((zz) & 31)))
+#define      WORD_BH(zz)  bhtab[(zz) >> 5]
+#define UNALIGNED_BH(zz)  ((zz) & 0x01f)
+
+static
+void fallbackSort ( UInt32* fmap, 
+                    UInt32* eclass, 
+                    UInt32* bhtab,
+                    Int32   nblock,
+                    Int32   verb )
+{
+   Int32 ftab[257];
+   Int32 ftabCopy[256];
+   Int32 H, i, j, k, l, r, cc, cc1;
+   Int32 nNotDone;
+   Int32 nBhtab;
+   UChar* eclass8 = (UChar*)eclass;
+
+   /*--
+      Initial 1-char radix sort to generate
+      initial fmap and initial BH bits.
+   --*/
+   if (verb >= 4)
+      VPrintf0 ( "        bucket sorting ...\n" );
+   for (i = 0; i < 257;    i++) ftab[i] = 0;
+   for (i = 0; i < nblock; i++) ftab[eclass8[i]]++;
+   for (i = 0; i < 256;    i++) ftabCopy[i] = ftab[i];
+   for (i = 1; i < 257;    i++) ftab[i] += ftab[i-1];
+
+   for (i = 0; i < nblock; i++) {
+      j = eclass8[i];
+      k = ftab[j] - 1;
+      ftab[j] = k;
+      fmap[k] = i;
+   }
+
+   nBhtab = 2 + (nblock / 32);
+   for (i = 0; i < nBhtab; i++) bhtab[i] = 0;
+   for (i = 0; i < 256; i++) SET_BH(ftab[i]);
+
+   /*--
+      Inductively refine the buckets.  Kind-of an
+      "exponential radix sort" (!), inspired by the
+      Manber-Myers suffix array construction algorithm.
+   --*/
+
+   /*-- set sentinel bits for block-end detection --*/
+   for (i = 0; i < 32; i++) { 
+      SET_BH(nblock + 2*i);
+      CLEAR_BH(nblock + 2*i + 1);
+   }
+
+   /*-- the log(N) loop --*/
+   H = 1;
+   while (1) {
+
+      if (verb >= 4) 
+         VPrintf1 ( "        depth %6d has ", H );
+
+      j = 0;
+      for (i = 0; i < nblock; i++) {
+         if (ISSET_BH(i)) j = i;
+         k = fmap[i] - H; if (k < 0) k += nblock;
+         eclass[k] = j;
+      }
+
+      nNotDone = 0;
+      r = -1;
+      while (1) {
+
+	 /*-- find the next non-singleton bucket --*/
+         k = r + 1;
+         while (ISSET_BH(k) && UNALIGNED_BH(k)) k++;
+         if (ISSET_BH(k)) {
+            while (WORD_BH(k) == 0xffffffff) k += 32;
+            while (ISSET_BH(k)) k++;
+         }
+         l = k - 1;
+         if (l >= nblock) break;
+         while (!ISSET_BH(k) && UNALIGNED_BH(k)) k++;
+         if (!ISSET_BH(k)) {
+            while (WORD_BH(k) == 0x00000000) k += 32;
+            while (!ISSET_BH(k)) k++;
+         }
+         r = k - 1;
+         if (r >= nblock) break;
+
+         /*-- now [l, r] bracket current bucket --*/
+         if (r > l) {
+            nNotDone += (r - l + 1);
+            fallbackQSort3 ( fmap, eclass, l, r );
+
+            /*-- scan bucket and generate header bits-- */
+            cc = -1;
+            for (i = l; i <= r; i++) {
+               cc1 = eclass[fmap[i]];
+               if (cc != cc1) { SET_BH(i); cc = cc1; };
+            }
+         }
+      }
+
+      if (verb >= 4) 
+         VPrintf1 ( "%6d unresolved strings\n", nNotDone );
+
+      H *= 2;
+      if (H > nblock || nNotDone == 0) break;
+   }
+
+   /*-- 
+      Reconstruct the original block in
+      eclass8 [0 .. nblock-1], since the
+      previous phase destroyed it.
+   --*/
+   if (verb >= 4)
+      VPrintf0 ( "        reconstructing block ...\n" );
+   j = 0;
+   for (i = 0; i < nblock; i++) {
+      while (ftabCopy[j] == 0) j++;
+      ftabCopy[j]--;
+      eclass8[fmap[i]] = (UChar)j;
+   }
+   AssertH ( j < 256, 1005 );
+}
+
+#undef       SET_BH
+#undef     CLEAR_BH
+#undef     ISSET_BH
+#undef      WORD_BH
+#undef UNALIGNED_BH
+
+
+/*---------------------------------------------*/
+/*--- The main, O(N^2 log(N)) sorting       ---*/
+/*--- algorithm.  Faster for "normal"       ---*/
+/*--- non-repetitive blocks.                ---*/
+/*---------------------------------------------*/
+
+/*---------------------------------------------*/
+static
+__inline__
+Bool mainGtU ( UInt32  i1, 
+               UInt32  i2,
+               UChar*  block, 
+               UInt16* quadrant,
+               UInt32  nblock,
+               Int32*  budget )
+{
+   Int32  k;
+   UChar  c1, c2;
+   UInt16 s1, s2;
+
+   AssertD ( i1 != i2, "mainGtU" );
+   /* 1 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 2 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 3 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 4 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 5 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 6 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 7 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 8 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 9 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 10 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 11 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+   /* 12 */
+   c1 = block[i1]; c2 = block[i2];
+   if (c1 != c2) return (c1 > c2);
+   i1++; i2++;
+
+   k = nblock + 8;
+
+   do {
+      /* 1 */
+      c1 = block[i1]; c2 = block[i2];
+      if (c1 != c2) return (c1 > c2);
+      s1 = quadrant[i1]; s2 = quadrant[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1++; i2++;
+      /* 2 */
+      c1 = block[i1]; c2 = block[i2];
+      if (c1 != c2) return (c1 > c2);
+      s1 = quadrant[i1]; s2 = quadrant[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1++; i2++;
+      /* 3 */
+      c1 = block[i1]; c2 = block[i2];
+      if (c1 != c2) return (c1 > c2);
+      s1 = quadrant[i1]; s2 = quadrant[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1++; i2++;
+      /* 4 */
+      c1 = block[i1]; c2 = block[i2];
+      if (c1 != c2) return (c1 > c2);
+      s1 = quadrant[i1]; s2 = quadrant[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1++; i2++;
+      /* 5 */
+      c1 = block[i1]; c2 = block[i2];
+      if (c1 != c2) return (c1 > c2);
+      s1 = quadrant[i1]; s2 = quadrant[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1++; i2++;
+      /* 6 */
+      c1 = block[i1]; c2 = block[i2];
+      if (c1 != c2) return (c1 > c2);
+      s1 = quadrant[i1]; s2 = quadrant[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1++; i2++;
+      /* 7 */
+      c1 = block[i1]; c2 = block[i2];
+      if (c1 != c2) return (c1 > c2);
+      s1 = quadrant[i1]; s2 = quadrant[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1++; i2++;
+      /* 8 */
+      c1 = block[i1]; c2 = block[i2];
+      if (c1 != c2) return (c1 > c2);
+      s1 = quadrant[i1]; s2 = quadrant[i2];
+      if (s1 != s2) return (s1 > s2);
+      i1++; i2++;
+
+      if (i1 >= nblock) i1 -= nblock;
+      if (i2 >= nblock) i2 -= nblock;
+
+      k -= 8;
+      (*budget)--;
+   }
+      while (k >= 0);
+
+   return False;
+}
+
+
+/*---------------------------------------------*/
+/*--
+   Knuth's increments seem to work better
+   than Incerpi-Sedgewick here.  Possibly
+   because the number of elems to sort is
+   usually small, typically <= 20.
+--*/
+static
+Int32 incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280,
+                   9841, 29524, 88573, 265720,
+                   797161, 2391484 };
+
+static
+void mainSimpleSort ( UInt32* ptr,
+                      UChar*  block,
+                      UInt16* quadrant,
+                      Int32   nblock,
+                      Int32   lo, 
+                      Int32   hi, 
+                      Int32   d,
+                      Int32*  budget )
+{
+   Int32 i, j, h, bigN, hp;
+   UInt32 v;
+
+   bigN = hi - lo + 1;
+   if (bigN < 2) return;
+
+   hp = 0;
+   while (incs[hp] < bigN) hp++;
+   hp--;
+
+   for (; hp >= 0; hp--) {
+      h = incs[hp];
+
+      i = lo + h;
+      while (True) {
+
+         /*-- copy 1 --*/
+         if (i > hi) break;
+         v = ptr[i];
+         j = i;
+         while ( mainGtU ( 
+                    ptr[j-h]+d, v+d, block, quadrant, nblock, budget 
+                 ) ) {
+            ptr[j] = ptr[j-h];
+            j = j - h;
+            if (j <= (lo + h - 1)) break;
+         }
+         ptr[j] = v;
+         i++;
+
+         /*-- copy 2 --*/
+         if (i > hi) break;
+         v = ptr[i];
+         j = i;
+         while ( mainGtU ( 
+                    ptr[j-h]+d, v+d, block, quadrant, nblock, budget 
+                 ) ) {
+            ptr[j] = ptr[j-h];
+            j = j - h;
+            if (j <= (lo + h - 1)) break;
+         }
+         ptr[j] = v;
+         i++;
+
+         /*-- copy 3 --*/
+         if (i > hi) break;
+         v = ptr[i];
+         j = i;
+         while ( mainGtU ( 
+                    ptr[j-h]+d, v+d, block, quadrant, nblock, budget 
+                 ) ) {
+            ptr[j] = ptr[j-h];
+            j = j - h;
+            if (j <= (lo + h - 1)) break;
+         }
+         ptr[j] = v;
+         i++;
+
+         if (*budget < 0) return;
+      }
+   }
+}
+
+
+/*---------------------------------------------*/
+/*--
+   The following is an implementation of
+   an elegant 3-way quicksort for strings,
+   described in a paper "Fast Algorithms for
+   Sorting and Searching Strings", by Robert
+   Sedgewick and Jon L. Bentley.
+--*/
+
+#define mswap(zz1, zz2) \
+   { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; }
+
+#define mvswap(zzp1, zzp2, zzn)       \
+{                                     \
+   Int32 yyp1 = (zzp1);               \
+   Int32 yyp2 = (zzp2);               \
+   Int32 yyn  = (zzn);                \
+   while (yyn > 0) {                  \
+      mswap(ptr[yyp1], ptr[yyp2]);    \
+      yyp1++; yyp2++; yyn--;          \
+   }                                  \
+}
+
+static 
+__inline__
+UChar mmed3 ( UChar a, UChar b, UChar c )
+{
+   UChar t;
+   if (a > b) { t = a; a = b; b = t; };
+   if (b > c) { 
+      b = c;
+      if (a > b) b = a;
+   }
+   return b;
+}
+
+#define mmin(a,b) ((a) < (b)) ? (a) : (b)
+
+#define mpush(lz,hz,dz) { stackLo[sp] = lz; \
+                          stackHi[sp] = hz; \
+                          stackD [sp] = dz; \
+                          sp++; }
+
+#define mpop(lz,hz,dz) { sp--;             \
+                         lz = stackLo[sp]; \
+                         hz = stackHi[sp]; \
+                         dz = stackD [sp]; }
+
+
+#define mnextsize(az) (nextHi[az]-nextLo[az])
+
+#define mnextswap(az,bz)                                        \
+   { Int32 tz;                                                  \
+     tz = nextLo[az]; nextLo[az] = nextLo[bz]; nextLo[bz] = tz; \
+     tz = nextHi[az]; nextHi[az] = nextHi[bz]; nextHi[bz] = tz; \
+     tz = nextD [az]; nextD [az] = nextD [bz]; nextD [bz] = tz; }
+
+
+#define MAIN_QSORT_SMALL_THRESH 20
+#define MAIN_QSORT_DEPTH_THRESH (BZ_N_RADIX + BZ_N_QSORT)
+#define MAIN_QSORT_STACK_SIZE 100
+
+static
+void mainQSort3 ( UInt32* ptr,
+                  UChar*  block,
+                  UInt16* quadrant,
+                  Int32   nblock,
+                  Int32   loSt, 
+                  Int32   hiSt, 
+                  Int32   dSt,
+                  Int32*  budget )
+{
+   Int32 unLo, unHi, ltLo, gtHi, n, m, med;
+   Int32 sp, lo, hi, d;
+
+   Int32 stackLo[MAIN_QSORT_STACK_SIZE];
+   Int32 stackHi[MAIN_QSORT_STACK_SIZE];
+   Int32 stackD [MAIN_QSORT_STACK_SIZE];
+
+   Int32 nextLo[3];
+   Int32 nextHi[3];
+   Int32 nextD [3];
+
+   sp = 0;
+   mpush ( loSt, hiSt, dSt );
+
+   while (sp > 0) {
+
+      AssertH ( sp < MAIN_QSORT_STACK_SIZE, 1001 );
+
+      mpop ( lo, hi, d );
+      if (hi - lo < MAIN_QSORT_SMALL_THRESH || 
+          d > MAIN_QSORT_DEPTH_THRESH) {
+         mainSimpleSort ( ptr, block, quadrant, nblock, lo, hi, d, budget );
+         if (*budget < 0) return;
+         continue;
+      }
+
+      med = (Int32) 
+            mmed3 ( block[ptr[ lo         ]+d],
+                    block[ptr[ hi         ]+d],
+                    block[ptr[ (lo+hi)>>1 ]+d] );
+
+      unLo = ltLo = lo;
+      unHi = gtHi = hi;
+
+      while (True) {
+         while (True) {
+            if (unLo > unHi) break;
+            n = ((Int32)block[ptr[unLo]+d]) - med;
+            if (n == 0) { 
+               mswap(ptr[unLo], ptr[ltLo]); 
+               ltLo++; unLo++; continue; 
+            };
+            if (n >  0) break;
+            unLo++;
+         }
+         while (True) {
+            if (unLo > unHi) break;
+            n = ((Int32)block[ptr[unHi]+d]) - med;
+            if (n == 0) { 
+               mswap(ptr[unHi], ptr[gtHi]); 
+               gtHi--; unHi--; continue; 
+            };
+            if (n <  0) break;
+            unHi--;
+         }
+         if (unLo > unHi) break;
+         mswap(ptr[unLo], ptr[unHi]); unLo++; unHi--;
+      }
+
+      AssertD ( unHi == unLo-1, "mainQSort3(2)" );
+
+      if (gtHi < ltLo) {
+         mpush(lo, hi, d+1 );
+         continue;
+      }
+
+      n = mmin(ltLo-lo, unLo-ltLo); mvswap(lo, unLo-n, n);
+      m = mmin(hi-gtHi, gtHi-unHi); mvswap(unLo, hi-m+1, m);
+
+      n = lo + unLo - ltLo - 1;
+      m = hi - (gtHi - unHi) + 1;
+
+      nextLo[0] = lo;  nextHi[0] = n;   nextD[0] = d;
+      nextLo[1] = m;   nextHi[1] = hi;  nextD[1] = d;
+      nextLo[2] = n+1; nextHi[2] = m-1; nextD[2] = d+1;
+
+      if (mnextsize(0) < mnextsize(1)) mnextswap(0,1);
+      if (mnextsize(1) < mnextsize(2)) mnextswap(1,2);
+      if (mnextsize(0) < mnextsize(1)) mnextswap(0,1);
+
+      AssertD (mnextsize(0) >= mnextsize(1), "mainQSort3(8)" );
+      AssertD (mnextsize(1) >= mnextsize(2), "mainQSort3(9)" );
+
+      mpush (nextLo[0], nextHi[0], nextD[0]);
+      mpush (nextLo[1], nextHi[1], nextD[1]);
+      mpush (nextLo[2], nextHi[2], nextD[2]);
+   }
+}
+
+#undef mswap
+#undef mvswap
+#undef mpush
+#undef mpop
+#undef mmin
+#undef mnextsize
+#undef mnextswap
+#undef MAIN_QSORT_SMALL_THRESH
+#undef MAIN_QSORT_DEPTH_THRESH
+#undef MAIN_QSORT_STACK_SIZE
+
+
+/*---------------------------------------------*/
+/* Pre:
+      nblock > N_OVERSHOOT
+      block32 exists for [0 .. nblock-1 +N_OVERSHOOT]
+      ((UChar*)block32) [0 .. nblock-1] holds block
+      ptr exists for [0 .. nblock-1]
+
+   Post:
+      ((UChar*)block32) [0 .. nblock-1] holds block
+      All other areas of block32 destroyed
+      ftab [0 .. 65536 ] destroyed
+      ptr [0 .. nblock-1] holds sorted order
+      if (*budget < 0), sorting was abandoned
+*/
+
+#define BIGFREQ(b) (ftab[((b)+1) << 8] - ftab[(b) << 8])
+#define SETMASK (1 << 21)
+#define CLEARMASK (~(SETMASK))
+
+static
+void mainSort ( UInt32* ptr, 
+                UChar*  block,
+                UInt16* quadrant, 
+                UInt32* ftab,
+                Int32   nblock,
+                Int32   verb,
+                Int32*  budget )
+{
+   Int32  i, j, k, ss, sb;
+   Int32  runningOrder[256];
+   Bool   bigDone[256];
+   Int32  copyStart[256];
+   Int32  copyEnd  [256];
+   UChar  c1;
+   Int32  numQSorted;
+   UInt16 s;
+   if (verb >= 4) VPrintf0 ( "        main sort initialise ...\n" );
+
+   /*-- set up the 2-byte frequency table --*/
+   for (i = 65536; i >= 0; i--) ftab[i] = 0;
+
+   j = block[0] << 8;
+   i = nblock-1;
+   for (; i >= 3; i -= 4) {
+      quadrant[i] = 0;
+      j = (j >> 8) | ( ((UInt16)block[i]) << 8);
+      ftab[j]++;
+      quadrant[i-1] = 0;
+      j = (j >> 8) | ( ((UInt16)block[i-1]) << 8);
+      ftab[j]++;
+      quadrant[i-2] = 0;
+      j = (j >> 8) | ( ((UInt16)block[i-2]) << 8);
+      ftab[j]++;
+      quadrant[i-3] = 0;
+      j = (j >> 8) | ( ((UInt16)block[i-3]) << 8);
+      ftab[j]++;
+   }
+   for (; i >= 0; i--) {
+      quadrant[i] = 0;
+      j = (j >> 8) | ( ((UInt16)block[i]) << 8);
+      ftab[j]++;
+   }
+
+   /*-- (emphasises close relationship of block & quadrant) --*/
+   for (i = 0; i < BZ_N_OVERSHOOT; i++) {
+      block   [nblock+i] = block[i];
+      quadrant[nblock+i] = 0;
+   }
+
+   if (verb >= 4) VPrintf0 ( "        bucket sorting ...\n" );
+
+   /*-- Complete the initial radix sort --*/
+   for (i = 1; i <= 65536; i++) ftab[i] += ftab[i-1];
+
+   s = block[0] << 8;
+   i = nblock-1;
+   for (; i >= 3; i -= 4) {
+      s = (s >> 8) | (block[i] << 8);
+      j = ftab[s] -1;
+      ftab[s] = j;
+      ptr[j] = i;
+      s = (s >> 8) | (block[i-1] << 8);
+      j = ftab[s] -1;
+      ftab[s] = j;
+      ptr[j] = i-1;
+      s = (s >> 8) | (block[i-2] << 8);
+      j = ftab[s] -1;
+      ftab[s] = j;
+      ptr[j] = i-2;
+      s = (s >> 8) | (block[i-3] << 8);
+      j = ftab[s] -1;
+      ftab[s] = j;
+      ptr[j] = i-3;
+   }
+   for (; i >= 0; i--) {
+      s = (s >> 8) | (block[i] << 8);
+      j = ftab[s] -1;
+      ftab[s] = j;
+      ptr[j] = i;
+   }
+
+   /*--
+      Now ftab contains the first loc of every small bucket.
+      Calculate the running order, from smallest to largest
+      big bucket.
+   --*/
+   for (i = 0; i <= 255; i++) {
+      bigDone     [i] = False;
+      runningOrder[i] = i;
+   }
+
+   {
+      Int32 vv;
+      Int32 h = 1;
+      do h = 3 * h + 1; while (h <= 256);
+      do {
+         h = h / 3;
+         for (i = h; i <= 255; i++) {
+            vv = runningOrder[i];
+            j = i;
+            while ( BIGFREQ(runningOrder[j-h]) > BIGFREQ(vv) ) {
+               runningOrder[j] = runningOrder[j-h];
+               j = j - h;
+               if (j <= (h - 1)) goto zero;
+            }
+            zero:
+            runningOrder[j] = vv;
+         }
+      } while (h != 1);
+   }
+
+   /*--
+      The main sorting loop.
+   --*/
+
+   numQSorted = 0;
+
+   for (i = 0; i <= 255; i++) {
+
+      /*--
+         Process big buckets, starting with the least full.
+         Basically this is a 3-step process in which we call
+         mainQSort3 to sort the small buckets [ss, j], but
+         also make a big effort to avoid the calls if we can.
+      --*/
+      ss = runningOrder[i];
+
+      /*--
+         Step 1:
+         Complete the big bucket [ss] by quicksorting
+         any unsorted small buckets [ss, j], for j != ss.  
+         Hopefully previous pointer-scanning phases have already
+         completed many of the small buckets [ss, j], so
+         we don't have to sort them at all.
+      --*/
+      for (j = 0; j <= 255; j++) {
+         if (j != ss) {
+            sb = (ss << 8) + j;
+            if ( ! (ftab[sb] & SETMASK) ) {
+               Int32 lo = ftab[sb]   & CLEARMASK;
+               Int32 hi = (ftab[sb+1] & CLEARMASK) - 1;
+               if (hi > lo) {
+                  if (verb >= 4)
+                     VPrintf4 ( "        qsort [0x%x, 0x%x]   "
+                                "done %d   this %d\n",
+                                ss, j, numQSorted, hi - lo + 1 );
+                  mainQSort3 ( 
+                     ptr, block, quadrant, nblock, 
+                     lo, hi, BZ_N_RADIX, budget 
+                  );   
+                  numQSorted += (hi - lo + 1);
+                  if (*budget < 0) return;
+               }
+            }
+            ftab[sb] |= SETMASK;
+         }
+      }
+
+      AssertH ( !bigDone[ss], 1006 );
+
+      /*--
+         Step 2:
+         Now scan this big bucket [ss] so as to synthesise the
+         sorted order for small buckets [t, ss] for all t,
+         including, magically, the bucket [ss,ss] too.
+         This will avoid doing Real Work in subsequent Step 1's.
+      --*/
+      {
+         for (j = 0; j <= 255; j++) {
+            copyStart[j] =  ftab[(j << 8) + ss]     & CLEARMASK;
+            copyEnd  [j] = (ftab[(j << 8) + ss + 1] & CLEARMASK) - 1;
+         }
+         for (j = ftab[ss << 8] & CLEARMASK; j < copyStart[ss]; j++) {
+            k = ptr[j]-1; if (k < 0) k += nblock;
+            c1 = block[k];
+            if (!bigDone[c1])
+               ptr[ copyStart[c1]++ ] = k;
+         }
+         for (j = (ftab[(ss+1) << 8] & CLEARMASK) - 1; j > copyEnd[ss]; j--) {
+            k = ptr[j]-1; if (k < 0) k += nblock;
+            c1 = block[k];
+            if (!bigDone[c1]) 
+               ptr[ copyEnd[c1]-- ] = k;
+         }
+      }
+
+      AssertH ( (copyStart[ss]-1 == copyEnd[ss])
+                || 
+                /* Extremely rare case missing in bzip2-1.0.0 and 1.0.1.
+                   Necessity for this case is demonstrated by compressing 
+                   a sequence of approximately 48.5 million of character 
+                   251; 1.0.0/1.0.1 will then die here. */
+                (copyStart[ss] == 0 && copyEnd[ss] == nblock-1),
+                1007 )
+
+      for (j = 0; j <= 255; j++) ftab[(j << 8) + ss] |= SETMASK;
+
+      /*--
+         Step 3:
+         The [ss] big bucket is now done.  Record this fact,
+         and update the quadrant descriptors.  Remember to
+         update quadrants in the overshoot area too, if
+         necessary.  The "if (i < 255)" test merely skips
+         this updating for the last bucket processed, since
+         updating for the last bucket is pointless.
+
+         The quadrant array provides a way to incrementally
+         cache sort orderings, as they appear, so as to 
+         make subsequent comparisons in fullGtU() complete
+         faster.  For repetitive blocks this makes a big
+         difference (but not big enough to be able to avoid
+         the fallback sorting mechanism, exponential radix sort).
+
+         The precise meaning is: at all times:
+
+            for 0 <= i < nblock and 0 <= j <= nblock
+
+            if block[i] != block[j], 
+
+               then the relative values of quadrant[i] and 
+                    quadrant[j] are meaningless.
+
+               else {
+                  if quadrant[i] < quadrant[j]
+                     then the string starting at i lexicographically
+                     precedes the string starting at j
+
+                  else if quadrant[i] > quadrant[j]
+                     then the string starting at j lexicographically
+                     precedes the string starting at i
+
+                  else
+                     the relative ordering of the strings starting
+                     at i and j has not yet been determined.
+               }
+      --*/
+      bigDone[ss] = True;
+
+      if (i < 255) {
+         Int32 bbStart  = ftab[ss << 8] & CLEARMASK;
+         Int32 bbSize   = (ftab[(ss+1) << 8] & CLEARMASK) - bbStart;
+         Int32 shifts   = 0;
+
+         while ((bbSize >> shifts) > 65534) shifts++;
+
+         for (j = bbSize-1; j >= 0; j--) {
+            Int32 a2update     = ptr[bbStart + j];
+            UInt16 qVal        = (UInt16)(j >> shifts);
+            quadrant[a2update] = qVal;
+            if (a2update < BZ_N_OVERSHOOT)
+               quadrant[a2update + nblock] = qVal;
+         }
+         AssertH ( ((bbSize-1) >> shifts) <= 65535, 1002 );
+      }
+
+   }
+
+   if (verb >= 4)
+      VPrintf3 ( "        %d pointers, %d sorted, %d scanned\n",
+                 nblock, numQSorted, nblock - numQSorted );
+}
+
+#undef BIGFREQ
+#undef SETMASK
+#undef CLEARMASK
+
+
+/*---------------------------------------------*/
+/* Pre:
+      nblock > 0
+      arr2 exists for [0 .. nblock-1 +N_OVERSHOOT]
+      ((UChar*)arr2)  [0 .. nblock-1] holds block
+      arr1 exists for [0 .. nblock-1]
+
+   Post:
+      ((UChar*)arr2) [0 .. nblock-1] holds block
+      All other areas of block destroyed
+      ftab [ 0 .. 65536 ] destroyed
+      arr1 [0 .. nblock-1] holds sorted order
+*/
+void BZ2_blockSort ( EState* s )
+{
+   UInt32* ptr    = s->ptr; 
+   UChar*  block  = s->block;
+   UInt32* ftab   = s->ftab;
+   Int32   nblock = s->nblock;
+   Int32   verb   = s->verbosity;
+   Int32   wfact  = s->workFactor;
+   UInt16* quadrant;
+   Int32   budget;
+   Int32   budgetInit;
+   Int32   i;
+
+   if (nblock < 10000) {
+      fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb );
+   } else {
+      /* Calculate the location for quadrant, remembering to get
+         the alignment right.  Assumes that &(block[0]) is at least
+         2-byte aligned -- this should be ok since block is really
+         the first section of arr2.
+      */
+      i = nblock+BZ_N_OVERSHOOT;
+      if (i & 1) i++;
+      quadrant = (UInt16*)(&(block[i]));
+
+      /* (wfact-1) / 3 puts the default-factor-30
+         transition point at very roughly the same place as 
+         with v0.1 and v0.9.0.  
+         Not that it particularly matters any more, since the
+         resulting compressed stream is now the same regardless
+         of whether or not we use the main sort or fallback sort.
+      */
+      if (wfact < 1  ) wfact = 1;
+      if (wfact > 100) wfact = 100;
+      budgetInit = nblock * ((wfact-1) / 3);
+      budget = budgetInit;
+
+      mainSort ( ptr, block, quadrant, ftab, nblock, verb, &budget );
+      if (verb >= 3) 
+         VPrintf3 ( "      %d work, %d block, ratio %5.2f\n",
+                    budgetInit - budget,
+                    nblock, 
+                    (float)(budgetInit - budget) /
+                    (float)(nblock==0 ? 1 : nblock) ); 
+      if (budget < 0) {
+         if (verb >= 2) 
+            VPrintf0 ( "    too repetitive; using fallback"
+                       " sorting algorithm\n" );
+         fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb );
+      }
+   }
+
+   s->origPtr = -1;
+   for (i = 0; i < s->nblock; i++)
+      if (ptr[i] == 0)
+         { s->origPtr = i; break; };
+
+   AssertH( s->origPtr != -1, 1003 );
+}
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                       blocksort.c ---*/
+/*-------------------------------------------------------------*/
+
+/*-------------------------------------------------------------*/
+/*--- Huffman coding low-level stuff                        ---*/
+/*---                                             huffman.c ---*/
+/*-------------------------------------------------------------*/
+
+
+/*---------------------------------------------------*/
+#define WEIGHTOF(zz0)  ((zz0) & 0xffffff00)
+#define DEPTHOF(zz1)   ((zz1) & 0x000000ff)
+#define MYMAX(zz2,zz3) ((zz2) > (zz3) ? (zz2) : (zz3))
+
+#define ADDWEIGHTS(zw1,zw2)                           \
+   (WEIGHTOF(zw1)+WEIGHTOF(zw2)) |                    \
+   (1 + MYMAX(DEPTHOF(zw1),DEPTHOF(zw2)))
+
+#define UPHEAP(z)                                     \
+{                                                     \
+   Int32 zz, tmp;                                     \
+   zz = z; tmp = heap[zz];                            \
+   while (weight[tmp] < weight[heap[zz >> 1]]) {      \
+      heap[zz] = heap[zz >> 1];                       \
+      zz >>= 1;                                       \
+   }                                                  \
+   heap[zz] = tmp;                                    \
+}
+
+#define DOWNHEAP(z)                                   \
+{                                                     \
+   Int32 zz, yy, tmp;                                 \
+   zz = z; tmp = heap[zz];                            \
+   while (True) {                                     \
+      yy = zz << 1;                                   \
+      if (yy > nHeap) break;                          \
+      if (yy < nHeap &&                               \
+          weight[heap[yy+1]] < weight[heap[yy]])      \
+         yy++;                                        \
+      if (weight[tmp] < weight[heap[yy]]) break;      \
+      heap[zz] = heap[yy];                            \
+      zz = yy;                                        \
+   }                                                  \
+   heap[zz] = tmp;                                    \
+}
+
+
+/*---------------------------------------------------*/
+void BZ2_hbMakeCodeLengths ( UChar *len, 
+                             Int32 *freq,
+                             Int32 alphaSize,
+                             Int32 maxLen )
+{
+   /*--
+      Nodes and heap entries run from 1.  Entry 0
+      for both the heap and nodes is a sentinel.
+   --*/
+   Int32 nNodes, nHeap, n1, n2, i, j, k;
+   Bool  tooLong;
+
+   Int32 heap   [ BZ_MAX_ALPHA_SIZE + 2 ];
+   Int32 weight [ BZ_MAX_ALPHA_SIZE * 2 ];
+   Int32 parent [ BZ_MAX_ALPHA_SIZE * 2 ]; 
+
+   for (i = 0; i < alphaSize; i++)
+      weight[i+1] = (freq[i] == 0 ? 1 : freq[i]) << 8;
+
+   while (True) {
+
+      nNodes = alphaSize;
+      nHeap = 0;
+
+      heap[0] = 0;
+      weight[0] = 0;
+      parent[0] = -2;
+
+      for (i = 1; i <= alphaSize; i++) {
+         parent[i] = -1;
+         nHeap++;
+         heap[nHeap] = i;
+         UPHEAP(nHeap);
+      }
+
+      AssertH( nHeap < (BZ_MAX_ALPHA_SIZE+2), 2001 );
+   
+      while (nHeap > 1) {
+         n1 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1);
+         n2 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1);
+         nNodes++;
+         parent[n1] = parent[n2] = nNodes;
+         weight[nNodes] = ADDWEIGHTS(weight[n1], weight[n2]);
+         parent[nNodes] = -1;
+         nHeap++;
+         heap[nHeap] = nNodes;
+         UPHEAP(nHeap);
+      }
+
+      AssertH( nNodes < (BZ_MAX_ALPHA_SIZE * 2), 2002 );
+
+      tooLong = False;
+      for (i = 1; i <= alphaSize; i++) {
+         j = 0;
+         k = i;
+         while (parent[k] >= 0) { k = parent[k]; j++; }
+         len[i-1] = j;
+         if (j > maxLen) tooLong = True;
+      }
+      
+      if (! tooLong) break;
+
+      for (i = 1; i < alphaSize; i++) {
+         j = weight[i] >> 8;
+         j = 1 + (j / 2);
+         weight[i] = j << 8;
+      }
+   }
+}
+
+
+/*---------------------------------------------------*/
+void BZ2_hbAssignCodes ( Int32 *code,
+                         UChar *length,
+                         Int32 minLen,
+                         Int32 maxLen,
+                         Int32 alphaSize )
+{
+   Int32 n, vec, i;
+
+   vec = 0;
+   for (n = minLen; n <= maxLen; n++) {
+      for (i = 0; i < alphaSize; i++)
+         if (length[i] == n) { code[i] = vec; vec++; };
+      vec <<= 1;
+   }
+}
+
+
+/*---------------------------------------------------*/
+void BZ2_hbCreateDecodeTables ( Int32 *limit,
+                                Int32 *base,
+                                Int32 *perm,
+                                UChar *length,
+                                Int32 minLen,
+                                Int32 maxLen,
+                                Int32 alphaSize )
+{
+   Int32 pp, i, j, vec;
+
+   pp = 0;
+   for (i = minLen; i <= maxLen; i++)
+      for (j = 0; j < alphaSize; j++)
+         if (length[j] == i) { perm[pp] = j; pp++; };
+
+   for (i = 0; i < BZ_MAX_CODE_LEN; i++) base[i] = 0;
+   for (i = 0; i < alphaSize; i++) base[length[i]+1]++;
+
+   for (i = 1; i < BZ_MAX_CODE_LEN; i++) base[i] += base[i-1];
+
+   for (i = 0; i < BZ_MAX_CODE_LEN; i++) limit[i] = 0;
+   vec = 0;
+
+   for (i = minLen; i <= maxLen; i++) {
+      vec += (base[i+1] - base[i]);
+      limit[i] = vec-1;
+      vec <<= 1;
+   }
+   for (i = minLen + 1; i <= maxLen; i++)
+      base[i] = ((limit[i-1] + 1) << 1) - base[i];
+}
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                         huffman.c ---*/
+/*-------------------------------------------------------------*/
+
+/*-------------------------------------------------------------*/
+/*--- Table for doing CRCs                                  ---*/
+/*---                                            crctable.c ---*/
+/*-------------------------------------------------------------*/
+
+
+/*--
+  I think this is an implementation of the AUTODIN-II,
+  Ethernet & FDDI 32-bit CRC standard.  Vaguely derived
+  from code by Rob Warnock, in Section 51 of the
+  comp.compression FAQ.
+--*/
+
+UInt32 BZ2_crc32Table[256] = {
+
+   /*-- Ugly, innit? --*/
+
+   0x00000000L, 0x04c11db7L, 0x09823b6eL, 0x0d4326d9L,
+   0x130476dcL, 0x17c56b6bL, 0x1a864db2L, 0x1e475005L,
+   0x2608edb8L, 0x22c9f00fL, 0x2f8ad6d6L, 0x2b4bcb61L,
+   0x350c9b64L, 0x31cd86d3L, 0x3c8ea00aL, 0x384fbdbdL,
+   0x4c11db70L, 0x48d0c6c7L, 0x4593e01eL, 0x4152fda9L,
+   0x5f15adacL, 0x5bd4b01bL, 0x569796c2L, 0x52568b75L,
+   0x6a1936c8L, 0x6ed82b7fL, 0x639b0da6L, 0x675a1011L,
+   0x791d4014L, 0x7ddc5da3L, 0x709f7b7aL, 0x745e66cdL,
+   0x9823b6e0L, 0x9ce2ab57L, 0x91a18d8eL, 0x95609039L,
+   0x8b27c03cL, 0x8fe6dd8bL, 0x82a5fb52L, 0x8664e6e5L,
+   0xbe2b5b58L, 0xbaea46efL, 0xb7a96036L, 0xb3687d81L,
+   0xad2f2d84L, 0xa9ee3033L, 0xa4ad16eaL, 0xa06c0b5dL,
+   0xd4326d90L, 0xd0f37027L, 0xddb056feL, 0xd9714b49L,
+   0xc7361b4cL, 0xc3f706fbL, 0xceb42022L, 0xca753d95L,
+   0xf23a8028L, 0xf6fb9d9fL, 0xfbb8bb46L, 0xff79a6f1L,
+   0xe13ef6f4L, 0xe5ffeb43L, 0xe8bccd9aL, 0xec7dd02dL,
+   0x34867077L, 0x30476dc0L, 0x3d044b19L, 0x39c556aeL,
+   0x278206abL, 0x23431b1cL, 0x2e003dc5L, 0x2ac12072L,
+   0x128e9dcfL, 0x164f8078L, 0x1b0ca6a1L, 0x1fcdbb16L,
+   0x018aeb13L, 0x054bf6a4L, 0x0808d07dL, 0x0cc9cdcaL,
+   0x7897ab07L, 0x7c56b6b0L, 0x71159069L, 0x75d48ddeL,
+   0x6b93dddbL, 0x6f52c06cL, 0x6211e6b5L, 0x66d0fb02L,
+   0x5e9f46bfL, 0x5a5e5b08L, 0x571d7dd1L, 0x53dc6066L,
+   0x4d9b3063L, 0x495a2dd4L, 0x44190b0dL, 0x40d816baL,
+   0xaca5c697L, 0xa864db20L, 0xa527fdf9L, 0xa1e6e04eL,
+   0xbfa1b04bL, 0xbb60adfcL, 0xb6238b25L, 0xb2e29692L,
+   0x8aad2b2fL, 0x8e6c3698L, 0x832f1041L, 0x87ee0df6L,
+   0x99a95df3L, 0x9d684044L, 0x902b669dL, 0x94ea7b2aL,
+   0xe0b41de7L, 0xe4750050L, 0xe9362689L, 0xedf73b3eL,
+   0xf3b06b3bL, 0xf771768cL, 0xfa325055L, 0xfef34de2L,
+   0xc6bcf05fL, 0xc27dede8L, 0xcf3ecb31L, 0xcbffd686L,
+   0xd5b88683L, 0xd1799b34L, 0xdc3abdedL, 0xd8fba05aL,
+   0x690ce0eeL, 0x6dcdfd59L, 0x608edb80L, 0x644fc637L,
+   0x7a089632L, 0x7ec98b85L, 0x738aad5cL, 0x774bb0ebL,
+   0x4f040d56L, 0x4bc510e1L, 0x46863638L, 0x42472b8fL,
+   0x5c007b8aL, 0x58c1663dL, 0x558240e4L, 0x51435d53L,
+   0x251d3b9eL, 0x21dc2629L, 0x2c9f00f0L, 0x285e1d47L,
+   0x36194d42L, 0x32d850f5L, 0x3f9b762cL, 0x3b5a6b9bL,
+   0x0315d626L, 0x07d4cb91L, 0x0a97ed48L, 0x0e56f0ffL,
+   0x1011a0faL, 0x14d0bd4dL, 0x19939b94L, 0x1d528623L,
+   0xf12f560eL, 0xf5ee4bb9L, 0xf8ad6d60L, 0xfc6c70d7L,
+   0xe22b20d2L, 0xe6ea3d65L, 0xeba91bbcL, 0xef68060bL,
+   0xd727bbb6L, 0xd3e6a601L, 0xdea580d8L, 0xda649d6fL,
+   0xc423cd6aL, 0xc0e2d0ddL, 0xcda1f604L, 0xc960ebb3L,
+   0xbd3e8d7eL, 0xb9ff90c9L, 0xb4bcb610L, 0xb07daba7L,
+   0xae3afba2L, 0xaafbe615L, 0xa7b8c0ccL, 0xa379dd7bL,
+   0x9b3660c6L, 0x9ff77d71L, 0x92b45ba8L, 0x9675461fL,
+   0x8832161aL, 0x8cf30badL, 0x81b02d74L, 0x857130c3L,
+   0x5d8a9099L, 0x594b8d2eL, 0x5408abf7L, 0x50c9b640L,
+   0x4e8ee645L, 0x4a4ffbf2L, 0x470cdd2bL, 0x43cdc09cL,
+   0x7b827d21L, 0x7f436096L, 0x7200464fL, 0x76c15bf8L,
+   0x68860bfdL, 0x6c47164aL, 0x61043093L, 0x65c52d24L,
+   0x119b4be9L, 0x155a565eL, 0x18197087L, 0x1cd86d30L,
+   0x029f3d35L, 0x065e2082L, 0x0b1d065bL, 0x0fdc1becL,
+   0x3793a651L, 0x3352bbe6L, 0x3e119d3fL, 0x3ad08088L,
+   0x2497d08dL, 0x2056cd3aL, 0x2d15ebe3L, 0x29d4f654L,
+   0xc5a92679L, 0xc1683bceL, 0xcc2b1d17L, 0xc8ea00a0L,
+   0xd6ad50a5L, 0xd26c4d12L, 0xdf2f6bcbL, 0xdbee767cL,
+   0xe3a1cbc1L, 0xe760d676L, 0xea23f0afL, 0xeee2ed18L,
+   0xf0a5bd1dL, 0xf464a0aaL, 0xf9278673L, 0xfde69bc4L,
+   0x89b8fd09L, 0x8d79e0beL, 0x803ac667L, 0x84fbdbd0L,
+   0x9abc8bd5L, 0x9e7d9662L, 0x933eb0bbL, 0x97ffad0cL,
+   0xafb010b1L, 0xab710d06L, 0xa6322bdfL, 0xa2f33668L,
+   0xbcb4666dL, 0xb8757bdaL, 0xb5365d03L, 0xb1f740b4L
+};
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                        crctable.c ---*/
+/*-------------------------------------------------------------*/
+
+/*-------------------------------------------------------------*/
+/*--- Table for randomising repetitive blocks               ---*/
+/*---                                           randtable.c ---*/
+/*-------------------------------------------------------------*/
+
+
+/*---------------------------------------------*/
+Int32 BZ2_rNums[512] = { 
+   619, 720, 127, 481, 931, 816, 813, 233, 566, 247, 
+   985, 724, 205, 454, 863, 491, 741, 242, 949, 214, 
+   733, 859, 335, 708, 621, 574, 73, 654, 730, 472, 
+   419, 436, 278, 496, 867, 210, 399, 680, 480, 51, 
+   878, 465, 811, 169, 869, 675, 611, 697, 867, 561, 
+   862, 687, 507, 283, 482, 129, 807, 591, 733, 623, 
+   150, 238, 59, 379, 684, 877, 625, 169, 643, 105, 
+   170, 607, 520, 932, 727, 476, 693, 425, 174, 647, 
+   73, 122, 335, 530, 442, 853, 695, 249, 445, 515, 
+   909, 545, 703, 919, 874, 474, 882, 500, 594, 612, 
+   641, 801, 220, 162, 819, 984, 589, 513, 495, 799, 
+   161, 604, 958, 533, 221, 400, 386, 867, 600, 782, 
+   382, 596, 414, 171, 516, 375, 682, 485, 911, 276, 
+   98, 553, 163, 354, 666, 933, 424, 341, 533, 870, 
+   227, 730, 475, 186, 263, 647, 537, 686, 600, 224, 
+   469, 68, 770, 919, 190, 373, 294, 822, 808, 206, 
+   184, 943, 795, 384, 383, 461, 404, 758, 839, 887, 
+   715, 67, 618, 276, 204, 918, 873, 777, 604, 560, 
+   951, 160, 578, 722, 79, 804, 96, 409, 713, 940, 
+   652, 934, 970, 447, 318, 353, 859, 672, 112, 785, 
+   645, 863, 803, 350, 139, 93, 354, 99, 820, 908, 
+   609, 772, 154, 274, 580, 184, 79, 626, 630, 742, 
+   653, 282, 762, 623, 680, 81, 927, 626, 789, 125, 
+   411, 521, 938, 300, 821, 78, 343, 175, 128, 250, 
+   170, 774, 972, 275, 999, 639, 495, 78, 352, 126, 
+   857, 956, 358, 619, 580, 124, 737, 594, 701, 612, 
+   669, 112, 134, 694, 363, 992, 809, 743, 168, 974, 
+   944, 375, 748, 52, 600, 747, 642, 182, 862, 81, 
+   344, 805, 988, 739, 511, 655, 814, 334, 249, 515, 
+   897, 955, 664, 981, 649, 113, 974, 459, 893, 228, 
+   433, 837, 553, 268, 926, 240, 102, 654, 459, 51, 
+   686, 754, 806, 760, 493, 403, 415, 394, 687, 700, 
+   946, 670, 656, 610, 738, 392, 760, 799, 887, 653, 
+   978, 321, 576, 617, 626, 502, 894, 679, 243, 440, 
+   680, 879, 194, 572, 640, 724, 926, 56, 204, 700, 
+   707, 151, 457, 449, 797, 195, 791, 558, 945, 679, 
+   297, 59, 87, 824, 713, 663, 412, 693, 342, 606, 
+   134, 108, 571, 364, 631, 212, 174, 643, 304, 329, 
+   343, 97, 430, 751, 497, 314, 983, 374, 822, 928, 
+   140, 206, 73, 263, 980, 736, 876, 478, 430, 305, 
+   170, 514, 364, 692, 829, 82, 855, 953, 676, 246, 
+   369, 970, 294, 750, 807, 827, 150, 790, 288, 923, 
+   804, 378, 215, 828, 592, 281, 565, 555, 710, 82, 
+   896, 831, 547, 261, 524, 462, 293, 465, 502, 56, 
+   661, 821, 976, 991, 658, 869, 905, 758, 745, 193, 
+   768, 550, 608, 933, 378, 286, 215, 979, 792, 961, 
+   61, 688, 793, 644, 986, 403, 106, 366, 905, 644, 
+   372, 567, 466, 434, 645, 210, 389, 550, 919, 135, 
+   780, 773, 635, 389, 707, 100, 626, 958, 165, 504, 
+   920, 176, 193, 713, 857, 265, 203, 50, 668, 108, 
+   645, 990, 626, 197, 510, 357, 358, 850, 858, 364, 
+   936, 638
+};
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                       randtable.c ---*/
+/*-------------------------------------------------------------*/
+
+/*-------------------------------------------------------------*/
+/*--- Compression machinery (not incl block sorting)        ---*/
+/*---                                            compress.c ---*/
+/*-------------------------------------------------------------*/
+
+
+/*---------------------------------------------------*/
+/*--- Bit stream I/O                              ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+void BZ2_bsInitWrite ( EState* s )
+{
+   s->bsLive = 0;
+   s->bsBuff = 0;
+}
+
+
+/*---------------------------------------------------*/
+static
+void bsFinishWrite ( EState* s )
+{
+   while (s->bsLive > 0) {
+      s->zbits[s->numZ] = (UChar)(s->bsBuff >> 24);
+      s->numZ++;
+      s->bsBuff <<= 8;
+      s->bsLive -= 8;
+   }
+}
+
+
+/*---------------------------------------------------*/
+#define bsNEEDW(nz)                           \
+{                                             \
+   while (s->bsLive >= 8) {                   \
+      s->zbits[s->numZ]                       \
+         = (UChar)(s->bsBuff >> 24);          \
+      s->numZ++;                              \
+      s->bsBuff <<= 8;                        \
+      s->bsLive -= 8;                         \
+   }                                          \
+}
+
+
+/*---------------------------------------------------*/
+static
+__inline__
+void bsW ( EState* s, Int32 n, UInt32 v )
+{
+   bsNEEDW ( n );
+   s->bsBuff |= (v << (32 - s->bsLive - n));
+   s->bsLive += n;
+}
+
+
+/*---------------------------------------------------*/
+static
+void bsPutUInt32 ( EState* s, UInt32 u )
+{
+   bsW ( s, 8, (u >> 24) & 0xffL );
+   bsW ( s, 8, (u >> 16) & 0xffL );
+   bsW ( s, 8, (u >>  8) & 0xffL );
+   bsW ( s, 8,  u        & 0xffL );
+}
+
+
+/*---------------------------------------------------*/
+static
+void bsPutUChar ( EState* s, UChar c )
+{
+   bsW( s, 8, (UInt32)c );
+}
+
+
+/*---------------------------------------------------*/
+/*--- The back end proper                         ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+static
+void makeMaps_e ( EState* s )
+{
+   Int32 i;
+   s->nInUse = 0;
+   for (i = 0; i < 256; i++)
+      if (s->inUse[i]) {
+         s->unseqToSeq[i] = s->nInUse;
+         s->nInUse++;
+      }
+}
+
+
+/*---------------------------------------------------*/
+static
+void generateMTFValues ( EState* s )
+{
+   UChar   yy[256];
+   Int32   i, j;
+   Int32   zPend;
+   Int32   wr;
+   Int32   EOB;
+
+   /* 
+      After sorting (eg, here),
+         s->arr1 [ 0 .. s->nblock-1 ] holds sorted order,
+         and
+         ((UChar*)s->arr2) [ 0 .. s->nblock-1 ] 
+         holds the original block data.
+
+      The first thing to do is generate the MTF values,
+      and put them in
+         ((UInt16*)s->arr1) [ 0 .. s->nblock-1 ].
+      Because there are strictly fewer or equal MTF values
+      than block values, ptr values in this area are overwritten
+      with MTF values only when they are no longer needed.
+
+      The final compressed bitstream is generated into the
+      area starting at
+         (UChar*) (&((UChar*)s->arr2)[s->nblock])
+
+      These storage aliases are set up in bzCompressInit(),
+      except for the last one, which is arranged in 
+      compressBlock().
+   */
+   UInt32* ptr   = s->ptr;
+   UChar* block  = s->block;
+   UInt16* mtfv  = s->mtfv;
+
+   makeMaps_e ( s );
+   EOB = s->nInUse+1;
+
+   for (i = 0; i <= EOB; i++) s->mtfFreq[i] = 0;
+
+   wr = 0;
+   zPend = 0;
+   for (i = 0; i < s->nInUse; i++) yy[i] = (UChar) i;
+
+   for (i = 0; i < s->nblock; i++) {
+      UChar ll_i;
+      AssertD ( wr <= i, "generateMTFValues(1)" );
+      j = ptr[i]-1; if (j < 0) j += s->nblock;
+      ll_i = s->unseqToSeq[block[j]];
+      AssertD ( ll_i < s->nInUse, "generateMTFValues(2a)" );
+
+      if (yy[0] == ll_i) { 
+         zPend++;
+      } else {
+
+         if (zPend > 0) {
+            zPend--;
+            while (True) {
+               if (zPend & 1) {
+                  mtfv[wr] = BZ_RUNB; wr++; 
+                  s->mtfFreq[BZ_RUNB]++; 
+               } else {
+                  mtfv[wr] = BZ_RUNA; wr++; 
+                  s->mtfFreq[BZ_RUNA]++; 
+               }
+               if (zPend < 2) break;
+               zPend = (zPend - 2) / 2;
+            };
+            zPend = 0;
+         }
+         {
+            register UChar  rtmp;
+            register UChar* ryy_j;
+            register UChar  rll_i;
+            rtmp  = yy[1];
+            yy[1] = yy[0];
+            ryy_j = &(yy[1]);
+            rll_i = ll_i;
+            while ( rll_i != rtmp ) {
+               register UChar rtmp2;
+               ryy_j++;
+               rtmp2  = rtmp;
+               rtmp   = *ryy_j;
+               *ryy_j = rtmp2;
+            };
+            yy[0] = rtmp;
+            j = ryy_j - &(yy[0]);
+            mtfv[wr] = j+1; wr++; s->mtfFreq[j+1]++;
+         }
+
+      }
+   }
+
+   if (zPend > 0) {
+      zPend--;
+      while (True) {
+         if (zPend & 1) {
+            mtfv[wr] = BZ_RUNB; wr++; 
+            s->mtfFreq[BZ_RUNB]++; 
+         } else {
+            mtfv[wr] = BZ_RUNA; wr++; 
+            s->mtfFreq[BZ_RUNA]++; 
+         }
+         if (zPend < 2) break;
+         zPend = (zPend - 2) / 2;
+      };
+      zPend = 0;
+   }
+
+   mtfv[wr] = EOB; wr++; s->mtfFreq[EOB]++;
+
+   s->nMTF = wr;
+}
+
+
+/*---------------------------------------------------*/
+#define BZ_LESSER_ICOST  0
+#define BZ_GREATER_ICOST 15
+
+static
+void sendMTFValues ( EState* s )
+{
+   Int32 v, t, i, j, gs, ge, totc, bt, bc, iter;
+   Int32 nSelectors, alphaSize, minLen, maxLen, selCtr;
+   Int32 nGroups, nBytes;
+
+   /*--
+   UChar  len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+   is a global since the decoder also needs it.
+
+   Int32  code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+   Int32  rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+   are also globals only used in this proc.
+   Made global to keep stack frame size small.
+   --*/
+
+
+   UInt16 cost[BZ_N_GROUPS];
+   Int32  fave[BZ_N_GROUPS];
+
+   UInt16* mtfv = s->mtfv;
+
+   if (s->verbosity >= 3)
+      VPrintf3( "      %d in block, %d after MTF & 1-2 coding, "
+                "%d+2 syms in use\n", 
+                s->nblock, s->nMTF, s->nInUse );
+
+   alphaSize = s->nInUse+2;
+   for (t = 0; t < BZ_N_GROUPS; t++)
+      for (v = 0; v < alphaSize; v++)
+         s->len[t][v] = BZ_GREATER_ICOST;
+
+   /*--- Decide how many coding tables to use ---*/
+   AssertH ( s->nMTF > 0, 3001 );
+   if (s->nMTF < 200)  nGroups = 2; else
+   if (s->nMTF < 600)  nGroups = 3; else
+   if (s->nMTF < 1200) nGroups = 4; else
+   if (s->nMTF < 2400) nGroups = 5; else
+                       nGroups = 6;
+
+   /*--- Generate an initial set of coding tables ---*/
+   { 
+      Int32 nPart, remF, tFreq, aFreq;
+
+      nPart = nGroups;
+      remF  = s->nMTF;
+      gs = 0;
+      while (nPart > 0) {
+         tFreq = remF / nPart;
+         ge = gs-1;
+         aFreq = 0;
+         while (aFreq < tFreq && ge < alphaSize-1) {
+            ge++;
+            aFreq += s->mtfFreq[ge];
+         }
+
+         if (ge > gs 
+             && nPart != nGroups && nPart != 1 
+             && ((nGroups-nPart) % 2 == 1)) {
+            aFreq -= s->mtfFreq[ge];
+            ge--;
+         }
+
+         if (s->verbosity >= 3)
+            VPrintf5( "      initial group %d, [%d .. %d], "
+                      "has %d syms (%4.1f%%)\n",
+                      nPart, gs, ge, aFreq, 
+                      (100.0 * (float)aFreq) / (float)(s->nMTF) );
+ 
+         for (v = 0; v < alphaSize; v++)
+            if (v >= gs && v <= ge) 
+               s->len[nPart-1][v] = BZ_LESSER_ICOST; else
+               s->len[nPart-1][v] = BZ_GREATER_ICOST;
+ 
+         nPart--;
+         gs = ge+1;
+         remF -= aFreq;
+      }
+   }
+
+   /*--- 
+      Iterate up to BZ_N_ITERS times to improve the tables.
+   ---*/
+   for (iter = 0; iter < BZ_N_ITERS; iter++) {
+
+      for (t = 0; t < nGroups; t++) fave[t] = 0;
+
+      for (t = 0; t < nGroups; t++)
+         for (v = 0; v < alphaSize; v++)
+            s->rfreq[t][v] = 0;
+
+      /*---
+        Set up an auxiliary length table which is used to fast-track
+	the common case (nGroups == 6). 
+      ---*/
+      if (nGroups == 6) {
+         for (v = 0; v < alphaSize; v++) {
+            s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v];
+            s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v];
+            s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v];
+	 }
+      }
+
+      nSelectors = 0;
+      totc = 0;
+      gs = 0;
+      while (True) {
+
+         /*--- Set group start & end marks. --*/
+         if (gs >= s->nMTF) break;
+         ge = gs + BZ_G_SIZE - 1; 
+         if (ge >= s->nMTF) ge = s->nMTF-1;
+
+         /*-- 
+            Calculate the cost of this group as coded
+            by each of the coding tables.
+         --*/
+         for (t = 0; t < nGroups; t++) cost[t] = 0;
+
+         if (nGroups == 6 && 50 == ge-gs+1) {
+            /*--- fast track the common case ---*/
+            register UInt32 cost01, cost23, cost45;
+            register UInt16 icv;
+            cost01 = cost23 = cost45 = 0;
+
+#           define BZ_ITER(nn)                \
+               icv = mtfv[gs+(nn)];           \
+               cost01 += s->len_pack[icv][0]; \
+               cost23 += s->len_pack[icv][1]; \
+               cost45 += s->len_pack[icv][2]; \
+
+            BZ_ITER(0);  BZ_ITER(1);  BZ_ITER(2);  BZ_ITER(3);  BZ_ITER(4);
+            BZ_ITER(5);  BZ_ITER(6);  BZ_ITER(7);  BZ_ITER(8);  BZ_ITER(9);
+            BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14);
+            BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19);
+            BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24);
+            BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29);
+            BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34);
+            BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39);
+            BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44);
+            BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49);
+
+#           undef BZ_ITER
+
+            cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16;
+            cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16;
+            cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16;
+
+         } else {
+	    /*--- slow version which correctly handles all situations ---*/
+            for (i = gs; i <= ge; i++) { 
+               UInt16 icv = mtfv[i];
+               for (t = 0; t < nGroups; t++) cost[t] += s->len[t][icv];
+            }
+         }
+ 
+         /*-- 
+            Find the coding table which is best for this group,
+            and record its identity in the selector table.
+         --*/
+         bc = 999999999; bt = -1;
+         for (t = 0; t < nGroups; t++)
+            if (cost[t] < bc) { bc = cost[t]; bt = t; };
+         totc += bc;
+         fave[bt]++;
+         s->selector[nSelectors] = bt;
+         nSelectors++;
+
+         /*-- 
+            Increment the symbol frequencies for the selected table.
+          --*/
+         if (nGroups == 6 && 50 == ge-gs+1) {
+            /*--- fast track the common case ---*/
+
+#           define BZ_ITUR(nn) s->rfreq[bt][ mtfv[gs+(nn)] ]++
+
+            BZ_ITUR(0);  BZ_ITUR(1);  BZ_ITUR(2);  BZ_ITUR(3);  BZ_ITUR(4);
+            BZ_ITUR(5);  BZ_ITUR(6);  BZ_ITUR(7);  BZ_ITUR(8);  BZ_ITUR(9);
+            BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14);
+            BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19);
+            BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24);
+            BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29);
+            BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34);
+            BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39);
+            BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44);
+            BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49);
+
+#           undef BZ_ITUR
+
+         } else {
+	    /*--- slow version which correctly handles all situations ---*/
+            for (i = gs; i <= ge; i++)
+               s->rfreq[bt][ mtfv[i] ]++;
+         }
+
+         gs = ge+1;
+      }
+      if (s->verbosity >= 3) {
+         VPrintf2 ( "      pass %d: size is %d, grp uses are ", 
+                   iter+1, totc/8 );
+         for (t = 0; t < nGroups; t++)
+            VPrintf1 ( "%d ", fave[t] );
+         VPrintf0 ( "\n" );
+      }
+
+      /*--
+        Recompute the tables based on the accumulated frequencies.
+      --*/
+      for (t = 0; t < nGroups; t++)
+         BZ2_hbMakeCodeLengths ( &(s->len[t][0]), &(s->rfreq[t][0]), 
+                                 alphaSize, 20 );
+   }
+
+
+   AssertH( nGroups < 8, 3002 );
+   AssertH( nSelectors < 32768 &&
+            nSelectors <= (2 + (900000 / BZ_G_SIZE)),
+            3003 );
+
+
+   /*--- Compute MTF values for the selectors. ---*/
+   {
+      UChar pos[BZ_N_GROUPS], ll_i, tmp2, tmp;
+      for (i = 0; i < nGroups; i++) pos[i] = i;
+      for (i = 0; i < nSelectors; i++) {
+         ll_i = s->selector[i];
+         j = 0;
+         tmp = pos[j];
+         while ( ll_i != tmp ) {
+            j++;
+            tmp2 = tmp;
+            tmp = pos[j];
+            pos[j] = tmp2;
+         };
+         pos[0] = tmp;
+         s->selectorMtf[i] = j;
+      }
+   };
+
+   /*--- Assign actual codes for the tables. --*/
+   for (t = 0; t < nGroups; t++) {
+      minLen = 32;
+      maxLen = 0;
+      for (i = 0; i < alphaSize; i++) {
+         if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
+         if (s->len[t][i] < minLen) minLen = s->len[t][i];
+      }
+      AssertH ( !(maxLen > 20), 3004 );
+      AssertH ( !(minLen < 1),  3005 );
+      BZ2_hbAssignCodes ( &(s->code[t][0]), &(s->len[t][0]), 
+                          minLen, maxLen, alphaSize );
+   }
+
+   /*--- Transmit the mapping table. ---*/
+   { 
+      Bool inUse16[16];
+      for (i = 0; i < 16; i++) {
+          inUse16[i] = False;
+          for (j = 0; j < 16; j++)
+             if (s->inUse[i * 16 + j]) inUse16[i] = True;
+      }
+     
+      nBytes = s->numZ;
+      for (i = 0; i < 16; i++)
+         if (inUse16[i]) bsW(s,1,1); else bsW(s,1,0);
+
+      for (i = 0; i < 16; i++)
+         if (inUse16[i])
+            for (j = 0; j < 16; j++) {
+               if (s->inUse[i * 16 + j]) bsW(s,1,1); else bsW(s,1,0);
+            }
+
+      if (s->verbosity >= 3) 
+         VPrintf1( "      bytes: mapping %d, ", s->numZ-nBytes );
+   }
+
+   /*--- Now the selectors. ---*/
+   nBytes = s->numZ;
+   bsW ( s, 3, nGroups );
+   bsW ( s, 15, nSelectors );
+   for (i = 0; i < nSelectors; i++) { 
+      for (j = 0; j < s->selectorMtf[i]; j++) bsW(s,1,1);
+      bsW(s,1,0);
+   }
+   if (s->verbosity >= 3)
+      VPrintf1( "selectors %d, ", s->numZ-nBytes );
+
+   /*--- Now the coding tables. ---*/
+   nBytes = s->numZ;
+
+   for (t = 0; t < nGroups; t++) {
+      Int32 curr = s->len[t][0];
+      bsW ( s, 5, curr );
+      for (i = 0; i < alphaSize; i++) {
+         while (curr < s->len[t][i]) { bsW(s,2,2); curr++; /* 10 */ };
+         while (curr > s->len[t][i]) { bsW(s,2,3); curr--; /* 11 */ };
+         bsW ( s, 1, 0 );
+      }
+   }
+
+   if (s->verbosity >= 3)
+      VPrintf1 ( "code lengths %d, ", s->numZ-nBytes );
+
+   /*--- And finally, the block data proper ---*/
+   nBytes = s->numZ;
+   selCtr = 0;
+   gs = 0;
+   while (True) {
+      if (gs >= s->nMTF) break;
+      ge = gs + BZ_G_SIZE - 1; 
+      if (ge >= s->nMTF) ge = s->nMTF-1;
+      AssertH ( s->selector[selCtr] < nGroups, 3006 );
+
+      if (nGroups == 6 && 50 == ge-gs+1) {
+            /*--- fast track the common case ---*/
+            UInt16 mtfv_i;
+            UChar* s_len_sel_selCtr 
+               = &(s->len[s->selector[selCtr]][0]);
+            Int32* s_code_sel_selCtr
+               = &(s->code[s->selector[selCtr]][0]);
+
+#           define BZ_ITAH(nn)                      \
+               mtfv_i = mtfv[gs+(nn)];              \
+               bsW ( s,                             \
+                     s_len_sel_selCtr[mtfv_i],      \
+                     s_code_sel_selCtr[mtfv_i] )
+
+            BZ_ITAH(0);  BZ_ITAH(1);  BZ_ITAH(2);  BZ_ITAH(3);  BZ_ITAH(4);
+            BZ_ITAH(5);  BZ_ITAH(6);  BZ_ITAH(7);  BZ_ITAH(8);  BZ_ITAH(9);
+            BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14);
+            BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19);
+            BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24);
+            BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29);
+            BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34);
+            BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39);
+            BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44);
+            BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49);
+
+#           undef BZ_ITAH
+
+      } else {
+	 /*--- slow version which correctly handles all situations ---*/
+         for (i = gs; i <= ge; i++) {
+            bsW ( s, 
+                  s->len  [s->selector[selCtr]] [mtfv[i]],
+                  s->code [s->selector[selCtr]] [mtfv[i]] );
+         }
+      }
+
+
+      gs = ge+1;
+      selCtr++;
+   }
+   AssertH( selCtr == nSelectors, 3007 );
+
+   if (s->verbosity >= 3)
+      VPrintf1( "codes %d\n", s->numZ-nBytes );
+}
+
+
+/*---------------------------------------------------*/
+void BZ2_compressBlock ( EState* s, Bool is_last_block )
+{
+   if (s->nblock > 0) {
+
+      BZ_FINALISE_CRC ( s->blockCRC );
+      s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31);
+      s->combinedCRC ^= s->blockCRC;
+      if (s->blockNo > 1) s->numZ = 0;
+
+      if (s->verbosity >= 2)
+         VPrintf4( "    block %d: crc = 0x%8x, "
+                   "combined CRC = 0x%8x, size = %d\n",
+                   s->blockNo, s->blockCRC, s->combinedCRC, s->nblock );
+
+      BZ2_blockSort ( s );
+   }
+
+   s->zbits = (UChar*) (&((UChar*)s->arr2)[s->nblock]);
+
+   /*-- If this is the first block, create the stream header. --*/
+   if (s->blockNo == 1) {
+      BZ2_bsInitWrite ( s );
+      bsPutUChar ( s, BZ_HDR_B );
+      bsPutUChar ( s, BZ_HDR_Z );
+      bsPutUChar ( s, BZ_HDR_h );
+      bsPutUChar ( s, (UChar)(BZ_HDR_0 + s->blockSize100k) );
+   }
+
+   if (s->nblock > 0) {
+
+      bsPutUChar ( s, 0x31 ); bsPutUChar ( s, 0x41 );
+      bsPutUChar ( s, 0x59 ); bsPutUChar ( s, 0x26 );
+      bsPutUChar ( s, 0x53 ); bsPutUChar ( s, 0x59 );
+
+      /*-- Now the block's CRC, so it is in a known place. --*/
+      bsPutUInt32 ( s, s->blockCRC );
+
+      /*-- 
+         Now a single bit indicating (non-)randomisation. 
+         As of version 0.9.5, we use a better sorting algorithm
+         which makes randomisation unnecessary.  So always set
+         the randomised bit to 'no'.  Of course, the decoder
+         still needs to be able to handle randomised blocks
+         so as to maintain backwards compatibility with
+         older versions of bzip2.
+      --*/
+      bsW(s,1,0);
+
+      bsW ( s, 24, s->origPtr );
+      generateMTFValues ( s );
+      sendMTFValues ( s );
+   }
+
+
+   /*-- If this is the last block, add the stream trailer. --*/
+   if (is_last_block) {
+
+      bsPutUChar ( s, 0x17 ); bsPutUChar ( s, 0x72 );
+      bsPutUChar ( s, 0x45 ); bsPutUChar ( s, 0x38 );
+      bsPutUChar ( s, 0x50 ); bsPutUChar ( s, 0x90 );
+      bsPutUInt32 ( s, s->combinedCRC );
+      if (s->verbosity >= 2)
+         VPrintf1( "    final combined CRC = 0x%x\n   ", s->combinedCRC );
+      bsFinishWrite ( s );
+   }
+}
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                        compress.c ---*/
+/*-------------------------------------------------------------*/
+
+/*-------------------------------------------------------------*/
+/*--- Decompression machinery                               ---*/
+/*---                                          decompress.c ---*/
+/*-------------------------------------------------------------*/
+
+
+/*---------------------------------------------------*/
+static
+void makeMaps_d ( DState* s )
+{
+   Int32 i;
+   s->nInUse = 0;
+   for (i = 0; i < 256; i++)
+      if (s->inUse[i]) {
+         s->seqToUnseq[s->nInUse] = i;
+         s->nInUse++;
+      }
+}
+
+
+/*---------------------------------------------------*/
+#define RETURN(rrr)                               \
+   { retVal = rrr; goto save_state_and_return; };
+
+#define GET_BITS(lll,vvv,nnn)                     \
+   case lll: s->state = lll;                      \
+   while (True) {                                 \
+      if (s->bsLive >= nnn) {                     \
+         UInt32 v;                                \
+         v = (s->bsBuff >>                        \
+             (s->bsLive-nnn)) & ((1 << nnn)-1);   \
+         s->bsLive -= nnn;                        \
+         vvv = v;                                 \
+         break;                                   \
+      }                                           \
+      if (s->strm->avail_in == 0) RETURN(BZ_OK);  \
+      s->bsBuff                                   \
+         = (s->bsBuff << 8) |                     \
+           ((UInt32)                              \
+              (*((UChar*)(s->strm->next_in))));   \
+      s->bsLive += 8;                             \
+      s->strm->next_in++;                         \
+      s->strm->avail_in--;                        \
+      s->strm->total_in_lo32++;                   \
+      if (s->strm->total_in_lo32 == 0)            \
+         s->strm->total_in_hi32++;                \
+   }
+
+#define GET_UCHAR(lll,uuu)                        \
+   GET_BITS(lll,uuu,8)
+
+#define GET_BIT(lll,uuu)                          \
+   GET_BITS(lll,uuu,1)
+
+/*---------------------------------------------------*/
+#define GET_MTF_VAL(label1,label2,lval)           \
+{                                                 \
+   if (groupPos == 0) {                           \
+      groupNo++;                                  \
+      if (groupNo >= nSelectors)                  \
+         RETURN(BZ_DATA_ERROR);                   \
+      groupPos = BZ_G_SIZE;                       \
+      gSel = s->selector[groupNo];                \
+      gMinlen = s->minLens[gSel];                 \
+      gLimit = &(s->limit[gSel][0]);              \
+      gPerm = &(s->perm[gSel][0]);                \
+      gBase = &(s->base[gSel][0]);                \
+   }                                              \
+   groupPos--;                                    \
+   zn = gMinlen;                                  \
+   GET_BITS(label1, zvec, zn);                    \
+   while (1) {                                    \
+      if (zn > 20 /* the longest code */)         \
+         RETURN(BZ_DATA_ERROR);                   \
+      if (zvec <= gLimit[zn]) break;              \
+      zn++;                                       \
+      GET_BIT(label2, zj);                        \
+      zvec = (zvec << 1) | zj;                    \
+   };                                             \
+   if (zvec - gBase[zn] < 0                       \
+       || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE)  \
+      RETURN(BZ_DATA_ERROR);                      \
+   lval = gPerm[zvec - gBase[zn]];                \
+}
+
+
+/*---------------------------------------------------*/
+Int32 BZ2_decompress ( DState* s )
+{
+   UChar      uc;
+   Int32      retVal;
+   Int32      minLen, maxLen;
+   bz_stream* strm = s->strm;
+
+   /* stuff that needs to be saved/restored */
+   Int32  i;
+   Int32  j;
+   Int32  t;
+   Int32  alphaSize;
+   Int32  nGroups;
+   Int32  nSelectors;
+   Int32  EOB;
+   Int32  groupNo;
+   Int32  groupPos;
+   Int32  nextSym;
+   Int32  nblockMAX;
+   Int32  nblock;
+   Int32  es;
+   Int32  N;
+   Int32  curr;
+   Int32  zt;
+   Int32  zn; 
+   Int32  zvec;
+   Int32  zj;
+   Int32  gSel;
+   Int32  gMinlen;
+   Int32* gLimit;
+   Int32* gBase;
+   Int32* gPerm;
+
+   if (s->state == BZ_X_MAGIC_1) {
+      /*initialise the save area*/
+      s->save_i           = 0;
+      s->save_j           = 0;
+      s->save_t           = 0;
+      s->save_alphaSize   = 0;
+      s->save_nGroups     = 0;
+      s->save_nSelectors  = 0;
+      s->save_EOB         = 0;
+      s->save_groupNo     = 0;
+      s->save_groupPos    = 0;
+      s->save_nextSym     = 0;
+      s->save_nblockMAX   = 0;
+      s->save_nblock      = 0;
+      s->save_es          = 0;
+      s->save_N           = 0;
+      s->save_curr        = 0;
+      s->save_zt          = 0;
+      s->save_zn          = 0;
+      s->save_zvec        = 0;
+      s->save_zj          = 0;
+      s->save_gSel        = 0;
+      s->save_gMinlen     = 0;
+      s->save_gLimit      = NULL;
+      s->save_gBase       = NULL;
+      s->save_gPerm       = NULL;
+   }
+
+   /*restore from the save area*/
+   i           = s->save_i;
+   j           = s->save_j;
+   t           = s->save_t;
+   alphaSize   = s->save_alphaSize;
+   nGroups     = s->save_nGroups;
+   nSelectors  = s->save_nSelectors;
+   EOB         = s->save_EOB;
+   groupNo     = s->save_groupNo;
+   groupPos    = s->save_groupPos;
+   nextSym     = s->save_nextSym;
+   nblockMAX   = s->save_nblockMAX;
+   nblock      = s->save_nblock;
+   es          = s->save_es;
+   N           = s->save_N;
+   curr        = s->save_curr;
+   zt          = s->save_zt;
+   zn          = s->save_zn; 
+   zvec        = s->save_zvec;
+   zj          = s->save_zj;
+   gSel        = s->save_gSel;
+   gMinlen     = s->save_gMinlen;
+   gLimit      = s->save_gLimit;
+   gBase       = s->save_gBase;
+   gPerm       = s->save_gPerm;
+
+   retVal = BZ_OK;
+
+   switch (s->state) {
+
+      GET_UCHAR(BZ_X_MAGIC_1, uc);
+      if (uc != BZ_HDR_B) RETURN(BZ_DATA_ERROR_MAGIC);
+
+      GET_UCHAR(BZ_X_MAGIC_2, uc);
+      if (uc != BZ_HDR_Z) RETURN(BZ_DATA_ERROR_MAGIC);
+
+      GET_UCHAR(BZ_X_MAGIC_3, uc)
+      if (uc != BZ_HDR_h) RETURN(BZ_DATA_ERROR_MAGIC);
+
+      GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8)
+      if (s->blockSize100k < (BZ_HDR_0 + 1) || 
+          s->blockSize100k > (BZ_HDR_0 + 9)) RETURN(BZ_DATA_ERROR_MAGIC);
+      s->blockSize100k -= BZ_HDR_0;
+
+      if (s->smallDecompress) {
+         s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) );
+         s->ll4  = BZALLOC( 
+                      ((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar) 
+                   );
+         if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR);
+      } else {
+         s->tt  = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) );
+         if (s->tt == NULL) RETURN(BZ_MEM_ERROR);
+      }
+
+      GET_UCHAR(BZ_X_BLKHDR_1, uc);
+
+      if (uc == 0x17) goto endhdr_2;
+      if (uc != 0x31) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_BLKHDR_2, uc);
+      if (uc != 0x41) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_BLKHDR_3, uc);
+      if (uc != 0x59) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_BLKHDR_4, uc);
+      if (uc != 0x26) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_BLKHDR_5, uc);
+      if (uc != 0x53) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_BLKHDR_6, uc);
+      if (uc != 0x59) RETURN(BZ_DATA_ERROR);
+
+      s->currBlockNo++;
+      if (s->verbosity >= 2)
+         VPrintf1 ( "\n    [%d: huff+mtf ", s->currBlockNo );
+ 
+      s->storedBlockCRC = 0;
+      GET_UCHAR(BZ_X_BCRC_1, uc);
+      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
+      GET_UCHAR(BZ_X_BCRC_2, uc);
+      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
+      GET_UCHAR(BZ_X_BCRC_3, uc);
+      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
+      GET_UCHAR(BZ_X_BCRC_4, uc);
+      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
+
+      GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1);
+
+      s->origPtr = 0;
+      GET_UCHAR(BZ_X_ORIGPTR_1, uc);
+      s->origPtr = (s->origPtr << 8) | ((Int32)uc);
+      GET_UCHAR(BZ_X_ORIGPTR_2, uc);
+      s->origPtr = (s->origPtr << 8) | ((Int32)uc);
+      GET_UCHAR(BZ_X_ORIGPTR_3, uc);
+      s->origPtr = (s->origPtr << 8) | ((Int32)uc);
+
+      if (s->origPtr < 0)
+         RETURN(BZ_DATA_ERROR);
+      if (s->origPtr > 10 + 100000*s->blockSize100k) 
+         RETURN(BZ_DATA_ERROR);
+
+      /*--- Receive the mapping table ---*/
+      for (i = 0; i < 16; i++) {
+         GET_BIT(BZ_X_MAPPING_1, uc);
+         if (uc == 1) 
+            s->inUse16[i] = True; else 
+            s->inUse16[i] = False;
+      }
+
+      for (i = 0; i < 256; i++) s->inUse[i] = False;
+
+      for (i = 0; i < 16; i++)
+         if (s->inUse16[i])
+            for (j = 0; j < 16; j++) {
+               GET_BIT(BZ_X_MAPPING_2, uc);
+               if (uc == 1) s->inUse[i * 16 + j] = True;
+            }
+      makeMaps_d ( s );
+      if (s->nInUse == 0) RETURN(BZ_DATA_ERROR);
+      alphaSize = s->nInUse+2;
+
+      /*--- Now the selectors ---*/
+      GET_BITS(BZ_X_SELECTOR_1, nGroups, 3);
+      if (nGroups < 2 || nGroups > 6) RETURN(BZ_DATA_ERROR);
+      GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15);
+      if (nSelectors < 1) RETURN(BZ_DATA_ERROR);
+      for (i = 0; i < nSelectors; i++) {
+         j = 0;
+         while (True) {
+            GET_BIT(BZ_X_SELECTOR_3, uc);
+            if (uc == 0) break;
+            j++;
+            if (j >= nGroups) RETURN(BZ_DATA_ERROR);
+         }
+         s->selectorMtf[i] = j;
+      }
+
+      /*--- Undo the MTF values for the selectors. ---*/
+      {
+         UChar pos[BZ_N_GROUPS], tmp, v;
+         for (v = 0; v < nGroups; v++) pos[v] = v;
+   
+         for (i = 0; i < nSelectors; i++) {
+            v = s->selectorMtf[i];
+            tmp = pos[v];
+            while (v > 0) { pos[v] = pos[v-1]; v--; }
+            pos[0] = tmp;
+            s->selector[i] = tmp;
+         }
+      }
+
+      /*--- Now the coding tables ---*/
+      for (t = 0; t < nGroups; t++) {
+         GET_BITS(BZ_X_CODING_1, curr, 5);
+         for (i = 0; i < alphaSize; i++) {
+            while (True) {
+               if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR);
+               GET_BIT(BZ_X_CODING_2, uc);
+               if (uc == 0) break;
+               GET_BIT(BZ_X_CODING_3, uc);
+               if (uc == 0) curr++; else curr--;
+            }
+            s->len[t][i] = curr;
+         }
+      }
+
+      /*--- Create the Huffman decoding tables ---*/
+      for (t = 0; t < nGroups; t++) {
+         minLen = 32;
+         maxLen = 0;
+         for (i = 0; i < alphaSize; i++) {
+            if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
+            if (s->len[t][i] < minLen) minLen = s->len[t][i];
+         }
+         BZ2_hbCreateDecodeTables ( 
+            &(s->limit[t][0]), 
+            &(s->base[t][0]), 
+            &(s->perm[t][0]), 
+            &(s->len[t][0]),
+            minLen, maxLen, alphaSize
+         );
+         s->minLens[t] = minLen;
+      }
+
+      /*--- Now the MTF values ---*/
+
+      EOB      = s->nInUse+1;
+      nblockMAX = 100000 * s->blockSize100k;
+      groupNo  = -1;
+      groupPos = 0;
+
+      for (i = 0; i <= 255; i++) s->unzftab[i] = 0;
+
+      /*-- MTF init --*/
+      {
+         Int32 ii, jj, kk;
+         kk = MTFA_SIZE-1;
+         for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) {
+            for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
+               s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj);
+               kk--;
+            }
+            s->mtfbase[ii] = kk + 1;
+         }
+      }
+      /*-- end MTF init --*/
+
+      nblock = 0;
+      GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym);
+
+      while (True) {
+
+         if (nextSym == EOB) break;
+
+         if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) {
+
+            es = -1;
+            N = 1;
+            do {
+               if (nextSym == BZ_RUNA) es = es + (0+1) * N; else
+               if (nextSym == BZ_RUNB) es = es + (1+1) * N;
+               N = N * 2;
+               GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym);
+            }
+               while (nextSym == BZ_RUNA || nextSym == BZ_RUNB);
+
+            es++;
+            uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ];
+            s->unzftab[uc] += es;
+
+            if (s->smallDecompress)
+               while (es > 0) {
+                  if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
+                  s->ll16[nblock] = (UInt16)uc;
+                  nblock++;
+                  es--;
+               }
+            else
+               while (es > 0) {
+                  if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
+                  s->tt[nblock] = (UInt32)uc;
+                  nblock++;
+                  es--;
+               };
+
+            continue;
+
+         } else {
+
+            if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
+
+            /*-- uc = MTF ( nextSym-1 ) --*/
+            {
+               Int32 ii, jj, kk, pp, lno, off;
+               UInt32 nn;
+               nn = (UInt32)(nextSym - 1);
+
+               if (nn < MTFL_SIZE) {
+                  /* avoid general-case expense */
+                  pp = s->mtfbase[0];
+                  uc = s->mtfa[pp+nn];
+                  while (nn > 3) {
+                     Int32 z = pp+nn;
+                     s->mtfa[(z)  ] = s->mtfa[(z)-1];
+                     s->mtfa[(z)-1] = s->mtfa[(z)-2];
+                     s->mtfa[(z)-2] = s->mtfa[(z)-3];
+                     s->mtfa[(z)-3] = s->mtfa[(z)-4];
+                     nn -= 4;
+                  }
+                  while (nn > 0) { 
+                     s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--; 
+                  };
+                  s->mtfa[pp] = uc;
+               } else { 
+                  /* general case */
+                  lno = nn / MTFL_SIZE;
+                  off = nn % MTFL_SIZE;
+                  pp = s->mtfbase[lno] + off;
+                  uc = s->mtfa[pp];
+                  while (pp > s->mtfbase[lno]) { 
+                     s->mtfa[pp] = s->mtfa[pp-1]; pp--; 
+                  };
+                  s->mtfbase[lno]++;
+                  while (lno > 0) {
+                     s->mtfbase[lno]--;
+                     s->mtfa[s->mtfbase[lno]] 
+                        = s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1];
+                     lno--;
+                  }
+                  s->mtfbase[0]--;
+                  s->mtfa[s->mtfbase[0]] = uc;
+                  if (s->mtfbase[0] == 0) {
+                     kk = MTFA_SIZE-1;
+                     for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) {
+                        for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
+                           s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj];
+                           kk--;
+                        }
+                        s->mtfbase[ii] = kk + 1;
+                     }
+                  }
+               }
+            }
+            /*-- end uc = MTF ( nextSym-1 ) --*/
+
+            s->unzftab[s->seqToUnseq[uc]]++;
+            if (s->smallDecompress)
+               s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else
+               s->tt[nblock]   = (UInt32)(s->seqToUnseq[uc]);
+            nblock++;
+
+            GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym);
+            continue;
+         }
+      }
+
+      /* Now we know what nblock is, we can do a better sanity
+         check on s->origPtr.
+      */
+      if (s->origPtr < 0 || s->origPtr >= nblock)
+         RETURN(BZ_DATA_ERROR);
+
+      s->state_out_len = 0;
+      s->state_out_ch  = 0;
+      BZ_INITIALISE_CRC ( s->calculatedBlockCRC );
+      s->state = BZ_X_OUTPUT;
+      if (s->verbosity >= 2) VPrintf0 ( "rt+rld" );
+
+      /*-- Set up cftab to facilitate generation of T^(-1) --*/
+      s->cftab[0] = 0;
+      for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1];
+      for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1];
+
+      if (s->smallDecompress) {
+
+         /*-- Make a copy of cftab, used in generation of T --*/
+         for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i];
+
+         /*-- compute the T vector --*/
+         for (i = 0; i < nblock; i++) {
+            uc = (UChar)(s->ll16[i]);
+            SET_LL(i, s->cftabCopy[uc]);
+            s->cftabCopy[uc]++;
+         }
+
+         /*-- Compute T^(-1) by pointer reversal on T --*/
+         i = s->origPtr;
+         j = GET_LL(i);
+         do {
+            Int32 tmp = GET_LL(j);
+            SET_LL(j, i);
+            i = j;
+            j = tmp;
+         }
+            while (i != s->origPtr);
+
+         s->tPos = s->origPtr;
+         s->nblock_used = 0;
+         if (s->blockRandomised) {
+            BZ_RAND_INIT_MASK;
+            BZ_GET_SMALL(s->k0); s->nblock_used++;
+            BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; 
+         } else {
+            BZ_GET_SMALL(s->k0); s->nblock_used++;
+         }
+
+      } else {
+
+         /*-- compute the T^(-1) vector --*/
+         for (i = 0; i < nblock; i++) {
+            uc = (UChar)(s->tt[i] & 0xff);
+            s->tt[s->cftab[uc]] |= (i << 8);
+            s->cftab[uc]++;
+         }
+
+         s->tPos = s->tt[s->origPtr] >> 8;
+         s->nblock_used = 0;
+         if (s->blockRandomised) {
+            BZ_RAND_INIT_MASK;
+            BZ_GET_FAST(s->k0); s->nblock_used++;
+            BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; 
+         } else {
+            BZ_GET_FAST(s->k0); s->nblock_used++;
+         }
+
+      }
+
+      RETURN(BZ_OK);
+
+
+
+    endhdr_2:
+
+      GET_UCHAR(BZ_X_ENDHDR_2, uc);
+      if (uc != 0x72) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_ENDHDR_3, uc);
+      if (uc != 0x45) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_ENDHDR_4, uc);
+      if (uc != 0x38) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_ENDHDR_5, uc);
+      if (uc != 0x50) RETURN(BZ_DATA_ERROR);
+      GET_UCHAR(BZ_X_ENDHDR_6, uc);
+      if (uc != 0x90) RETURN(BZ_DATA_ERROR);
+
+      s->storedCombinedCRC = 0;
+      GET_UCHAR(BZ_X_CCRC_1, uc);
+      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
+      GET_UCHAR(BZ_X_CCRC_2, uc);
+      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
+      GET_UCHAR(BZ_X_CCRC_3, uc);
+      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
+      GET_UCHAR(BZ_X_CCRC_4, uc);
+      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
+
+      s->state = BZ_X_IDLE;
+      RETURN(BZ_STREAM_END);
+
+      default: AssertH ( False, 4001 );
+   }
+
+   AssertH ( False, 4002 );
+
+   save_state_and_return:
+
+   s->save_i           = i;
+   s->save_j           = j;
+   s->save_t           = t;
+   s->save_alphaSize   = alphaSize;
+   s->save_nGroups     = nGroups;
+   s->save_nSelectors  = nSelectors;
+   s->save_EOB         = EOB;
+   s->save_groupNo     = groupNo;
+   s->save_groupPos    = groupPos;
+   s->save_nextSym     = nextSym;
+   s->save_nblockMAX   = nblockMAX;
+   s->save_nblock      = nblock;
+   s->save_es          = es;
+   s->save_N           = N;
+   s->save_curr        = curr;
+   s->save_zt          = zt;
+   s->save_zn          = zn;
+   s->save_zvec        = zvec;
+   s->save_zj          = zj;
+   s->save_gSel        = gSel;
+   s->save_gMinlen     = gMinlen;
+   s->save_gLimit      = gLimit;
+   s->save_gBase       = gBase;
+   s->save_gPerm       = gPerm;
+
+   return retVal;   
+}
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                      decompress.c ---*/
+/*-------------------------------------------------------------*/
+
+/*-------------------------------------------------------------*/
+/*--- Library top-level functions.                          ---*/
+/*---                                               bzlib.c ---*/
+/*-------------------------------------------------------------*/
+
+/*---------------------------------------------------*/
+/*--- Compression stuff                           ---*/
+/*---------------------------------------------------*/
+
+
+/*---------------------------------------------------*/
+#ifndef BZ_NO_STDIO
+void BZ2_bz__AssertH__fail ( int errcode )
+{
+   fprintf(stderr, 
+      "\n\nbzip2/libbzip2: internal error number %d.\n"
+      "This is a bug in bzip2/libbzip2, %s.\n"
+      "Please report it to me at: jseward@acm.org.  If this happened\n"
+      "when you were using some program which uses libbzip2 as a\n"
+      "component, you should also report this bug to the author(s)\n"
+      "of that program.  Please make an effort to report this bug;\n"
+      "timely and accurate bug reports eventually lead to higher\n"
+      "quality software.  Thanks.  Julian Seward, 30 December 2001.\n\n",
+      errcode,
+      BZ2_bzlibVersion()
+   );
+
+   if (errcode == 1007) {
+   fprintf(stderr,
+      "\n*** A special note about internal error number 1007 ***\n"
+      "\n"
+      "Experience suggests that a common cause of i.e. 1007\n"
+      "is unreliable memory or other hardware.  The 1007 assertion\n"
+      "just happens to cross-check the results of huge numbers of\n"
+      "memory reads/writes, and so acts (unintendedly) as a stress\n"
+      "test of your memory system.\n"
+      "\n"
+      "I suggest the following: try compressing the file again,\n"
+      "possibly monitoring progress in detail with the -vv flag.\n"
+      "\n"
+      "* If the error cannot be reproduced, and/or happens at different\n"
+      "  points in compression, you may have a flaky memory system.\n"
+      "  Try a memory-test program.  I have used Memtest86\n"
+      "  (www.memtest86.com).  At the time of writing it is free (GPLd).\n"
+      "  Memtest86 tests memory much more thorougly than your BIOSs\n"
+      "  power-on test, and may find failures that the BIOS doesn't.\n"
+      "\n"
+      "* If the error can be repeatably reproduced, this is a bug in\n"
+      "  bzip2, and I would very much like to hear about it.  Please\n"
+      "  let me know, and, ideally, save a copy of the file causing the\n"
+      "  problem -- without which I will be unable to investigate it.\n"
+      "\n"
+   );
+   }
+
+   exit(3);
+}
+#endif
+
+
+/*---------------------------------------------------*/
+static
+int bz_config_ok ( void )
+{
+   if (sizeof(int)   != 4) return 0;
+   if (sizeof(short) != 2) return 0;
+   if (sizeof(char)  != 1) return 0;
+   return 1;
+}
+
+
+/*---------------------------------------------------*/
+static
+void* default_bzalloc ( void* opaque, Int32 items, Int32 size )
+{
+   void* v = malloc ( items * size );
+   return v;
+}
+
+static
+void default_bzfree ( void* opaque, void* addr )
+{
+   if (addr != NULL) free ( addr );
+}
+
+
+/*---------------------------------------------------*/
+static
+void prepare_new_block ( EState* s )
+{
+   Int32 i;
+   s->nblock = 0;
+   s->numZ = 0;
+   s->state_out_pos = 0;
+   BZ_INITIALISE_CRC ( s->blockCRC );
+   for (i = 0; i < 256; i++) s->inUse[i] = False;
+   s->blockNo++;
+}
+
+
+/*---------------------------------------------------*/
+static
+void init_RL ( EState* s )
+{
+   s->state_in_ch  = 256;
+   s->state_in_len = 0;
+}
+
+
+static
+Bool isempty_RL ( EState* s )
+{
+   if (s->state_in_ch < 256 && s->state_in_len > 0)
+      return False; else
+      return True;
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzCompressInit) 
+                    ( bz_stream* strm, 
+                     int        blockSize100k,
+                     int        verbosity,
+                     int        workFactor )
+{
+   Int32   n;
+   EState* s;
+
+   if (!bz_config_ok()) return BZ_CONFIG_ERROR;
+
+   if (strm == NULL || 
+       blockSize100k < 1 || blockSize100k > 9 ||
+       workFactor < 0 || workFactor > 250)
+     return BZ_PARAM_ERROR;
+
+   if (workFactor == 0) workFactor = 30;
+   if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc;
+   if (strm->bzfree == NULL) strm->bzfree = default_bzfree;
+
+   s = BZALLOC( sizeof(EState) );
+   if (s == NULL) return BZ_MEM_ERROR;
+   s->strm = strm;
+
+   s->arr1 = NULL;
+   s->arr2 = NULL;
+   s->ftab = NULL;
+
+   n       = 100000 * blockSize100k;
+   s->arr1 = BZALLOC( n                  * sizeof(UInt32) );
+   s->arr2 = BZALLOC( (n+BZ_N_OVERSHOOT) * sizeof(UInt32) );
+   s->ftab = BZALLOC( 65537              * sizeof(UInt32) );
+
+   if (s->arr1 == NULL || s->arr2 == NULL || s->ftab == NULL) {
+      if (s->arr1 != NULL) BZFREE(s->arr1);
+      if (s->arr2 != NULL) BZFREE(s->arr2);
+      if (s->ftab != NULL) BZFREE(s->ftab);
+      if (s       != NULL) BZFREE(s);
+      return BZ_MEM_ERROR;
+   }
+
+   s->blockNo           = 0;
+   s->state             = BZ_S_INPUT;
+   s->mode              = BZ_M_RUNNING;
+   s->combinedCRC       = 0;
+   s->blockSize100k     = blockSize100k;
+   s->nblockMAX         = 100000 * blockSize100k - 19;
+   s->verbosity         = verbosity;
+   s->workFactor        = workFactor;
+
+   s->block             = (UChar*)s->arr2;
+   s->mtfv              = (UInt16*)s->arr1;
+   s->zbits             = NULL;
+   s->ptr               = (UInt32*)s->arr1;
+
+   strm->state          = s;
+   strm->total_in_lo32  = 0;
+   strm->total_in_hi32  = 0;
+   strm->total_out_lo32 = 0;
+   strm->total_out_hi32 = 0;
+   init_RL ( s );
+   prepare_new_block ( s );
+   return BZ_OK;
+}
+
+
+/*---------------------------------------------------*/
+static
+void add_pair_to_block ( EState* s )
+{
+   Int32 i;
+   UChar ch = (UChar)(s->state_in_ch);
+   for (i = 0; i < s->state_in_len; i++) {
+      BZ_UPDATE_CRC( s->blockCRC, ch );
+   }
+   s->inUse[s->state_in_ch] = True;
+   switch (s->state_in_len) {
+      case 1:
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         break;
+      case 2:
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         break;
+      case 3:
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         break;
+      default:
+         s->inUse[s->state_in_len-4] = True;
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         s->block[s->nblock] = (UChar)ch; s->nblock++;
+         s->block[s->nblock] = ((UChar)(s->state_in_len-4));
+         s->nblock++;
+         break;
+   }
+}
+
+
+/*---------------------------------------------------*/
+static
+void flush_RL ( EState* s )
+{
+   if (s->state_in_ch < 256) add_pair_to_block ( s );
+   init_RL ( s );
+}
+
+
+/*---------------------------------------------------*/
+#define ADD_CHAR_TO_BLOCK(zs,zchh0)               \
+{                                                 \
+   UInt32 zchh = (UInt32)(zchh0);                 \
+   /*-- fast track the common case --*/           \
+   if (zchh != zs->state_in_ch &&                 \
+       zs->state_in_len == 1) {                   \
+      UChar ch = (UChar)(zs->state_in_ch);        \
+      BZ_UPDATE_CRC( zs->blockCRC, ch );          \
+      zs->inUse[zs->state_in_ch] = True;          \
+      zs->block[zs->nblock] = (UChar)ch;          \
+      zs->nblock++;                               \
+      zs->state_in_ch = zchh;                     \
+   }                                              \
+   else                                           \
+   /*-- general, uncommon cases --*/              \
+   if (zchh != zs->state_in_ch ||                 \
+      zs->state_in_len == 255) {                  \
+      if (zs->state_in_ch < 256)                  \
+         add_pair_to_block ( zs );                \
+      zs->state_in_ch = zchh;                     \
+      zs->state_in_len = 1;                       \
+   } else {                                       \
+      zs->state_in_len++;                         \
+   }                                              \
+}
+
+
+/*---------------------------------------------------*/
+static
+Bool copy_input_until_stop ( EState* s )
+{
+   Bool progress_in = False;
+
+   if (s->mode == BZ_M_RUNNING) {
+
+      /*-- fast track the common case --*/
+      while (True) {
+         /*-- block full? --*/
+         if (s->nblock >= s->nblockMAX) break;
+         /*-- no input? --*/
+         if (s->strm->avail_in == 0) break;
+         progress_in = True;
+         ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); 
+         s->strm->next_in++;
+         s->strm->avail_in--;
+         s->strm->total_in_lo32++;
+         if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++;
+      }
+
+   } else {
+
+      /*-- general, uncommon case --*/
+      while (True) {
+         /*-- block full? --*/
+         if (s->nblock >= s->nblockMAX) break;
+         /*-- no input? --*/
+         if (s->strm->avail_in == 0) break;
+         /*-- flush/finish end? --*/
+         if (s->avail_in_expect == 0) break;
+         progress_in = True;
+         ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); 
+         s->strm->next_in++;
+         s->strm->avail_in--;
+         s->strm->total_in_lo32++;
+         if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++;
+         s->avail_in_expect--;
+      }
+   }
+   return progress_in;
+}
+
+
+/*---------------------------------------------------*/
+static
+Bool copy_output_until_stop ( EState* s )
+{
+   Bool progress_out = False;
+
+   while (True) {
+
+      /*-- no output space? --*/
+      if (s->strm->avail_out == 0) break;
+
+      /*-- block done? --*/
+      if (s->state_out_pos >= s->numZ) break;
+
+      progress_out = True;
+      *(s->strm->next_out) = s->zbits[s->state_out_pos];
+      s->state_out_pos++;
+      s->strm->avail_out--;
+      s->strm->next_out++;
+      s->strm->total_out_lo32++;
+      if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
+   }
+
+   return progress_out;
+}
+
+
+/*---------------------------------------------------*/
+static
+Bool handle_compress ( bz_stream* strm )
+{
+   Bool progress_in  = False;
+   Bool progress_out = False;
+   EState* s = strm->state;
+   
+   while (True) {
+
+      if (s->state == BZ_S_OUTPUT) {
+         progress_out |= copy_output_until_stop ( s );
+         if (s->state_out_pos < s->numZ) break;
+         if (s->mode == BZ_M_FINISHING && 
+             s->avail_in_expect == 0 &&
+             isempty_RL(s)) break;
+         prepare_new_block ( s );
+         s->state = BZ_S_INPUT;
+         if (s->mode == BZ_M_FLUSHING && 
+             s->avail_in_expect == 0 &&
+             isempty_RL(s)) break;
+      }
+
+      if (s->state == BZ_S_INPUT) {
+         progress_in |= copy_input_until_stop ( s );
+         if (s->mode != BZ_M_RUNNING && s->avail_in_expect == 0) {
+            flush_RL ( s );
+            BZ2_compressBlock ( s, (Bool)(s->mode == BZ_M_FINISHING) );
+            s->state = BZ_S_OUTPUT;
+         }
+         else
+         if (s->nblock >= s->nblockMAX) {
+            BZ2_compressBlock ( s, False );
+            s->state = BZ_S_OUTPUT;
+         }
+         else
+         if (s->strm->avail_in == 0) {
+            break;
+         }
+      }
+
+   }
+
+   return progress_in || progress_out;
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzCompress) ( bz_stream *strm, int action )
+{
+   Bool progress;
+   EState* s;
+   if (strm == NULL) return BZ_PARAM_ERROR;
+   s = strm->state;
+   if (s == NULL) return BZ_PARAM_ERROR;
+   if (s->strm != strm) return BZ_PARAM_ERROR;
+
+   preswitch:
+   switch (s->mode) {
+
+      case BZ_M_IDLE:
+         return BZ_SEQUENCE_ERROR;
+
+      case BZ_M_RUNNING:
+         if (action == BZ_RUN) {
+            progress = handle_compress ( strm );
+            return progress ? BZ_RUN_OK : BZ_PARAM_ERROR;
+         } 
+         else
+	 if (action == BZ_FLUSH) {
+            s->avail_in_expect = strm->avail_in;
+            s->mode = BZ_M_FLUSHING;
+            goto preswitch;
+         }
+         else
+         if (action == BZ_FINISH) {
+            s->avail_in_expect = strm->avail_in;
+            s->mode = BZ_M_FINISHING;
+            goto preswitch;
+         }
+         else 
+            return BZ_PARAM_ERROR;
+
+      case BZ_M_FLUSHING:
+         if (action != BZ_FLUSH) return BZ_SEQUENCE_ERROR;
+         if (s->avail_in_expect != s->strm->avail_in) 
+            return BZ_SEQUENCE_ERROR;
+         progress = handle_compress ( strm );
+         if (s->avail_in_expect > 0 || !isempty_RL(s) ||
+             s->state_out_pos < s->numZ) return BZ_FLUSH_OK;
+         s->mode = BZ_M_RUNNING;
+         return BZ_RUN_OK;
+
+      case BZ_M_FINISHING:
+         if (action != BZ_FINISH) return BZ_SEQUENCE_ERROR;
+         if (s->avail_in_expect != s->strm->avail_in) 
+            return BZ_SEQUENCE_ERROR;
+         progress = handle_compress ( strm );
+         if (!progress) return BZ_SEQUENCE_ERROR;
+         if (s->avail_in_expect > 0 || !isempty_RL(s) ||
+             s->state_out_pos < s->numZ) return BZ_FINISH_OK;
+         s->mode = BZ_M_IDLE;
+         return BZ_STREAM_END;
+   }
+   return BZ_OK; /*--not reached--*/
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzCompressEnd)  ( bz_stream *strm )
+{
+   EState* s;
+   if (strm == NULL) return BZ_PARAM_ERROR;
+   s = strm->state;
+   if (s == NULL) return BZ_PARAM_ERROR;
+   if (s->strm != strm) return BZ_PARAM_ERROR;
+
+   if (s->arr1 != NULL) BZFREE(s->arr1);
+   if (s->arr2 != NULL) BZFREE(s->arr2);
+   if (s->ftab != NULL) BZFREE(s->ftab);
+   BZFREE(strm->state);
+
+   strm->state = NULL;   
+
+   return BZ_OK;
+}
+
+
+/*---------------------------------------------------*/
+/*--- Decompression stuff                         ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzDecompressInit) 
+                     ( bz_stream* strm, 
+                       int        verbosity,
+                       int        small )
+{
+   DState* s;
+
+   if (!bz_config_ok()) return BZ_CONFIG_ERROR;
+
+   if (strm == NULL) return BZ_PARAM_ERROR;
+   if (small != 0 && small != 1) return BZ_PARAM_ERROR;
+   if (verbosity < 0 || verbosity > 4) return BZ_PARAM_ERROR;
+
+   if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc;
+   if (strm->bzfree == NULL) strm->bzfree = default_bzfree;
+
+   s = BZALLOC( sizeof(DState) );
+   if (s == NULL) return BZ_MEM_ERROR;
+   s->strm                  = strm;
+   strm->state              = s;
+   s->state                 = BZ_X_MAGIC_1;
+   s->bsLive                = 0;
+   s->bsBuff                = 0;
+   s->calculatedCombinedCRC = 0;
+   strm->total_in_lo32      = 0;
+   strm->total_in_hi32      = 0;
+   strm->total_out_lo32     = 0;
+   strm->total_out_hi32     = 0;
+   s->smallDecompress       = (Bool)small;
+   s->ll4                   = NULL;
+   s->ll16                  = NULL;
+   s->tt                    = NULL;
+   s->currBlockNo           = 0;
+   s->verbosity             = verbosity;
+
+   return BZ_OK;
+}
+
+
+/*---------------------------------------------------*/
+static
+void unRLE_obuf_to_output_FAST ( DState* s )
+{
+   UChar k1;
+
+   if (s->blockRandomised) {
+
+      while (True) {
+         /* try to finish existing run */
+         while (True) {
+            if (s->strm->avail_out == 0) return;
+            if (s->state_out_len == 0) break;
+            *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
+            BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
+            s->state_out_len--;
+            s->strm->next_out++;
+            s->strm->avail_out--;
+            s->strm->total_out_lo32++;
+            if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
+         }
+   
+         /* can a new run be started? */
+         if (s->nblock_used == s->save_nblock+1) return;
+               
+   
+         s->state_out_len = 1;
+         s->state_out_ch = s->k0;
+         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; 
+         k1 ^= BZ_RAND_MASK; s->nblock_used++;
+         if (s->nblock_used == s->save_nblock+1) continue;
+         if (k1 != s->k0) { s->k0 = k1; continue; };
+   
+         s->state_out_len = 2;
+         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; 
+         k1 ^= BZ_RAND_MASK; s->nblock_used++;
+         if (s->nblock_used == s->save_nblock+1) continue;
+         if (k1 != s->k0) { s->k0 = k1; continue; };
+   
+         s->state_out_len = 3;
+         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; 
+         k1 ^= BZ_RAND_MASK; s->nblock_used++;
+         if (s->nblock_used == s->save_nblock+1) continue;
+         if (k1 != s->k0) { s->k0 = k1; continue; };
+   
+         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; 
+         k1 ^= BZ_RAND_MASK; s->nblock_used++;
+         s->state_out_len = ((Int32)k1) + 4;
+         BZ_GET_FAST(s->k0); BZ_RAND_UPD_MASK; 
+         s->k0 ^= BZ_RAND_MASK; s->nblock_used++;
+      }
+
+   } else {
+
+      /* restore */
+      UInt32        c_calculatedBlockCRC = s->calculatedBlockCRC;
+      UChar         c_state_out_ch       = s->state_out_ch;
+      Int32         c_state_out_len      = s->state_out_len;
+      Int32         c_nblock_used        = s->nblock_used;
+      Int32         c_k0                 = s->k0;
+      UInt32*       c_tt                 = s->tt;
+      UInt32        c_tPos               = s->tPos;
+      char*         cs_next_out          = s->strm->next_out;
+      unsigned int  cs_avail_out         = s->strm->avail_out;
+      /* end restore */
+
+      UInt32       avail_out_INIT = cs_avail_out;
+      Int32        s_save_nblockPP = s->save_nblock+1;
+      unsigned int total_out_lo32_old;
+
+      while (True) {
+
+         /* try to finish existing run */
+         if (c_state_out_len > 0) {
+            while (True) {
+               if (cs_avail_out == 0) goto return_notr;
+               if (c_state_out_len == 1) break;
+               *( (UChar*)(cs_next_out) ) = c_state_out_ch;
+               BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch );
+               c_state_out_len--;
+               cs_next_out++;
+               cs_avail_out--;
+            }
+            s_state_out_len_eq_one:
+            {
+               if (cs_avail_out == 0) { 
+                  c_state_out_len = 1; goto return_notr;
+               };
+               *( (UChar*)(cs_next_out) ) = c_state_out_ch;
+               BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch );
+               cs_next_out++;
+               cs_avail_out--;
+            }
+         }   
+         /* can a new run be started? */
+         if (c_nblock_used == s_save_nblockPP) {
+            c_state_out_len = 0; goto return_notr;
+         };   
+         c_state_out_ch = c_k0;
+         BZ_GET_FAST_C(k1); c_nblock_used++;
+         if (k1 != c_k0) { 
+            c_k0 = k1; goto s_state_out_len_eq_one; 
+         };
+         if (c_nblock_used == s_save_nblockPP) 
+            goto s_state_out_len_eq_one;
+   
+         c_state_out_len = 2;
+         BZ_GET_FAST_C(k1); c_nblock_used++;
+         if (c_nblock_used == s_save_nblockPP) continue;
+         if (k1 != c_k0) { c_k0 = k1; continue; };
+   
+         c_state_out_len = 3;
+         BZ_GET_FAST_C(k1); c_nblock_used++;
+         if (c_nblock_used == s_save_nblockPP) continue;
+         if (k1 != c_k0) { c_k0 = k1; continue; };
+   
+         BZ_GET_FAST_C(k1); c_nblock_used++;
+         c_state_out_len = ((Int32)k1) + 4;
+         BZ_GET_FAST_C(c_k0); c_nblock_used++;
+      }
+
+      return_notr:
+      total_out_lo32_old = s->strm->total_out_lo32;
+      s->strm->total_out_lo32 += (avail_out_INIT - cs_avail_out);
+      if (s->strm->total_out_lo32 < total_out_lo32_old)
+         s->strm->total_out_hi32++;
+
+      /* save */
+      s->calculatedBlockCRC = c_calculatedBlockCRC;
+      s->state_out_ch       = c_state_out_ch;
+      s->state_out_len      = c_state_out_len;
+      s->nblock_used        = c_nblock_used;
+      s->k0                 = c_k0;
+      s->tt                 = c_tt;
+      s->tPos               = c_tPos;
+      s->strm->next_out     = cs_next_out;
+      s->strm->avail_out    = cs_avail_out;
+      /* end save */
+   }
+}
+
+
+
+/*---------------------------------------------------*/
+Int32 BZ2_indexIntoF ( Int32 indx, Int32 *cftab )
+{
+   Int32 nb, na, mid;
+   nb = 0;
+   na = 256;
+   do {
+      mid = (nb + na) >> 1;
+      if (indx >= cftab[mid]) nb = mid; else na = mid;
+   }
+   while (na - nb != 1);
+   return nb;
+}
+
+
+/*---------------------------------------------------*/
+static
+void unRLE_obuf_to_output_SMALL ( DState* s )
+{
+   UChar k1;
+
+   if (s->blockRandomised) {
+
+      while (True) {
+         /* try to finish existing run */
+         while (True) {
+            if (s->strm->avail_out == 0) return;
+            if (s->state_out_len == 0) break;
+            *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
+            BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
+            s->state_out_len--;
+            s->strm->next_out++;
+            s->strm->avail_out--;
+            s->strm->total_out_lo32++;
+            if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
+         }
+   
+         /* can a new run be started? */
+         if (s->nblock_used == s->save_nblock+1) return;
+               
+   
+         s->state_out_len = 1;
+         s->state_out_ch = s->k0;
+         BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; 
+         k1 ^= BZ_RAND_MASK; s->nblock_used++;
+         if (s->nblock_used == s->save_nblock+1) continue;
+         if (k1 != s->k0) { s->k0 = k1; continue; };
+   
+         s->state_out_len = 2;
+         BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; 
+         k1 ^= BZ_RAND_MASK; s->nblock_used++;
+         if (s->nblock_used == s->save_nblock+1) continue;
+         if (k1 != s->k0) { s->k0 = k1; continue; };
+   
+         s->state_out_len = 3;
+         BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; 
+         k1 ^= BZ_RAND_MASK; s->nblock_used++;
+         if (s->nblock_used == s->save_nblock+1) continue;
+         if (k1 != s->k0) { s->k0 = k1; continue; };
+   
+         BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; 
+         k1 ^= BZ_RAND_MASK; s->nblock_used++;
+         s->state_out_len = ((Int32)k1) + 4;
+         BZ_GET_SMALL(s->k0); BZ_RAND_UPD_MASK; 
+         s->k0 ^= BZ_RAND_MASK; s->nblock_used++;
+      }
+
+   } else {
+
+      while (True) {
+         /* try to finish existing run */
+         while (True) {
+            if (s->strm->avail_out == 0) return;
+            if (s->state_out_len == 0) break;
+            *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
+            BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
+            s->state_out_len--;
+            s->strm->next_out++;
+            s->strm->avail_out--;
+            s->strm->total_out_lo32++;
+            if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
+         }
+   
+         /* can a new run be started? */
+         if (s->nblock_used == s->save_nblock+1) return;
+   
+         s->state_out_len = 1;
+         s->state_out_ch = s->k0;
+         BZ_GET_SMALL(k1); s->nblock_used++;
+         if (s->nblock_used == s->save_nblock+1) continue;
+         if (k1 != s->k0) { s->k0 = k1; continue; };
+   
+         s->state_out_len = 2;
+         BZ_GET_SMALL(k1); s->nblock_used++;
+         if (s->nblock_used == s->save_nblock+1) continue;
+         if (k1 != s->k0) { s->k0 = k1; continue; };
+   
+         s->state_out_len = 3;
+         BZ_GET_SMALL(k1); s->nblock_used++;
+         if (s->nblock_used == s->save_nblock+1) continue;
+         if (k1 != s->k0) { s->k0 = k1; continue; };
+   
+         BZ_GET_SMALL(k1); s->nblock_used++;
+         s->state_out_len = ((Int32)k1) + 4;
+         BZ_GET_SMALL(s->k0); s->nblock_used++;
+      }
+
+   }
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzDecompress) ( bz_stream *strm )
+{
+   DState* s;
+   if (strm == NULL) return BZ_PARAM_ERROR;
+   s = strm->state;
+   if (s == NULL) return BZ_PARAM_ERROR;
+   if (s->strm != strm) return BZ_PARAM_ERROR;
+
+   while (True) {
+      if (s->state == BZ_X_IDLE) return BZ_SEQUENCE_ERROR;
+      if (s->state == BZ_X_OUTPUT) {
+         if (s->smallDecompress)
+            unRLE_obuf_to_output_SMALL ( s ); else
+            unRLE_obuf_to_output_FAST  ( s );
+         if (s->nblock_used == s->save_nblock+1 && s->state_out_len == 0) {
+            BZ_FINALISE_CRC ( s->calculatedBlockCRC );
+            if (s->verbosity >= 3) 
+               VPrintf2 ( " {0x%x, 0x%x}", s->storedBlockCRC, 
+                          s->calculatedBlockCRC );
+            if (s->verbosity >= 2) VPrintf0 ( "]" );
+            if (s->calculatedBlockCRC != s->storedBlockCRC)
+               return BZ_DATA_ERROR;
+            s->calculatedCombinedCRC 
+               = (s->calculatedCombinedCRC << 1) | 
+                    (s->calculatedCombinedCRC >> 31);
+            s->calculatedCombinedCRC ^= s->calculatedBlockCRC;
+            s->state = BZ_X_BLKHDR_1;
+         } else {
+            return BZ_OK;
+         }
+      }
+      if (s->state >= BZ_X_MAGIC_1) {
+         Int32 r = BZ2_decompress ( s );
+         if (r == BZ_STREAM_END) {
+            if (s->verbosity >= 3)
+               VPrintf2 ( "\n    combined CRCs: stored = 0x%x, computed = 0x%x", 
+                          s->storedCombinedCRC, s->calculatedCombinedCRC );
+            if (s->calculatedCombinedCRC != s->storedCombinedCRC)
+               return BZ_DATA_ERROR;
+            return r;
+         }
+         if (s->state != BZ_X_OUTPUT) return r;
+      }
+   }
+
+   AssertH ( 0, 6001 );
+
+   return 0;  /*NOTREACHED*/
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzDecompressEnd)  ( bz_stream *strm )
+{
+   DState* s;
+   if (strm == NULL) return BZ_PARAM_ERROR;
+   s = strm->state;
+   if (s == NULL) return BZ_PARAM_ERROR;
+   if (s->strm != strm) return BZ_PARAM_ERROR;
+
+   if (s->tt   != NULL) BZFREE(s->tt);
+   if (s->ll16 != NULL) BZFREE(s->ll16);
+   if (s->ll4  != NULL) BZFREE(s->ll4);
+
+   BZFREE(strm->state);
+   strm->state = NULL;
+
+   return BZ_OK;
+}
+
+
+#ifndef BZ_NO_STDIO
+/*---------------------------------------------------*/
+/*--- File I/O stuff                              ---*/
+/*---------------------------------------------------*/
+
+#define BZ_SETERR(eee)                    \
+{                                         \
+   if (bzerror != NULL) *bzerror = eee;   \
+   if (bzf != NULL) bzf->lastErr = eee;   \
+}
+
+typedef 
+   struct {
+      FILE*     handle;
+      Char      buf[BZ_MAX_UNUSED];
+      Int32     bufN;
+      Bool      writing;
+      bz_stream strm;
+      Int32     lastErr;
+      Bool      initialisedOk;
+   }
+   bzFile;
+
+
+/*---------------------------------------------*/
+static Bool myfeof ( FILE* f )
+{
+   Int32 c = fgetc ( f );
+   if (c == EOF) return True;
+   ungetc ( c, f );
+   return False;
+}
+
+
+/*---------------------------------------------------*/
+BZFILE* BZ_API(BZ2_bzWriteOpen) 
+                    ( int*  bzerror,      
+                      FILE* f, 
+                      int   blockSize100k, 
+                      int   verbosity,
+                      int   workFactor )
+{
+   Int32   ret;
+   bzFile* bzf = NULL;
+
+   BZ_SETERR(BZ_OK);
+
+   if (f == NULL ||
+       (blockSize100k < 1 || blockSize100k > 9) ||
+       (workFactor < 0 || workFactor > 250) ||
+       (verbosity < 0 || verbosity > 4))
+      { BZ_SETERR(BZ_PARAM_ERROR); return NULL; };
+
+   if (ferror(f))
+      { BZ_SETERR(BZ_IO_ERROR); return NULL; };
+
+   bzf = malloc ( sizeof(bzFile) );
+   if (bzf == NULL)
+      { BZ_SETERR(BZ_MEM_ERROR); return NULL; };
+
+   BZ_SETERR(BZ_OK);
+   bzf->initialisedOk = False;
+   bzf->bufN          = 0;
+   bzf->handle        = f;
+   bzf->writing       = True;
+   bzf->strm.bzalloc  = NULL;
+   bzf->strm.bzfree   = NULL;
+   bzf->strm.opaque   = NULL;
+
+   if (workFactor == 0) workFactor = 30;
+   ret = BZ2_bzCompressInit ( &(bzf->strm), blockSize100k, 
+                              verbosity, workFactor );
+   if (ret != BZ_OK)
+      { BZ_SETERR(ret); free(bzf); return NULL; };
+
+   bzf->strm.avail_in = 0;
+   bzf->initialisedOk = True;
+   return bzf;   
+}
+
+
+
+/*---------------------------------------------------*/
+void BZ_API(BZ2_bzWrite)
+             ( int*    bzerror, 
+               BZFILE* b, 
+               void*   buf, 
+               int     len )
+{
+   Int32 n, n2, ret;
+   bzFile* bzf = (bzFile*)b;
+
+   BZ_SETERR(BZ_OK);
+   if (bzf == NULL || buf == NULL || len < 0)
+      { BZ_SETERR(BZ_PARAM_ERROR); return; };
+   if (!(bzf->writing))
+      { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };
+   if (ferror(bzf->handle))
+      { BZ_SETERR(BZ_IO_ERROR); return; };
+
+   if (len == 0)
+      { BZ_SETERR(BZ_OK); return; };
+
+   bzf->strm.avail_in = len;
+   bzf->strm.next_in  = buf;
+
+   while (True) {
+      bzf->strm.avail_out = BZ_MAX_UNUSED;
+      bzf->strm.next_out = bzf->buf;
+      ret = BZ2_bzCompress ( &(bzf->strm), BZ_RUN );
+      if (ret != BZ_RUN_OK)
+         { BZ_SETERR(ret); return; };
+
+      if (bzf->strm.avail_out < BZ_MAX_UNUSED) {
+         n = BZ_MAX_UNUSED - bzf->strm.avail_out;
+         n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), 
+                       n, bzf->handle );
+         if (n != n2 || ferror(bzf->handle))
+            { BZ_SETERR(BZ_IO_ERROR); return; };
+      }
+
+      if (bzf->strm.avail_in == 0)
+         { BZ_SETERR(BZ_OK); return; };
+   }
+}
+
+
+/*---------------------------------------------------*/
+void BZ_API(BZ2_bzWriteClose)
+                  ( int*          bzerror, 
+                    BZFILE*       b, 
+                    int           abandon,
+                    unsigned int* nbytes_in,
+                    unsigned int* nbytes_out )
+{
+   BZ2_bzWriteClose64 ( bzerror, b, abandon, 
+                        nbytes_in, NULL, nbytes_out, NULL );
+}
+
+
+void BZ_API(BZ2_bzWriteClose64)
+                  ( int*          bzerror, 
+                    BZFILE*       b, 
+                    int           abandon,
+                    unsigned int* nbytes_in_lo32,
+                    unsigned int* nbytes_in_hi32,
+                    unsigned int* nbytes_out_lo32,
+                    unsigned int* nbytes_out_hi32 )
+{
+   Int32   n, n2, ret;
+   bzFile* bzf = (bzFile*)b;
+
+   if (bzf == NULL)
+      { BZ_SETERR(BZ_OK); return; };
+   if (!(bzf->writing))
+      { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };
+   if (ferror(bzf->handle))
+      { BZ_SETERR(BZ_IO_ERROR); return; };
+
+   if (nbytes_in_lo32 != NULL) *nbytes_in_lo32 = 0;
+   if (nbytes_in_hi32 != NULL) *nbytes_in_hi32 = 0;
+   if (nbytes_out_lo32 != NULL) *nbytes_out_lo32 = 0;
+   if (nbytes_out_hi32 != NULL) *nbytes_out_hi32 = 0;
+
+   if ((!abandon) && bzf->lastErr == BZ_OK) {
+      while (True) {
+         bzf->strm.avail_out = BZ_MAX_UNUSED;
+         bzf->strm.next_out = bzf->buf;
+         ret = BZ2_bzCompress ( &(bzf->strm), BZ_FINISH );
+         if (ret != BZ_FINISH_OK && ret != BZ_STREAM_END)
+            { BZ_SETERR(ret); return; };
+
+         if (bzf->strm.avail_out < BZ_MAX_UNUSED) {
+            n = BZ_MAX_UNUSED - bzf->strm.avail_out;
+            n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), 
+                          n, bzf->handle );
+            if (n != n2 || ferror(bzf->handle))
+               { BZ_SETERR(BZ_IO_ERROR); return; };
+         }
+
+         if (ret == BZ_STREAM_END) break;
+      }
+   }
+
+   if ( !abandon && !ferror ( bzf->handle ) ) {
+      fflush ( bzf->handle );
+      if (ferror(bzf->handle))
+         { BZ_SETERR(BZ_IO_ERROR); return; };
+   }
+
+   if (nbytes_in_lo32 != NULL)
+      *nbytes_in_lo32 = bzf->strm.total_in_lo32;
+   if (nbytes_in_hi32 != NULL)
+      *nbytes_in_hi32 = bzf->strm.total_in_hi32;
+   if (nbytes_out_lo32 != NULL)
+      *nbytes_out_lo32 = bzf->strm.total_out_lo32;
+   if (nbytes_out_hi32 != NULL)
+      *nbytes_out_hi32 = bzf->strm.total_out_hi32;
+
+   BZ_SETERR(BZ_OK);
+   BZ2_bzCompressEnd ( &(bzf->strm) );
+   free ( bzf );
+}
+
+
+/*---------------------------------------------------*/
+BZFILE* BZ_API(BZ2_bzReadOpen) 
+                   ( int*  bzerror, 
+                     FILE* f, 
+                     int   verbosity,
+                     int   small,
+                     void* unused,
+                     int   nUnused )
+{
+   bzFile* bzf = NULL;
+   int     ret;
+
+   BZ_SETERR(BZ_OK);
+
+   if (f == NULL || 
+       (small != 0 && small != 1) ||
+       (verbosity < 0 || verbosity > 4) ||
+       (unused == NULL && nUnused != 0) ||
+       (unused != NULL && (nUnused < 0 || nUnused > BZ_MAX_UNUSED)))
+      { BZ_SETERR(BZ_PARAM_ERROR); return NULL; };
+
+   if (ferror(f))
+      { BZ_SETERR(BZ_IO_ERROR); return NULL; };
+
+   bzf = malloc ( sizeof(bzFile) );
+   if (bzf == NULL) 
+      { BZ_SETERR(BZ_MEM_ERROR); return NULL; };
+
+   BZ_SETERR(BZ_OK);
+
+   bzf->initialisedOk = False;
+   bzf->handle        = f;
+   bzf->bufN          = 0;
+   bzf->writing       = False;
+   bzf->strm.bzalloc  = NULL;
+   bzf->strm.bzfree   = NULL;
+   bzf->strm.opaque   = NULL;
+   
+   while (nUnused > 0) {
+      bzf->buf[bzf->bufN] = *((UChar*)(unused)); bzf->bufN++;
+      unused = ((void*)( 1 + ((UChar*)(unused))  ));
+      nUnused--;
+   }
+
+   ret = BZ2_bzDecompressInit ( &(bzf->strm), verbosity, small );
+   if (ret != BZ_OK)
+      { BZ_SETERR(ret); free(bzf); return NULL; };
+
+   bzf->strm.avail_in = bzf->bufN;
+   bzf->strm.next_in  = bzf->buf;
+
+   bzf->initialisedOk = True;
+   return bzf;   
+}
+
+
+/*---------------------------------------------------*/
+void BZ_API(BZ2_bzReadClose) ( int *bzerror, BZFILE *b )
+{
+   bzFile* bzf = (bzFile*)b;
+
+   BZ_SETERR(BZ_OK);
+   if (bzf == NULL)
+      { BZ_SETERR(BZ_OK); return; };
+
+   if (bzf->writing)
+      { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };
+
+   if (bzf->initialisedOk)
+      (void)BZ2_bzDecompressEnd ( &(bzf->strm) );
+   free ( bzf );
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzRead) 
+           ( int*    bzerror, 
+             BZFILE* b, 
+             void*   buf, 
+             int     len )
+{
+   Int32   n, ret;
+   bzFile* bzf = (bzFile*)b;
+
+   BZ_SETERR(BZ_OK);
+
+   if (bzf == NULL || buf == NULL || len < 0)
+      { BZ_SETERR(BZ_PARAM_ERROR); return 0; };
+
+   if (bzf->writing)
+      { BZ_SETERR(BZ_SEQUENCE_ERROR); return 0; };
+
+   if (len == 0)
+      { BZ_SETERR(BZ_OK); return 0; };
+
+   bzf->strm.avail_out = len;
+   bzf->strm.next_out = buf;
+
+   while (True) {
+
+      if (ferror(bzf->handle)) 
+         { BZ_SETERR(BZ_IO_ERROR); return 0; };
+
+      if (bzf->strm.avail_in == 0 && !myfeof(bzf->handle)) {
+         n = fread ( bzf->buf, sizeof(UChar), 
+                     BZ_MAX_UNUSED, bzf->handle );
+         if (ferror(bzf->handle))
+            { BZ_SETERR(BZ_IO_ERROR); return 0; };
+         bzf->bufN = n;
+         bzf->strm.avail_in = bzf->bufN;
+         bzf->strm.next_in = bzf->buf;
+      }
+
+      ret = BZ2_bzDecompress ( &(bzf->strm) );
+
+      if (ret != BZ_OK && ret != BZ_STREAM_END)
+         { BZ_SETERR(ret); return 0; };
+
+      if (ret == BZ_OK && myfeof(bzf->handle) && 
+          bzf->strm.avail_in == 0 && bzf->strm.avail_out > 0)
+         { BZ_SETERR(BZ_UNEXPECTED_EOF); return 0; };
+
+      if (ret == BZ_STREAM_END)
+         { BZ_SETERR(BZ_STREAM_END);
+           return len - bzf->strm.avail_out; };
+      if (bzf->strm.avail_out == 0)
+         { BZ_SETERR(BZ_OK); return len; };
+      
+   }
+
+   return 0; /*not reached*/
+}
+
+
+/*---------------------------------------------------*/
+void BZ_API(BZ2_bzReadGetUnused) 
+                     ( int*    bzerror, 
+                       BZFILE* b, 
+                       void**  unused, 
+                       int*    nUnused )
+{
+   bzFile* bzf = (bzFile*)b;
+   if (bzf == NULL)
+      { BZ_SETERR(BZ_PARAM_ERROR); return; };
+   if (bzf->lastErr != BZ_STREAM_END)
+      { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };
+   if (unused == NULL || nUnused == NULL)
+      { BZ_SETERR(BZ_PARAM_ERROR); return; };
+
+   BZ_SETERR(BZ_OK);
+   *nUnused = bzf->strm.avail_in;
+   *unused = bzf->strm.next_in;
+}
+#endif
+
+
+/*---------------------------------------------------*/
+/*--- Misc convenience stuff                      ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzBuffToBuffCompress) 
+                         ( char*         dest, 
+                           unsigned int* destLen,
+                           char*         source, 
+                           unsigned int  sourceLen,
+                           int           blockSize100k, 
+                           int           verbosity, 
+                           int           workFactor )
+{
+   bz_stream strm;
+   int ret;
+
+   if (dest == NULL || destLen == NULL || 
+       source == NULL ||
+       blockSize100k < 1 || blockSize100k > 9 ||
+       verbosity < 0 || verbosity > 4 ||
+       workFactor < 0 || workFactor > 250) 
+      return BZ_PARAM_ERROR;
+
+   if (workFactor == 0) workFactor = 30;
+   strm.bzalloc = NULL;
+   strm.bzfree = NULL;
+   strm.opaque = NULL;
+   ret = BZ2_bzCompressInit ( &strm, blockSize100k, 
+                              verbosity, workFactor );
+   if (ret != BZ_OK) return ret;
+
+   strm.next_in = source;
+   strm.next_out = dest;
+   strm.avail_in = sourceLen;
+   strm.avail_out = *destLen;
+
+   ret = BZ2_bzCompress ( &strm, BZ_FINISH );
+   if (ret == BZ_FINISH_OK) goto output_overflow;
+   if (ret != BZ_STREAM_END) goto errhandler;
+
+   /* normal termination */
+   *destLen -= strm.avail_out;   
+   BZ2_bzCompressEnd ( &strm );
+   return BZ_OK;
+
+   output_overflow:
+   BZ2_bzCompressEnd ( &strm );
+   return BZ_OUTBUFF_FULL;
+
+   errhandler:
+   BZ2_bzCompressEnd ( &strm );
+   return ret;
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzBuffToBuffDecompress) 
+                           ( char*         dest, 
+                             unsigned int* destLen,
+                             char*         source, 
+                             unsigned int  sourceLen,
+                             int           small,
+                             int           verbosity )
+{
+   bz_stream strm;
+   int ret;
+
+   if (dest == NULL || destLen == NULL || 
+       source == NULL ||
+       (small != 0 && small != 1) ||
+       verbosity < 0 || verbosity > 4) 
+          return BZ_PARAM_ERROR;
+
+   strm.bzalloc = NULL;
+   strm.bzfree = NULL;
+   strm.opaque = NULL;
+   ret = BZ2_bzDecompressInit ( &strm, verbosity, small );
+   if (ret != BZ_OK) return ret;
+
+   strm.next_in = source;
+   strm.next_out = dest;
+   strm.avail_in = sourceLen;
+   strm.avail_out = *destLen;
+
+   ret = BZ2_bzDecompress ( &strm );
+   if (ret == BZ_OK) goto output_overflow_or_eof;
+   if (ret != BZ_STREAM_END) goto errhandler;
+
+   /* normal termination */
+   *destLen -= strm.avail_out;
+   BZ2_bzDecompressEnd ( &strm );
+   return BZ_OK;
+
+   output_overflow_or_eof:
+   if (strm.avail_out > 0) {
+      BZ2_bzDecompressEnd ( &strm );
+      return BZ_UNEXPECTED_EOF;
+   } else {
+      BZ2_bzDecompressEnd ( &strm );
+      return BZ_OUTBUFF_FULL;
+   };      
+
+   errhandler:
+   BZ2_bzDecompressEnd ( &strm );
+   return ret; 
+}
+
+
+/*---------------------------------------------------*/
+/*--
+   Code contributed by Yoshioka Tsuneo
+   (QWF00133@niftyserve.or.jp/tsuneo-y@is.aist-nara.ac.jp),
+   to support better zlib compatibility.
+   This code is not _officially_ part of libbzip2 (yet);
+   I haven't tested it, documented it, or considered the
+   threading-safeness of it.
+   If this code breaks, please contact both Yoshioka and me.
+--*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+/*--
+   return version like "0.9.0c".
+--*/
+const char * BZ_API(BZ2_bzlibVersion)(void)
+{
+   return BZ_VERSION;
+}
+
+
+#ifndef BZ_NO_STDIO
+/*---------------------------------------------------*/
+
+static
+BZFILE * bzopen_or_bzdopen
+               ( const char *path,   /* no use when bzdopen */
+                 int fd,             /* no use when bzdopen */
+                 const char *mode,
+                 int open_mode)      /* bzopen: 0, bzdopen:1 */
+{
+   int    bzerr;
+   char   unused[BZ_MAX_UNUSED];
+   int    blockSize100k = 9;
+   int    writing       = 0;
+   char   mode2[10]     = "";
+   FILE   *fp           = NULL;
+   BZFILE *bzfp         = NULL;
+   int    verbosity     = 0;
+   int    workFactor    = 30;
+   int    smallMode     = 0;
+   int    nUnused       = 0; 
+
+   if (mode == NULL) return NULL;
+   while (*mode) {
+      switch (*mode) {
+      case 'r':
+         writing = 0; break;
+      case 'w':
+         writing = 1; break;
+      case 's':
+         smallMode = 1; break;
+      default:
+         if (isdigit((int)(*mode))) {
+            blockSize100k = *mode-BZ_HDR_0;
+         }
+      }
+      mode++;
+   }
+   strcat(mode2, writing ? "w" : "r" );
+   strcat(mode2,"b");   /* binary mode */
+
+   if (open_mode==0) {
+      if (path==NULL || strcmp(path,"")==0) {
+        fp = (writing ? stdout : stdin);
+      } else {
+        fp = fopen(path,mode2);
+      }
+   } else {
+#ifdef BZ_STRICT_ANSI
+      fp = NULL;
+#else
+      fp = fdopen(fd,mode2);
+#endif
+   }
+   if (fp == NULL) return NULL;
+
+   if (writing) {
+      /* Guard against total chaos and anarchy -- JRS */
+      if (blockSize100k < 1) blockSize100k = 1;
+      if (blockSize100k > 9) blockSize100k = 9; 
+      bzfp = BZ2_bzWriteOpen(&bzerr,fp,blockSize100k,
+                             verbosity,workFactor);
+   } else {
+      bzfp = BZ2_bzReadOpen(&bzerr,fp,verbosity,smallMode,
+                            unused,nUnused);
+   }
+   if (bzfp == NULL) {
+      if (fp != stdin && fp != stdout) fclose(fp);
+      return NULL;
+   }
+   return bzfp;
+}
+
+
+/*---------------------------------------------------*/
+/*--
+   open file for read or write.
+      ex) bzopen("file","w9")
+      case path="" or NULL => use stdin or stdout.
+--*/
+BZFILE * BZ_API(BZ2_bzopen)
+               ( const char *path,
+                 const char *mode )
+{
+   return bzopen_or_bzdopen(path,-1,mode,/*bzopen*/0);
+}
+
+
+/*---------------------------------------------------*/
+BZFILE * BZ_API(BZ2_bzdopen)
+               ( int fd,
+                 const char *mode )
+{
+   return bzopen_or_bzdopen(NULL,fd,mode,/*bzdopen*/1);
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzread) (BZFILE* b, void* buf, int len )
+{
+   int bzerr, nread;
+   if (((bzFile*)b)->lastErr == BZ_STREAM_END) return 0;
+   nread = BZ2_bzRead(&bzerr,b,buf,len);
+   if (bzerr == BZ_OK || bzerr == BZ_STREAM_END) {
+      return nread;
+   } else {
+      return -1;
+   }
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzwrite) (BZFILE* b, void* buf, int len )
+{
+   int bzerr;
+
+   BZ2_bzWrite(&bzerr,b,buf,len);
+   if(bzerr == BZ_OK){
+      return len;
+   }else{
+      return -1;
+   }
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzflush) (BZFILE *b)
+{
+   /* do nothing now... */
+   return 0;
+}
+
+
+/*---------------------------------------------------*/
+void BZ_API(BZ2_bzclose) (BZFILE* b)
+{
+   int bzerr;
+   FILE *fp = ((bzFile *)b)->handle;
+   
+   if (b==NULL) {return;}
+   if(((bzFile*)b)->writing){
+      BZ2_bzWriteClose(&bzerr,b,0,NULL,NULL);
+      if(bzerr != BZ_OK){
+         BZ2_bzWriteClose(NULL,b,1,NULL,NULL);
+      }
+   }else{
+      BZ2_bzReadClose(&bzerr,b);
+   }
+   if(fp!=stdin && fp!=stdout){
+      fclose(fp);
+   }
+}
+
+
+/*---------------------------------------------------*/
+/*--
+   return last error code 
+--*/
+static char *bzerrorstrings[] = {
+       "OK"
+      ,"SEQUENCE_ERROR"
+      ,"PARAM_ERROR"
+      ,"MEM_ERROR"
+      ,"DATA_ERROR"
+      ,"DATA_ERROR_MAGIC"
+      ,"IO_ERROR"
+      ,"UNEXPECTED_EOF"
+      ,"OUTBUFF_FULL"
+      ,"CONFIG_ERROR"
+      ,"???"   /* for future */
+      ,"???"   /* for future */
+      ,"???"   /* for future */
+      ,"???"   /* for future */
+      ,"???"   /* for future */
+      ,"???"   /* for future */
+};
+
+
+const char * BZ_API(BZ2_bzerror) (BZFILE *b, int *errnum)
+{
+   int err = ((bzFile *)b)->lastErr;
+
+   if(err>0) err = 0;
+   *errnum = err;
+   return bzerrorstrings[err*-1];
+}
+#endif
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                           bzlib.c ---*/
+/*-------------------------------------------------------------*/
+
+/*-----------------------------------------------------------*/
+/*--- A block-sorting, lossless compressor        bzip2.c ---*/
+/*-----------------------------------------------------------*/
+
+
+
+/*----------------------------------------------------*/
+/*--- IMPORTANT                                    ---*/
+/*----------------------------------------------------*/
+
+/*--
+   WARNING:
+      This program and library (attempts to) compress data by 
+      performing several non-trivial transformations on it.  
+      Unless you are 100% familiar with *all* the algorithms 
+      contained herein, and with the consequences of modifying them, 
+      you should NOT meddle with the compression or decompression 
+      machinery.  Incorrect changes can and very likely *will* 
+      lead to disasterous loss of data.
+
+   DISCLAIMER:
+      I TAKE NO RESPONSIBILITY FOR ANY LOSS OF DATA ARISING FROM THE
+      USE OF THIS PROGRAM, HOWSOEVER CAUSED.
+
+      Every compression of a file implies an assumption that the
+      compressed file can be decompressed to reproduce the original.
+      Great efforts in design, coding and testing have been made to
+      ensure that this program works correctly.  However, the
+      complexity of the algorithms, and, in particular, the presence
+      of various special cases in the code which occur with very low
+      but non-zero probability make it impossible to rule out the
+      possibility of bugs remaining in the program.  DO NOT COMPRESS
+      ANY DATA WITH THIS PROGRAM AND/OR LIBRARY UNLESS YOU ARE PREPARED 
+      TO ACCEPT THE POSSIBILITY, HOWEVER SMALL, THAT THE DATA WILL 
+      NOT BE RECOVERABLE.
+
+      That is not to say this program is inherently unreliable.
+      Indeed, I very much hope the opposite is true.  bzip2/libbzip2
+      has been carefully constructed and extensively tested.
+
+   PATENTS:
+      To the best of my knowledge, bzip2/libbzip2 does not use any 
+      patented algorithms.  However, I do not have the resources 
+      available to carry out a full patent search.  Therefore I cannot 
+      give any guarantee of the above statement.
+--*/
+
+
+
+/*----------------------------------------------------*/
+/*--- and now for something much more pleasant :-) ---*/
+/*----------------------------------------------------*/
+
+/*---------------------------------------------*/
+/*--
+  Place a 1 beside your platform, and 0 elsewhere.
+--*/
+
+/*--
+  Generic 32-bit Unix.
+  Also works on 64-bit Unix boxes.
+  This is the default.
+--*/
+#define BZ_UNIX      1
+
+/*--
+  Win32, as seen by Jacob Navia's excellent
+  port of (Chris Fraser & David Hanson)'s excellent
+  lcc compiler.  Or with MS Visual C.
+  This is selected automatically if compiled by a compiler which
+  defines _WIN32, not including the Cygwin GCC.
+--*/
+#define BZ_LCCWIN32  0
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#undef  BZ_LCCWIN32
+#define BZ_LCCWIN32 1
+#undef  BZ_UNIX
+#define BZ_UNIX 0
+#endif
+
+
+/*---------------------------------------------*/
+/*--
+  Some stuff for all platforms.
+--*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <math.h>
+#include <errno.h>
+#include <ctype.h>
+
+#define ERROR_IF_EOF(i)       { if ((i) == EOF)  ioError(); }
+#define ERROR_IF_NOT_ZERO(i)  { if ((i) != 0)    ioError(); }
+#define ERROR_IF_MINUS_ONE(i) { if ((i) == (-1)) ioError(); }
+
+
+/*---------------------------------------------*/
+/*--
+   Platform-specific stuff.
+--*/
+
+#if BZ_UNIX
+#   include <fcntl.h>
+#   include <sys/types.h>
+#   include <utime.h>
+#   include <unistd.h>
+#   include <sys/stat.h>
+#   include <sys/times.h>
+
+#   define PATH_SEP    '/'
+#   define MY_LSTAT    lstat
+#   define MY_STAT     stat
+#   define MY_S_ISREG  S_ISREG
+#   define MY_S_ISDIR  S_ISDIR
+
+#   define APPEND_FILESPEC(root, name) \
+      root=snocString((root), (name))
+
+#   define APPEND_FLAG(root, name) \
+      root=snocString((root), (name))
+
+#   define SET_BINARY_MODE(fd) /**/
+
+#   ifdef __GNUC__
+#      define NORETURN __attribute__ ((noreturn))
+#   else
+#      define NORETURN /**/
+#   endif
+
+#   ifdef __DJGPP__
+#     include <io.h>
+#     include <fcntl.h>
+#     undef MY_LSTAT
+#     undef MY_STAT
+#     define MY_LSTAT stat
+#     define MY_STAT stat
+#     undef SET_BINARY_MODE
+#     define SET_BINARY_MODE(fd)                        \
+        do {                                            \
+           int retVal = setmode ( fileno ( fd ),        \
+                                  O_BINARY );           \
+           ERROR_IF_MINUS_ONE ( retVal );               \
+        } while ( 0 )
+#   endif
+
+#   ifdef __CYGWIN__
+#     include <io.h>
+#     include <fcntl.h>
+#     undef SET_BINARY_MODE
+#     define SET_BINARY_MODE(fd)                        \
+        do {                                            \
+           int retVal = setmode ( fileno ( fd ),        \
+                                  O_BINARY );           \
+           ERROR_IF_MINUS_ONE ( retVal );               \
+        } while ( 0 )
+#   endif
+#endif /* BZ_UNIX */
+
+
+
+#if BZ_LCCWIN32
+#   include <io.h>
+#   include <fcntl.h>
+#   include <sys\stat.h>
+
+#   define NORETURN       /**/
+#   define PATH_SEP       '\\'
+#   define MY_LSTAT       _stat
+#   define MY_STAT        _stat
+#   define MY_S_ISREG(x)  ((x) & _S_IFREG)
+#   define MY_S_ISDIR(x)  ((x) & _S_IFDIR)
+
+#   define APPEND_FLAG(root, name) \
+      root=snocString((root), (name))
+
+#   define APPEND_FILESPEC(root, name)                \
+      root = snocString ((root), (name))
+
+#   define SET_BINARY_MODE(fd)                        \
+      do {                                            \
+         int retVal = setmode ( fileno ( fd ),        \
+                                O_BINARY );           \
+         ERROR_IF_MINUS_ONE ( retVal );               \
+      } while ( 0 )
+
+#endif /* BZ_LCCWIN32 */
+
+
+/*---------------------------------------------*/
+/*--
+  Some more stuff for all platforms :-)
+--*/
+                                       
+#define True  ((Bool)1)
+#define False ((Bool)0)
+
+/*--
+  IntNative is your platform's `native' int size.
+  Only here to avoid probs with 64-bit platforms.
+--*/
+typedef int IntNative;
+
+
+/*---------------------------------------------------*/
+/*--- Misc (file handling) data decls             ---*/
+/*---------------------------------------------------*/
+
+Int32   verbosity;
+Bool    keepInputFiles, smallMode, deleteOutputOnInterrupt;
+Bool    forceOverwrite, testFailsExist, unzFailsExist, noisy;
+Int32   numFileNames, numFilesProcessed, blockSize100k;
+Int32   exitValue;
+
+/*-- source modes; F==file, I==stdin, O==stdout --*/
+#define SM_I2O           1
+#define SM_F2O           2
+#define SM_F2F           3
+
+/*-- operation modes --*/
+#define OM_Z             1
+#define OM_UNZ           2
+#define OM_TEST          3
+
+Int32   opMode;
+Int32   srcMode;
+
+#define FILE_NAME_LEN 1034
+
+Int32   longestFileName;
+Char    inName [FILE_NAME_LEN];
+Char    outName[FILE_NAME_LEN];
+Char    tmpName[FILE_NAME_LEN];
+Char    *progName;
+Char    progNameReally[FILE_NAME_LEN];
+FILE    *outputHandleJustInCase;
+Int32   workFactor;
+
+static void    panic                 ( Char* )   NORETURN;
+static void    ioError               ( void )    NORETURN;
+static void    outOfMemory           ( void )    NORETURN;
+static void    configError           ( void )    NORETURN;
+static void    crcError              ( void )    NORETURN;
+static void    cleanUpAndFail        ( Int32 )   NORETURN;
+static void    compressedStreamEOF   ( void )    NORETURN;
+
+static void    copyFileName ( Char*, Char* );
+static void*   myMalloc     ( Int32 );
+
+
+
+/*---------------------------------------------------*/
+/*--- An implementation of 64-bit ints.  Sigh.    ---*/
+/*--- Roll on widespread deployment of ANSI C9X ! ---*/
+/*---------------------------------------------------*/
+
+typedef
+   struct { UChar b[8]; } 
+   UInt64;
+
+
+static
+void uInt64_from_UInt32s ( UInt64* n, UInt32 lo32, UInt32 hi32 )
+{
+   n->b[7] = (UChar)((hi32 >> 24) & 0xFF);
+   n->b[6] = (UChar)((hi32 >> 16) & 0xFF);
+   n->b[5] = (UChar)((hi32 >> 8)  & 0xFF);
+   n->b[4] = (UChar) (hi32        & 0xFF);
+   n->b[3] = (UChar)((lo32 >> 24) & 0xFF);
+   n->b[2] = (UChar)((lo32 >> 16) & 0xFF);
+   n->b[1] = (UChar)((lo32 >> 8)  & 0xFF);
+   n->b[0] = (UChar) (lo32        & 0xFF);
+}
+
+
+static
+double uInt64_to_double ( UInt64* n )
+{
+   Int32  i;
+   double base = 1.0;
+   double sum  = 0.0;
+   for (i = 0; i < 8; i++) {
+      sum  += base * (double)(n->b[i]);
+      base *= 256.0;
+   }
+   return sum;
+}
+
+
+static
+Bool uInt64_isZero ( UInt64* n )
+{
+   Int32 i;
+   for (i = 0; i < 8; i++)
+      if (n->b[i] != 0) return 0;
+   return 1;
+}
+
+
+/* Divide *n by 10, and return the remainder.  */
+static 
+Int32 uInt64_qrm10 ( UInt64* n )
+{
+   UInt32 rem, tmp;
+   Int32  i;
+   rem = 0;
+   for (i = 7; i >= 0; i--) {
+      tmp = rem * 256 + n->b[i];
+      n->b[i] = tmp / 10;
+      rem = tmp % 10;
+   }
+   return rem;
+}
+
+
+/* ... and the Whole Entire Point of all this UInt64 stuff is
+   so that we can supply the following function.
+*/
+static
+void uInt64_toAscii ( char* outbuf, UInt64* n )
+{
+   Int32  i, q;
+   UChar  buf[32];
+   Int32  nBuf   = 0;
+   UInt64 n_copy = *n;
+   do {
+      q = uInt64_qrm10 ( &n_copy );
+      buf[nBuf] = q + '0';
+      nBuf++;
+   } while (!uInt64_isZero(&n_copy));
+   outbuf[nBuf] = 0;
+   for (i = 0; i < nBuf; i++) 
+      outbuf[i] = buf[nBuf-i-1];
+}
+
+
+/*---------------------------------------------------*/
+/*--- Processing of complete files and streams    ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------*/
+
+/*---------------------------------------------*/
+static 
+void compressStream ( FILE *stream, FILE *zStream )
+{
+   BZFILE* bzf = NULL;
+   UChar   ibuf[5000];
+   Int32   nIbuf;
+   UInt32  nbytes_in_lo32, nbytes_in_hi32;
+   UInt32  nbytes_out_lo32, nbytes_out_hi32;
+   Int32   bzerr, bzerr_dummy, ret;
+
+   SET_BINARY_MODE(stream);
+   SET_BINARY_MODE(zStream);
+
+   if (ferror(stream)) goto errhandler_io;
+   if (ferror(zStream)) goto errhandler_io;
+
+   bzf = BZ2_bzWriteOpen ( &bzerr, zStream, 
+                           blockSize100k, verbosity, workFactor );   
+   if (bzerr != BZ_OK) goto errhandler;
+
+   if (verbosity >= 2) fprintf ( stderr, "\n" );
+
+   while (True) {
+
+      if (myfeof(stream)) break;
+      nIbuf = fread ( ibuf, sizeof(UChar), 5000, stream );
+      if (ferror(stream)) goto errhandler_io;
+      if (nIbuf > 0) BZ2_bzWrite ( &bzerr, bzf, (void*)ibuf, nIbuf );
+      if (bzerr != BZ_OK) goto errhandler;
+
+   }
+
+   BZ2_bzWriteClose64 ( &bzerr, bzf, 0, 
+                        &nbytes_in_lo32, &nbytes_in_hi32,
+                        &nbytes_out_lo32, &nbytes_out_hi32 );
+   if (bzerr != BZ_OK) goto errhandler;
+
+   if (ferror(zStream)) goto errhandler_io;
+   ret = fflush ( zStream );
+   if (ret == EOF) goto errhandler_io;
+   if (zStream != stdout) {
+      ret = fclose ( zStream );
+      outputHandleJustInCase = NULL;
+      if (ret == EOF) goto errhandler_io;
+   }
+   outputHandleJustInCase = NULL;
+   if (ferror(stream)) goto errhandler_io;
+   ret = fclose ( stream );
+   if (ret == EOF) goto errhandler_io;
+
+   if (verbosity >= 1) {
+      if (nbytes_in_lo32 == 0 && nbytes_in_hi32 == 0) {
+	 fprintf ( stderr, " no data compressed.\n");
+      } else {
+	 Char   buf_nin[32], buf_nout[32];
+	 UInt64 nbytes_in,   nbytes_out;
+	 double nbytes_in_d, nbytes_out_d;
+	 uInt64_from_UInt32s ( &nbytes_in, 
+			       nbytes_in_lo32, nbytes_in_hi32 );
+	 uInt64_from_UInt32s ( &nbytes_out, 
+			       nbytes_out_lo32, nbytes_out_hi32 );
+	 nbytes_in_d  = uInt64_to_double ( &nbytes_in );
+	 nbytes_out_d = uInt64_to_double ( &nbytes_out );
+	 uInt64_toAscii ( buf_nin, &nbytes_in );
+	 uInt64_toAscii ( buf_nout, &nbytes_out );
+	 fprintf ( stderr, "%6.3f:1, %6.3f bits/byte, "
+		   "%5.2f%% saved, %s in, %s out.\n",
+		   nbytes_in_d / nbytes_out_d,
+		   (8.0 * nbytes_out_d) / nbytes_in_d,
+		   100.0 * (1.0 - nbytes_out_d / nbytes_in_d),
+		   buf_nin,
+		   buf_nout
+		 );
+      }
+   }
+
+   return;
+
+   errhandler:
+   BZ2_bzWriteClose64 ( &bzerr_dummy, bzf, 1, 
+                        &nbytes_in_lo32, &nbytes_in_hi32,
+                        &nbytes_out_lo32, &nbytes_out_hi32 );
+   switch (bzerr) {
+      case BZ_CONFIG_ERROR:
+         configError(); break;
+      case BZ_MEM_ERROR:
+         outOfMemory (); break;
+      case BZ_IO_ERROR:
+         errhandler_io:
+         ioError(); break;
+      default:
+         panic ( "compress:unexpected error" );
+   }
+
+   panic ( "compress:end" );
+   /*notreached*/
+}
+
+
+
+/*---------------------------------------------*/
+static 
+Bool uncompressStream ( FILE *zStream, FILE *stream )
+{
+   BZFILE* bzf = NULL;
+   Int32   bzerr, bzerr_dummy, ret, nread, streamNo, i;
+   UChar   obuf[5000];
+   UChar   unused[BZ_MAX_UNUSED];
+   Int32   nUnused;
+   UChar*  unusedTmp;
+
+   nUnused = 0;
+   streamNo = 0;
+
+   SET_BINARY_MODE(stream);
+   SET_BINARY_MODE(zStream);
+
+   if (ferror(stream)) goto errhandler_io;
+   if (ferror(zStream)) goto errhandler_io;
+
+   while (True) {
+
+      bzf = BZ2_bzReadOpen ( 
+               &bzerr, zStream, verbosity, 
+               (int)smallMode, unused, nUnused
+            );
+      if (bzf == NULL || bzerr != BZ_OK) goto errhandler;
+      streamNo++;
+
+      while (bzerr == BZ_OK) {
+         nread = BZ2_bzRead ( &bzerr, bzf, obuf, 5000 );
+         if (bzerr == BZ_DATA_ERROR_MAGIC) goto trycat;
+         if ((bzerr == BZ_OK || bzerr == BZ_STREAM_END) && nread > 0)
+            fwrite ( obuf, sizeof(UChar), nread, stream );
+         if (ferror(stream)) goto errhandler_io;
+      }
+      if (bzerr != BZ_STREAM_END) goto errhandler;
+
+      BZ2_bzReadGetUnused ( &bzerr, bzf, (void**)(&unusedTmp), &nUnused );
+      if (bzerr != BZ_OK) panic ( "decompress:bzReadGetUnused" );
+
+      for (i = 0; i < nUnused; i++) unused[i] = unusedTmp[i];
+
+      BZ2_bzReadClose ( &bzerr, bzf );
+      if (bzerr != BZ_OK) panic ( "decompress:bzReadGetUnused" );
+
+      if (nUnused == 0 && myfeof(zStream)) break;
+   }
+
+   closeok:
+   if (ferror(zStream)) goto errhandler_io;
+   ret = fclose ( zStream );
+   if (ret == EOF) goto errhandler_io;
+
+   if (ferror(stream)) goto errhandler_io;
+   ret = fflush ( stream );
+   if (ret != 0) goto errhandler_io;
+   if (stream != stdout) {
+      ret = fclose ( stream );
+      outputHandleJustInCase = NULL;
+      if (ret == EOF) goto errhandler_io;
+   }
+   outputHandleJustInCase = NULL;
+   if (verbosity >= 2) fprintf ( stderr, "\n    " );
+   return True;
+
+   trycat: 
+   if (forceOverwrite) {
+      rewind(zStream);
+      while (True) {
+      	 if (myfeof(zStream)) break;
+      	 nread = fread ( obuf, sizeof(UChar), 5000, zStream );
+      	 if (ferror(zStream)) goto errhandler_io;
+      	 if (nread > 0) fwrite ( obuf, sizeof(UChar), nread, stream );
+      	 if (ferror(stream)) goto errhandler_io;
+      }
+      goto closeok;
+   }
+  
+   errhandler:
+   BZ2_bzReadClose ( &bzerr_dummy, bzf );
+   switch (bzerr) {
+      case BZ_CONFIG_ERROR:
+         configError(); break;
+      case BZ_IO_ERROR:
+         errhandler_io:
+         ioError(); break;
+      case BZ_DATA_ERROR:
+         crcError();
+      case BZ_MEM_ERROR:
+         outOfMemory();
+      case BZ_UNEXPECTED_EOF:
+         compressedStreamEOF();
+      case BZ_DATA_ERROR_MAGIC:
+         if (zStream != stdin) fclose(zStream);
+         if (stream != stdout) fclose(stream);
+         if (streamNo == 1) {
+            return False;
+         } else {
+            if (noisy)
+            fprintf ( stderr, 
+                      "\n%s: %s: trailing garbage after EOF ignored\n",
+                      progName, inName );
+            return True;       
+         }
+      default:
+         panic ( "decompress:unexpected error" );
+   }
+
+   panic ( "decompress:end" );
+   return True; /*notreached*/
+}
+
+
+/*---------------------------------------------*/
+static 
+Bool testStream ( FILE *zStream )
+{
+   BZFILE* bzf = NULL;
+   Int32   bzerr, bzerr_dummy, ret, nread, streamNo, i;
+   UChar   obuf[5000];
+   UChar   unused[BZ_MAX_UNUSED];
+   Int32   nUnused;
+   UChar*  unusedTmp;
+
+   nUnused = 0;
+   streamNo = 0;
+
+   SET_BINARY_MODE(zStream);
+   if (ferror(zStream)) goto errhandler_io;
+
+   while (True) {
+
+      bzf = BZ2_bzReadOpen ( 
+               &bzerr, zStream, verbosity, 
+               (int)smallMode, unused, nUnused
+            );
+      if (bzf == NULL || bzerr != BZ_OK) goto errhandler;
+      streamNo++;
+
+      while (bzerr == BZ_OK) {
+         nread = BZ2_bzRead ( &bzerr, bzf, obuf, 5000 );
+         if (bzerr == BZ_DATA_ERROR_MAGIC) goto errhandler;
+      }
+      if (bzerr != BZ_STREAM_END) goto errhandler;
+
+      BZ2_bzReadGetUnused ( &bzerr, bzf, (void**)(&unusedTmp), &nUnused );
+      if (bzerr != BZ_OK) panic ( "test:bzReadGetUnused" );
+
+      for (i = 0; i < nUnused; i++) unused[i] = unusedTmp[i];
+
+      BZ2_bzReadClose ( &bzerr, bzf );
+      if (bzerr != BZ_OK) panic ( "test:bzReadGetUnused" );
+      if (nUnused == 0 && myfeof(zStream)) break;
+
+   }
+
+   if (ferror(zStream)) goto errhandler_io;
+   ret = fclose ( zStream );
+   if (ret == EOF) goto errhandler_io;
+
+   if (verbosity >= 2) fprintf ( stderr, "\n    " );
+   return True;
+
+   errhandler:
+   BZ2_bzReadClose ( &bzerr_dummy, bzf );
+   if (verbosity == 0) 
+      fprintf ( stderr, "%s: %s: ", progName, inName );
+   switch (bzerr) {
+      case BZ_CONFIG_ERROR:
+         configError(); break;
+      case BZ_IO_ERROR:
+         errhandler_io:
+         ioError(); break;
+      case BZ_DATA_ERROR:
+         fprintf ( stderr,
+                   "data integrity (CRC) error in data\n" );
+         return False;
+      case BZ_MEM_ERROR:
+         outOfMemory();
+      case BZ_UNEXPECTED_EOF:
+         fprintf ( stderr,
+                   "file ends unexpectedly\n" );
+         return False;
+      case BZ_DATA_ERROR_MAGIC:
+         if (zStream != stdin) fclose(zStream);
+         if (streamNo == 1) {
+          fprintf ( stderr, 
+                    "bad magic number (file not created by bzip2)\n" );
+            return False;
+         } else {
+            if (noisy)
+            fprintf ( stderr, 
+                      "trailing garbage after EOF ignored\n" );
+            return True;       
+         }
+      default:
+         panic ( "test:unexpected error" );
+   }
+
+   panic ( "test:end" );
+   return True; /*notreached*/
+}
+
+
+/*---------------------------------------------------*/
+/*--- Error [non-] handling grunge                ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------*/
+static
+void setExit ( Int32 v )
+{
+   if (v > exitValue) exitValue = v;
+}
+
+
+/*---------------------------------------------*/
+static 
+void cadvise ( void )
+{
+   if (noisy)
+   fprintf (
+      stderr,
+      "\nIt is possible that the compressed file(s) have become corrupted.\n"
+        "You can use the -tvv option to test integrity of such files.\n\n"
+        "You can use the `bzip2recover' program to attempt to recover\n"
+        "data from undamaged sections of corrupted files.\n\n"
+    );
+}
+
+
+/*---------------------------------------------*/
+static 
+void showFileNames ( void )
+{
+   if (noisy)
+   fprintf (
+      stderr,
+      "\tInput file = %s, output file = %s\n",
+      inName, outName 
+   );
+}
+
+
+/*---------------------------------------------*/
+static 
+void cleanUpAndFail ( Int32 ec )
+{
+   IntNative      retVal;
+   struct MY_STAT statBuf;
+
+   if ( srcMode == SM_F2F 
+        && opMode != OM_TEST
+        && deleteOutputOnInterrupt ) {
+
+      /* Check whether input file still exists.  Delete output file
+         only if input exists to avoid loss of data.  Joerg Prante, 5
+         January 2002.  (JRS 06-Jan-2002: other changes in 1.0.2 mean
+         this is less likely to happen.  But to be ultra-paranoid, we
+         do the check anyway.)  */
+      retVal = MY_STAT ( inName, &statBuf );
+      if (retVal == 0) {
+         if (noisy)
+            fprintf ( stderr, 
+                      "%s: Deleting output file %s, if it exists.\n",
+                      progName, outName );
+         if (outputHandleJustInCase != NULL)
+            fclose ( outputHandleJustInCase );
+         retVal = remove ( outName );
+         if (retVal != 0)
+            fprintf ( stderr,
+                      "%s: WARNING: deletion of output file "
+                      "(apparently) failed.\n",
+                      progName );
+      } else {
+         fprintf ( stderr,
+                   "%s: WARNING: deletion of output file suppressed\n",
+                    progName );
+         fprintf ( stderr,
+                   "%s:    since input file no longer exists.  Output file\n",
+                   progName );
+         fprintf ( stderr,
+                   "%s:    `%s' may be incomplete.\n",
+                   progName, outName );
+         fprintf ( stderr, 
+                   "%s:    I suggest doing an integrity test (bzip2 -tv)"
+                   " of it.\n",
+                   progName );
+      }
+   }
+
+   if (noisy && numFileNames > 0 && numFilesProcessed < numFileNames) {
+      fprintf ( stderr, 
+                "%s: WARNING: some files have not been processed:\n"
+                "%s:    %d specified on command line, %d not processed yet.\n\n",
+                progName, progName,
+                numFileNames, numFileNames - numFilesProcessed );
+   }
+   setExit(ec);
+   exit(exitValue);
+}
+
+
+/*---------------------------------------------*/
+static 
+void panic ( Char* s )
+{
+   fprintf ( stderr,
+             "\n%s: PANIC -- internal consistency error:\n"
+             "\t%s\n"
+             "\tThis is a BUG.  Please report it to me at:\n"
+             "\tjseward@acm.org\n",
+             progName, s );
+   showFileNames();
+   cleanUpAndFail( 3 );
+}
+
+
+/*---------------------------------------------*/
+static 
+void crcError ( void )
+{
+   fprintf ( stderr,
+             "\n%s: Data integrity error when decompressing.\n",
+             progName );
+   showFileNames();
+   cadvise();
+   cleanUpAndFail( 2 );
+}
+
+
+/*---------------------------------------------*/
+static 
+void compressedStreamEOF ( void )
+{
+  if (noisy) {
+    fprintf ( stderr,
+	      "\n%s: Compressed file ends unexpectedly;\n\t"
+	      "perhaps it is corrupted?  *Possible* reason follows.\n",
+	      progName );
+    perror ( progName );
+    showFileNames();
+    cadvise();
+  }
+  cleanUpAndFail( 2 );
+}
+
+
+/*---------------------------------------------*/
+static 
+void ioError ( void )
+{
+   fprintf ( stderr,
+             "\n%s: I/O or other error, bailing out.  "
+             "Possible reason follows.\n",
+             progName );
+   perror ( progName );
+   showFileNames();
+   cleanUpAndFail( 1 );
+}
+
+
+/*---------------------------------------------*/
+static 
+void mySignalCatcher ( IntNative n )
+{
+   fprintf ( stderr,
+             "\n%s: Control-C or similar caught, quitting.\n",
+             progName );
+   cleanUpAndFail(1);
+}
+
+
+/*---------------------------------------------*/
+static 
+void mySIGSEGVorSIGBUScatcher ( IntNative n )
+{
+   if (opMode == OM_Z)
+      fprintf ( 
+      stderr,
+      "\n%s: Caught a SIGSEGV or SIGBUS whilst compressing.\n"
+      "\n"
+      "   Possible causes are (most likely first):\n"
+      "   (1) This computer has unreliable memory or cache hardware\n"
+      "       (a surprisingly common problem; try a different machine.)\n"
+      "   (2) A bug in the compiler used to create this executable\n"
+      "       (unlikely, if you didn't compile bzip2 yourself.)\n"
+      "   (3) A real bug in bzip2 -- I hope this should never be the case.\n"
+      "   The user's manual, Section 4.3, has more info on (1) and (2).\n"
+      "   \n"
+      "   If you suspect this is a bug in bzip2, or are unsure about (1)\n"
+      "   or (2), feel free to report it to me at: jseward@acm.org.\n"
+      "   Section 4.3 of the user's manual describes the info a useful\n"
+      "   bug report should have.  If the manual is available on your\n"
+      "   system, please try and read it before mailing me.  If you don't\n"
+      "   have the manual or can't be bothered to read it, mail me anyway.\n"
+      "\n",
+      progName );
+      else
+      fprintf ( 
+      stderr,
+      "\n%s: Caught a SIGSEGV or SIGBUS whilst decompressing.\n"
+      "\n"
+      "   Possible causes are (most likely first):\n"
+      "   (1) The compressed data is corrupted, and bzip2's usual checks\n"
+      "       failed to detect this.  Try bzip2 -tvv my_file.bz2.\n"
+      "   (2) This computer has unreliable memory or cache hardware\n"
+      "       (a surprisingly common problem; try a different machine.)\n"
+      "   (3) A bug in the compiler used to create this executable\n"
+      "       (unlikely, if you didn't compile bzip2 yourself.)\n"
+      "   (4) A real bug in bzip2 -- I hope this should never be the case.\n"
+      "   The user's manual, Section 4.3, has more info on (2) and (3).\n"
+      "   \n"
+      "   If you suspect this is a bug in bzip2, or are unsure about (2)\n"
+      "   or (3), feel free to report it to me at: jseward@acm.org.\n"
+      "   Section 4.3 of the user's manual describes the info a useful\n"
+      "   bug report should have.  If the manual is available on your\n"
+      "   system, please try and read it before mailing me.  If you don't\n"
+      "   have the manual or can't be bothered to read it, mail me anyway.\n"
+      "\n",
+      progName );
+
+   showFileNames();
+   if (opMode == OM_Z)
+      cleanUpAndFail( 3 ); else
+      { cadvise(); cleanUpAndFail( 2 ); }
+}
+
+
+/*---------------------------------------------*/
+static 
+void outOfMemory ( void )
+{
+   fprintf ( stderr,
+             "\n%s: couldn't allocate enough memory\n",
+             progName );
+   showFileNames();
+   cleanUpAndFail(1);
+}
+
+
+/*---------------------------------------------*/
+static 
+void configError ( void )
+{
+   fprintf ( stderr,
+             "bzip2: I'm not configured correctly for this platform!\n"
+             "\tI require Int32, Int16 and Char to have sizes\n"
+             "\tof 4, 2 and 1 bytes to run properly, and they don't.\n"
+             "\tProbably you can fix this by defining them correctly,\n"
+             "\tand recompiling.  Bye!\n" );
+   setExit(3);
+   exit(exitValue);
+}
+
+
+/*---------------------------------------------------*/
+/*--- The main driver machinery                   ---*/
+/*---------------------------------------------------*/
+
+/* All rather crufty.  The main problem is that input files
+   are stat()d multiple times before use.  This should be
+   cleaned up. 
+*/
+
+/*---------------------------------------------*/
+static 
+void pad ( Char *s )
+{
+   Int32 i;
+   if ( (Int32)strlen(s) >= longestFileName ) return;
+   for (i = 1; i <= longestFileName - (Int32)strlen(s); i++)
+      fprintf ( stderr, " " );
+}
+
+
+/*---------------------------------------------*/
+static 
+void copyFileName ( Char* to, Char* from ) 
+{
+   if ( strlen(from) > FILE_NAME_LEN-10 )  {
+      fprintf (
+         stderr,
+         "bzip2: file name\n`%s'\n"
+         "is suspiciously (more than %d chars) long.\n"
+         "Try using a reasonable file name instead.  Sorry! :-)\n",
+         from, FILE_NAME_LEN-10
+      );
+      setExit(1);
+      exit(exitValue);
+   }
+
+  strncpy(to,from,FILE_NAME_LEN-10);
+  to[FILE_NAME_LEN-10]='\0';
+}
+
+
+/*---------------------------------------------*/
+static 
+Bool fileExists ( Char* name )
+{
+   FILE *tmp   = fopen ( name, "rb" );
+   Bool exists = (tmp != NULL);
+   if (tmp != NULL) fclose ( tmp );
+   return exists;
+}
+
+
+/*---------------------------------------------*/
+/* Open an output file safely with O_EXCL and good permissions.
+   This avoids a race condition in versions < 1.0.2, in which
+   the file was first opened and then had its interim permissions
+   set safely.  We instead use open() to create the file with
+   the interim permissions required. (--- --- rw-).
+
+   For non-Unix platforms, if we are not worrying about
+   security issues, simple this simply behaves like fopen.
+*/
+FILE* fopen_output_safely ( Char* name, const char* mode )
+{
+#  if BZ_UNIX
+   FILE*     fp;
+   IntNative fh;
+   fh = open(name, O_WRONLY|O_CREAT|O_EXCL, S_IWUSR|S_IRUSR);
+   if (fh == -1) return NULL;
+   fp = fdopen(fh, mode);
+   if (fp == NULL) close(fh);
+   return fp;
+#  else
+   return fopen(name, mode);
+#  endif
+}
+
+
+/*---------------------------------------------*/
+/*--
+  if in doubt, return True
+--*/
+static 
+Bool notAStandardFile ( Char* name )
+{
+   IntNative      i;
+   struct MY_STAT statBuf;
+
+   i = MY_LSTAT ( name, &statBuf );
+   if (i != 0) return True;
+   if (MY_S_ISREG(statBuf.st_mode)) return False;
+   return True;
+}
+
+
+/*---------------------------------------------*/
+/*--
+  rac 11/21/98 see if file has hard links to it
+--*/
+static 
+Int32 countHardLinks ( Char* name )
+{  
+   IntNative      i;
+   struct MY_STAT statBuf;
+
+   i = MY_LSTAT ( name, &statBuf );
+   if (i != 0) return 0;
+   return (statBuf.st_nlink - 1);
+}
+
+
+/*---------------------------------------------*/
+/* Copy modification date, access date, permissions and owner from the
+   source to destination file.  We have to copy this meta-info off
+   into fileMetaInfo before starting to compress / decompress it,
+   because doing it afterwards means we get the wrong access time.
+
+   To complicate matters, in compress() and decompress() below, the
+   sequence of tests preceding the call to saveInputFileMetaInfo()
+   involves calling fileExists(), which in turn establishes its result
+   by attempting to fopen() the file, and if successful, immediately
+   fclose()ing it again.  So we have to assume that the fopen() call
+   does not cause the access time field to be updated.
+
+   Reading of the man page for stat() (man 2 stat) on RedHat 7.2 seems
+   to imply that merely doing open() will not affect the access time.
+   Therefore we merely need to hope that the C library only does
+   open() as a result of fopen(), and not any kind of read()-ahead
+   cleverness.
+
+   It sounds pretty fragile to me.  Whether this carries across
+   robustly to arbitrary Unix-like platforms (or even works robustly
+   on this one, RedHat 7.2) is unknown to me.  Nevertheless ...  
+*/
+#if BZ_UNIX
+static 
+struct MY_STAT fileMetaInfo;
+#endif
+
+static 
+void saveInputFileMetaInfo ( Char *srcName )
+{
+#  if BZ_UNIX
+   IntNative retVal;
+   /* Note use of stat here, not lstat. */
+   retVal = MY_STAT( srcName, &fileMetaInfo );
+   ERROR_IF_NOT_ZERO ( retVal );
+#  endif
+}
+
+
+static 
+void applySavedMetaInfoToOutputFile ( Char *dstName )
+{
+#  if BZ_UNIX
+   IntNative      retVal;
+   struct utimbuf uTimBuf;
+
+   uTimBuf.actime = fileMetaInfo.st_atime;
+   uTimBuf.modtime = fileMetaInfo.st_mtime;
+
+   retVal = chmod ( dstName, fileMetaInfo.st_mode );
+   ERROR_IF_NOT_ZERO ( retVal );
+
+   retVal = utime ( dstName, &uTimBuf );
+   ERROR_IF_NOT_ZERO ( retVal );
+
+   retVal = chown ( dstName, fileMetaInfo.st_uid, fileMetaInfo.st_gid );
+   /* chown() will in many cases return with EPERM, which can
+      be safely ignored.
+   */
+#  endif
+}
+
+
+/*---------------------------------------------*/
+static 
+Bool containsDubiousChars ( Char* name )
+{
+#  if BZ_UNIX
+   /* On unix, files can contain any characters and the file expansion
+    * is performed by the shell.
+    */
+   return False;
+#  else /* ! BZ_UNIX */
+   /* On non-unix (Win* platforms), wildcard characters are not allowed in 
+    * filenames.
+    */
+   for (; *name != '\0'; name++)
+      if (*name == '?' || *name == '*') return True;
+   return False;
+#  endif /* BZ_UNIX */
+}
+
+
+/*---------------------------------------------*/
+#define BZ_N_SUFFIX_PAIRS 4
+
+Char* zSuffix[BZ_N_SUFFIX_PAIRS] 
+   = { ".bz2", ".bz", ".tbz2", ".tbz" };
+Char* unzSuffix[BZ_N_SUFFIX_PAIRS] 
+   = { "", "", ".tar", ".tar" };
+
+static 
+Bool hasSuffix ( Char* s, Char* suffix )
+{
+   Int32 ns = strlen(s);
+   Int32 nx = strlen(suffix);
+   if (ns < nx) return False;
+   if (strcmp(s + ns - nx, suffix) == 0) return True;
+   return False;
+}
+
+static 
+Bool mapSuffix ( Char* name, 
+                 Char* oldSuffix, Char* newSuffix )
+{
+   if (!hasSuffix(name,oldSuffix)) return False;
+   name[strlen(name)-strlen(oldSuffix)] = 0;
+   strcat ( name, newSuffix );
+   return True;
+}
+
+
+/*---------------------------------------------*/
+static 
+void compress ( Char *name )
+{
+   FILE  *inStr;
+   FILE  *outStr;
+   Int32 n, i;
+   struct MY_STAT statBuf;
+
+   deleteOutputOnInterrupt = False;
+
+   if (name == NULL && srcMode != SM_I2O)
+      panic ( "compress: bad modes\n" );
+
+   switch (srcMode) {
+      case SM_I2O: 
+         copyFileName ( inName, "(stdin)" );
+         copyFileName ( outName, "(stdout)" ); 
+         break;
+      case SM_F2F: 
+         copyFileName ( inName, name );
+         copyFileName ( outName, name );
+         strcat ( outName, ".bz2" ); 
+         break;
+      case SM_F2O: 
+         copyFileName ( inName, name );
+         copyFileName ( outName, "(stdout)" ); 
+         break;
+   }
+
+   if ( srcMode != SM_I2O && containsDubiousChars ( inName ) ) {
+      if (noisy)
+      fprintf ( stderr, "%s: There are no files matching `%s'.\n",
+                progName, inName );
+      setExit(1);
+      return;
+   }
+   if ( srcMode != SM_I2O && !fileExists ( inName ) ) {
+      fprintf ( stderr, "%s: Can't open input file %s: %s.\n",
+                progName, inName, strerror(errno) );
+      setExit(1);
+      return;
+   }
+   for (i = 0; i < BZ_N_SUFFIX_PAIRS; i++) {
+      if (hasSuffix(inName, zSuffix[i])) {
+         if (noisy)
+         fprintf ( stderr, 
+                   "%s: Input file %s already has %s suffix.\n",
+                   progName, inName, zSuffix[i] );
+         setExit(1);
+         return;
+      }
+   }
+   if ( srcMode == SM_F2F || srcMode == SM_F2O ) {
+      MY_STAT(inName, &statBuf);
+      if ( MY_S_ISDIR(statBuf.st_mode) ) {
+         fprintf( stderr,
+                  "%s: Input file %s is a directory.\n",
+                  progName,inName);
+         setExit(1);
+         return;
+      }
+   }
+   if ( srcMode == SM_F2F && !forceOverwrite && notAStandardFile ( inName )) {
+      if (noisy)
+      fprintf ( stderr, "%s: Input file %s is not a normal file.\n",
+                progName, inName );
+      setExit(1);
+      return;
+   }
+   if ( srcMode == SM_F2F && fileExists ( outName ) ) {
+      if (forceOverwrite) {
+	 remove(outName);
+      } else {
+	 fprintf ( stderr, "%s: Output file %s already exists.\n",
+		   progName, outName );
+	 setExit(1);
+	 return;
+      }
+   }
+   if ( srcMode == SM_F2F && !forceOverwrite &&
+        (n=countHardLinks ( inName )) > 0) {
+      fprintf ( stderr, "%s: Input file %s has %d other link%s.\n",
+                progName, inName, n, n > 1 ? "s" : "" );
+      setExit(1);
+      return;
+   }
+
+   if ( srcMode == SM_F2F ) {
+      /* Save the file's meta-info before we open it.  Doing it later
+         means we mess up the access times. */
+      saveInputFileMetaInfo ( inName );
+   }
+
+   switch ( srcMode ) {
+
+      case SM_I2O:
+         inStr = stdin;
+         outStr = stdout;
+         if ( isatty ( fileno ( stdout ) ) ) {
+            fprintf ( stderr,
+                      "%s: I won't write compressed data to a terminal.\n",
+                      progName );
+            fprintf ( stderr, "%s: For help, type: `%s --help'.\n",
+                              progName, progName );
+            setExit(1);
+            return;
+         };
+         break;
+
+      case SM_F2O:
+         inStr = fopen ( inName, "rb" );
+         outStr = stdout;
+         if ( isatty ( fileno ( stdout ) ) ) {
+            fprintf ( stderr,
+                      "%s: I won't write compressed data to a terminal.\n",
+                      progName );
+            fprintf ( stderr, "%s: For help, type: `%s --help'.\n",
+                              progName, progName );
+            if ( inStr != NULL ) fclose ( inStr );
+            setExit(1);
+            return;
+         };
+         if ( inStr == NULL ) {
+            fprintf ( stderr, "%s: Can't open input file %s: %s.\n",
+                      progName, inName, strerror(errno) );
+            setExit(1);
+            return;
+         };
+         break;
+
+      case SM_F2F:
+         inStr = fopen ( inName, "rb" );
+         outStr = fopen_output_safely ( outName, "wb" );
+         if ( outStr == NULL) {
+            fprintf ( stderr, "%s: Can't create output file %s: %s.\n",
+                      progName, outName, strerror(errno) );
+            if ( inStr != NULL ) fclose ( inStr );
+            setExit(1);
+            return;
+         }
+         if ( inStr == NULL ) {
+            fprintf ( stderr, "%s: Can't open input file %s: %s.\n",
+                      progName, inName, strerror(errno) );
+            if ( outStr != NULL ) fclose ( outStr );
+            setExit(1);
+            return;
+         };
+         break;
+
+      default:
+         panic ( "compress: bad srcMode" );
+         break;
+   }
+
+   if (verbosity >= 1) {
+      fprintf ( stderr,  "  %s: ", inName );
+      pad ( inName );
+      fflush ( stderr );
+   }
+
+   /*--- Now the input and output handles are sane.  Do the Biz. ---*/
+   outputHandleJustInCase = outStr;
+   deleteOutputOnInterrupt = True;
+   compressStream ( inStr, outStr );
+   outputHandleJustInCase = NULL;
+
+   /*--- If there was an I/O error, we won't get here. ---*/
+   if ( srcMode == SM_F2F ) {
+      applySavedMetaInfoToOutputFile ( outName );
+      deleteOutputOnInterrupt = False;
+      if ( !keepInputFiles ) {
+         IntNative retVal = remove ( inName );
+         ERROR_IF_NOT_ZERO ( retVal );
+      }
+   }
+
+   deleteOutputOnInterrupt = False;
+}
+
+
+/*---------------------------------------------*/
+static 
+void uncompress ( Char *name )
+{
+   FILE  *inStr;
+   FILE  *outStr;
+   Int32 n, i;
+   Bool  magicNumberOK;
+   Bool  cantGuess;
+   struct MY_STAT statBuf;
+
+   deleteOutputOnInterrupt = False;
+
+   if (name == NULL && srcMode != SM_I2O)
+      panic ( "uncompress: bad modes\n" );
+
+   cantGuess = False;
+   switch (srcMode) {
+      case SM_I2O: 
+         copyFileName ( inName, "(stdin)" );
+         copyFileName ( outName, "(stdout)" ); 
+         break;
+      case SM_F2F: 
+         copyFileName ( inName, name );
+         copyFileName ( outName, name );
+         for (i = 0; i < BZ_N_SUFFIX_PAIRS; i++)
+            if (mapSuffix(outName,zSuffix[i],unzSuffix[i]))
+               goto zzz; 
+         cantGuess = True;
+         strcat ( outName, ".out" );
+         break;
+      case SM_F2O: 
+         copyFileName ( inName, name );
+         copyFileName ( outName, "(stdout)" ); 
+         break;
+   }
+
+   zzz:
+   if ( srcMode != SM_I2O && containsDubiousChars ( inName ) ) {
+      if (noisy)
+      fprintf ( stderr, "%s: There are no files matching `%s'.\n",
+                progName, inName );
+      setExit(1);
+      return;
+   }
+   if ( srcMode != SM_I2O && !fileExists ( inName ) ) {
+      fprintf ( stderr, "%s: Can't open input file %s: %s.\n",
+                progName, inName, strerror(errno) );
+      setExit(1);
+      return;
+   }
+   if ( srcMode == SM_F2F || srcMode == SM_F2O ) {
+      MY_STAT(inName, &statBuf);
+      if ( MY_S_ISDIR(statBuf.st_mode) ) {
+         fprintf( stderr,
+                  "%s: Input file %s is a directory.\n",
+                  progName,inName);
+         setExit(1);
+         return;
+      }
+   }
+   if ( srcMode == SM_F2F && !forceOverwrite && notAStandardFile ( inName )) {
+      if (noisy)
+      fprintf ( stderr, "%s: Input file %s is not a normal file.\n",
+                progName, inName );
+      setExit(1);
+      return;
+   }
+   if ( /* srcMode == SM_F2F implied && */ cantGuess ) {
+      if (noisy)
+      fprintf ( stderr, 
+                "%s: Can't guess original name for %s -- using %s\n",
+                progName, inName, outName );
+      /* just a warning, no return */
+   }   
+   if ( srcMode == SM_F2F && fileExists ( outName ) ) {
+      if (forceOverwrite) {
+	remove(outName);
+      } else {
+        fprintf ( stderr, "%s: Output file %s already exists.\n",
+                  progName, outName );
+        setExit(1);
+        return;
+      }
+   }
+   if ( srcMode == SM_F2F && !forceOverwrite &&
+        (n=countHardLinks ( inName ) ) > 0) {
+      fprintf ( stderr, "%s: Input file %s has %d other link%s.\n",
+                progName, inName, n, n > 1 ? "s" : "" );
+      setExit(1);
+      return;
+   }
+
+   if ( srcMode == SM_F2F ) {
+      /* Save the file's meta-info before we open it.  Doing it later
+         means we mess up the access times. */
+      saveInputFileMetaInfo ( inName );
+   }
+
+   switch ( srcMode ) {
+
+      case SM_I2O:
+         inStr = stdin;
+         outStr = stdout;
+         if ( isatty ( fileno ( stdin ) ) ) {
+            fprintf ( stderr,
+                      "%s: I won't read compressed data from a terminal.\n",
+                      progName );
+            fprintf ( stderr, "%s: For help, type: `%s --help'.\n",
+                              progName, progName );
+            setExit(1);
+            return;
+         };
+         break;
+
+      case SM_F2O:
+         inStr = fopen ( inName, "rb" );
+         outStr = stdout;
+         if ( inStr == NULL ) {
+            fprintf ( stderr, "%s: Can't open input file %s:%s.\n",
+                      progName, inName, strerror(errno) );
+            if ( inStr != NULL ) fclose ( inStr );
+            setExit(1);
+            return;
+         };
+         break;
+
+      case SM_F2F:
+         inStr = fopen ( inName, "rb" );
+         outStr = fopen_output_safely ( outName, "wb" );
+         if ( outStr == NULL) {
+            fprintf ( stderr, "%s: Can't create output file %s: %s.\n",
+                      progName, outName, strerror(errno) );
+            if ( inStr != NULL ) fclose ( inStr );
+            setExit(1);
+            return;
+         }
+         if ( inStr == NULL ) {
+            fprintf ( stderr, "%s: Can't open input file %s: %s.\n",
+                      progName, inName, strerror(errno) );
+            if ( outStr != NULL ) fclose ( outStr );
+            setExit(1);
+            return;
+         };
+         break;
+
+      default:
+         panic ( "uncompress: bad srcMode" );
+         break;
+   }
+
+   if (verbosity >= 1) {
+      fprintf ( stderr, "  %s: ", inName );
+      pad ( inName );
+      fflush ( stderr );
+   }
+
+   /*--- Now the input and output handles are sane.  Do the Biz. ---*/
+   outputHandleJustInCase = outStr;
+   deleteOutputOnInterrupt = True;
+   magicNumberOK = uncompressStream ( inStr, outStr );
+   outputHandleJustInCase = NULL;
+
+   /*--- If there was an I/O error, we won't get here. ---*/
+   if ( magicNumberOK ) {
+      if ( srcMode == SM_F2F ) {
+         applySavedMetaInfoToOutputFile ( outName );
+         deleteOutputOnInterrupt = False;
+         if ( !keepInputFiles ) {
+            IntNative retVal = remove ( inName );
+            ERROR_IF_NOT_ZERO ( retVal );
+         }
+      }
+   } else {
+      unzFailsExist = True;
+      deleteOutputOnInterrupt = False;
+      if ( srcMode == SM_F2F ) {
+         IntNative retVal = remove ( outName );
+         ERROR_IF_NOT_ZERO ( retVal );
+      }
+   }
+   deleteOutputOnInterrupt = False;
+
+   if ( magicNumberOK ) {
+      if (verbosity >= 1)
+         fprintf ( stderr, "done\n" );
+   } else {
+      setExit(2);
+      if (verbosity >= 1)
+         fprintf ( stderr, "not a bzip2 file.\n" ); else
+         fprintf ( stderr,
+                   "%s: %s is not a bzip2 file.\n",
+                   progName, inName );
+   }
+
+}
+
+
+/*---------------------------------------------*/
+static 
+void testf ( Char *name )
+{
+   FILE *inStr;
+   Bool allOK;
+   struct MY_STAT statBuf;
+
+   deleteOutputOnInterrupt = False;
+
+   if (name == NULL && srcMode != SM_I2O)
+      panic ( "testf: bad modes\n" );
+
+   copyFileName ( outName, "(none)" );
+   switch (srcMode) {
+      case SM_I2O: copyFileName ( inName, "(stdin)" ); break;
+      case SM_F2F: copyFileName ( inName, name ); break;
+      case SM_F2O: copyFileName ( inName, name ); break;
+   }
+
+   if ( srcMode != SM_I2O && containsDubiousChars ( inName ) ) {
+      if (noisy)
+      fprintf ( stderr, "%s: There are no files matching `%s'.\n",
+                progName, inName );
+      setExit(1);
+      return;
+   }
+   if ( srcMode != SM_I2O && !fileExists ( inName ) ) {
+      fprintf ( stderr, "%s: Can't open input %s: %s.\n",
+                progName, inName, strerror(errno) );
+      setExit(1);
+      return;
+   }
+   if ( srcMode != SM_I2O ) {
+      MY_STAT(inName, &statBuf);
+      if ( MY_S_ISDIR(statBuf.st_mode) ) {
+         fprintf( stderr,
+                  "%s: Input file %s is a directory.\n",
+                  progName,inName);
+         setExit(1);
+         return;
+      }
+   }
+
+   switch ( srcMode ) {
+
+      case SM_I2O:
+         if ( isatty ( fileno ( stdin ) ) ) {
+            fprintf ( stderr,
+                      "%s: I won't read compressed data from a terminal.\n",
+                      progName );
+            fprintf ( stderr, "%s: For help, type: `%s --help'.\n",
+                              progName, progName );
+            setExit(1);
+            return;
+         };
+         inStr = stdin;
+         break;
+
+      case SM_F2O: case SM_F2F:
+         inStr = fopen ( inName, "rb" );
+         if ( inStr == NULL ) {
+            fprintf ( stderr, "%s: Can't open input file %s:%s.\n",
+                      progName, inName, strerror(errno) );
+            setExit(1);
+            return;
+         };
+         break;
+
+      default:
+         panic ( "testf: bad srcMode" );
+         break;
+   }
+
+   if (verbosity >= 1) {
+      fprintf ( stderr, "  %s: ", inName );
+      pad ( inName );
+      fflush ( stderr );
+   }
+
+   /*--- Now the input handle is sane.  Do the Biz. ---*/
+   outputHandleJustInCase = NULL;
+   allOK = testStream ( inStr );
+
+   if (allOK && verbosity >= 1) fprintf ( stderr, "ok\n" );
+   if (!allOK) testFailsExist = True;
+}
+
+
+/*---------------------------------------------*/
+static 
+void license ( void )
+{
+   fprintf ( stderr,
+
+    "bzip2, a block-sorting file compressor.  "
+    "Version %s.\n"
+    "   \n"
+    "   Copyright (C) 1996-2002 by Julian Seward.\n"
+    "   \n"
+    "   This program is free software; you can redistribute it and/or modify\n"
+    "   it under the terms set out in the LICENSE file, which is included\n"
+    "   in the bzip2-1.0 source distribution.\n"
+    "   \n"
+    "   This program is distributed in the hope that it will be useful,\n"
+    "   but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+    "   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
+    "   LICENSE file for more details.\n"
+    "   \n",
+    BZ2_bzlibVersion()
+   );
+}
+
+
+/*---------------------------------------------*/
+static 
+void usage ( Char *fullProgName )
+{
+   fprintf (
+      stderr,
+      "bzip2, a block-sorting file compressor.  "
+      "Version %s.\n"
+      "\n   usage: %s [flags and input files in any order]\n"
+      "\n"
+      "   -h --help           print this message\n"
+      "   -d --decompress     force decompression\n"
+      "   -z --compress       force compression\n"
+      "   -k --keep           keep (don't delete) input files\n"
+      "   -f --force          overwrite existing output files\n"
+      "   -t --test           test compressed file integrity\n"
+      "   -c --stdout         output to standard out\n"
+      "   -q --quiet          suppress noncritical error messages\n"
+      "   -v --verbose        be verbose (a 2nd -v gives more)\n"
+      "   -L --license        display software version & license\n"
+      "   -V --version        display software version & license\n"
+      "   -s --small          use less memory (at most 2500k)\n"
+      "   -1 .. -9            set block size to 100k .. 900k\n"
+      "   --fast              alias for -1\n"
+      "   --best              alias for -9\n"
+      "\n"
+      "   If invoked as `bzip2', default action is to compress.\n"
+      "              as `bunzip2',  default action is to decompress.\n"
+      "              as `bzcat', default action is to decompress to stdout.\n"
+      "\n"
+      "   If no file names are given, bzip2 compresses or decompresses\n"
+      "   from standard input to standard output.  You can combine\n"
+      "   short flags, so `-v -4' means the same as -v4 or -4v, &c.\n"
+#     if BZ_UNIX
+      "\n"
+#     endif
+      ,
+
+      BZ2_bzlibVersion(),
+      fullProgName
+   );
+}
+
+
+/*---------------------------------------------*/
+static 
+void redundant ( Char* flag )
+{
+   fprintf ( 
+      stderr, 
+      "%s: %s is redundant in versions 0.9.5 and above\n",
+      progName, flag );
+}
+
+
+/*---------------------------------------------*/
+/*--
+  All the garbage from here to main() is purely to
+  implement a linked list of command-line arguments,
+  into which main() copies argv[1 .. argc-1].
+
+  The purpose of this exercise is to facilitate 
+  the expansion of wildcard characters * and ? in 
+  filenames for OSs which don't know how to do it
+  themselves, like MSDOS, Windows 95 and NT.
+
+  The actual Dirty Work is done by the platform-
+  specific macro APPEND_FILESPEC.
+--*/
+
+typedef
+   struct zzzz {
+      Char        *name;
+      struct zzzz *link;
+   }
+   Cell;
+
+
+/*---------------------------------------------*/
+static 
+void *myMalloc ( Int32 n )
+{
+   void* p;
+
+   p = malloc ( (size_t)n );
+   if (p == NULL) outOfMemory ();
+   return p;
+}
+
+
+/*---------------------------------------------*/
+static 
+Cell *mkCell ( void )
+{
+   Cell *c;
+
+   c = (Cell*) myMalloc ( sizeof ( Cell ) );
+   c->name = NULL;
+   c->link = NULL;
+   return c;
+}
+
+
+/*---------------------------------------------*/
+static 
+Cell *snocString ( Cell *root, Char *name )
+{
+   if (root == NULL) {
+      Cell *tmp = mkCell();
+      tmp->name = (Char*) myMalloc ( 5 + strlen(name) );
+      strcpy ( tmp->name, name );
+      return tmp;
+   } else {
+      Cell *tmp = root;
+      while (tmp->link != NULL) tmp = tmp->link;
+      tmp->link = snocString ( tmp->link, name );
+      return root;
+   }
+}
+
+
+/*---------------------------------------------*/
+static 
+void addFlagsFromEnvVar ( Cell** argList, Char* varName ) 
+{
+   Int32 i, j, k;
+   Char *envbase, *p;
+
+   envbase = getenv(varName);
+   if (envbase != NULL) {
+      p = envbase;
+      i = 0;
+      while (True) {
+         if (p[i] == 0) break;
+         p += i;
+         i = 0;
+         while (isspace((Int32)(p[0]))) p++;
+         while (p[i] != 0 && !isspace((Int32)(p[i]))) i++;
+         if (i > 0) {
+            k = i; if (k > FILE_NAME_LEN-10) k = FILE_NAME_LEN-10;
+            for (j = 0; j < k; j++) tmpName[j] = p[j];
+            tmpName[k] = 0;
+            APPEND_FLAG(*argList, tmpName);
+         }
+      }
+   }
+}
+
+
+/*---------------------------------------------*/
+#define ISFLAG(s) (strcmp(aa->name, (s))==0)
+
+IntNative main ( IntNative argc, Char *argv[] )
+{
+   Int32  i, j;
+   Char   *tmp;
+   Cell   *argList;
+   Cell   *aa;
+   Bool   decode;
+
+   /*-- Be really really really paranoid :-) --*/
+   if (sizeof(Int32) != 4 || sizeof(UInt32) != 4  ||
+       sizeof(Int16) != 2 || sizeof(UInt16) != 2  ||
+       sizeof(Char)  != 1 || sizeof(UChar)  != 1)
+      configError();
+
+   /*-- Initialise --*/
+   outputHandleJustInCase  = NULL;
+   smallMode               = False;
+   keepInputFiles          = False;
+   forceOverwrite          = False;
+   noisy                   = True;
+   verbosity               = 0;
+   blockSize100k           = 9;
+   testFailsExist          = False;
+   unzFailsExist           = False;
+   numFileNames            = 0;
+   numFilesProcessed       = 0;
+   workFactor              = 30;
+   deleteOutputOnInterrupt = False;
+   exitValue               = 0;
+   i = j = 0; /* avoid bogus warning from egcs-1.1.X */
+
+   /*-- Set up signal handlers for mem access errors --*/
+   signal (SIGSEGV, mySIGSEGVorSIGBUScatcher);
+#  if BZ_UNIX
+#  ifndef __DJGPP__
+   signal (SIGBUS,  mySIGSEGVorSIGBUScatcher);
+#  endif
+#  endif
+
+   copyFileName ( inName,  "(none)" );
+   copyFileName ( outName, "(none)" );
+
+   copyFileName ( progNameReally, argv[0] );
+   progName = &progNameReally[0];
+   for (tmp = &progNameReally[0]; *tmp != '\0'; tmp++)
+      if (*tmp == PATH_SEP) progName = tmp + 1;
+
+
+   /*-- Copy flags from env var BZIP2, and 
+        expand filename wildcards in arg list.
+   --*/
+   argList = NULL;
+   addFlagsFromEnvVar ( &argList,  "BZIP2" );
+   addFlagsFromEnvVar ( &argList,  "BZIP" );
+   for (i = 1; i <= argc-1; i++)
+      APPEND_FILESPEC(argList, argv[i]);
+
+
+   /*-- Find the length of the longest filename --*/
+   longestFileName = 7;
+   numFileNames    = 0;
+   decode          = True;
+   for (aa = argList; aa != NULL; aa = aa->link) {
+      if (ISFLAG("--")) { decode = False; continue; }
+      if (aa->name[0] == '-' && decode) continue;
+      numFileNames++;
+      if (longestFileName < (Int32)strlen(aa->name) )
+         longestFileName = (Int32)strlen(aa->name);
+   }
+
+
+   /*-- Determine source modes; flag handling may change this too. --*/
+   if (numFileNames == 0)
+      srcMode = SM_I2O; else srcMode = SM_F2F;
+
+
+   /*-- Determine what to do (compress/uncompress/test/cat). --*/
+   /*-- Note that subsequent flag handling may change this. --*/
+   opMode = OM_Z;
+
+   if ( (strstr ( progName, "unzip" ) != 0) ||
+        (strstr ( progName, "UNZIP" ) != 0) )
+      opMode = OM_UNZ;
+
+   if ( (strstr ( progName, "z2cat" ) != 0) ||
+        (strstr ( progName, "Z2CAT" ) != 0) ||
+        (strstr ( progName, "zcat" ) != 0)  ||
+        (strstr ( progName, "ZCAT" ) != 0) )  {
+      opMode = OM_UNZ;
+      srcMode = (numFileNames == 0) ? SM_I2O : SM_F2O;
+   }
+
+
+   /*-- Look at the flags. --*/
+   for (aa = argList; aa != NULL; aa = aa->link) {
+      if (ISFLAG("--")) break;
+      if (aa->name[0] == '-' && aa->name[1] != '-') {
+         for (j = 1; aa->name[j] != '\0'; j++) {
+            switch (aa->name[j]) {
+               case 'c': srcMode          = SM_F2O; break;
+               case 'd': opMode           = OM_UNZ; break;
+               case 'z': opMode           = OM_Z; break;
+               case 'f': forceOverwrite   = True; break;
+               case 't': opMode           = OM_TEST; break;
+               case 'k': keepInputFiles   = True; break;
+               case 's': smallMode        = True; break;
+               case 'q': noisy            = False; break;
+               case '1': blockSize100k    = 1; break;
+               case '2': blockSize100k    = 2; break;
+               case '3': blockSize100k    = 3; break;
+               case '4': blockSize100k    = 4; break;
+               case '5': blockSize100k    = 5; break;
+               case '6': blockSize100k    = 6; break;
+               case '7': blockSize100k    = 7; break;
+               case '8': blockSize100k    = 8; break;
+               case '9': blockSize100k    = 9; break;
+               case 'V':
+               case 'L': license();            break;
+               case 'v': verbosity++; break;
+               case 'h': usage ( progName );
+                         exit ( 0 );
+                         break;
+               default:  fprintf ( stderr, "%s: Bad flag `%s'\n",
+                                   progName, aa->name );
+                         usage ( progName );
+                         exit ( 1 );
+                         break;
+            }
+         }
+      }
+   }
+   
+   /*-- And again ... --*/
+   for (aa = argList; aa != NULL; aa = aa->link) {
+      if (ISFLAG("--")) break;
+      if (ISFLAG("--stdout"))            srcMode          = SM_F2O;  else
+      if (ISFLAG("--decompress"))        opMode           = OM_UNZ;  else
+      if (ISFLAG("--compress"))          opMode           = OM_Z;    else
+      if (ISFLAG("--force"))             forceOverwrite   = True;    else
+      if (ISFLAG("--test"))              opMode           = OM_TEST; else
+      if (ISFLAG("--keep"))              keepInputFiles   = True;    else
+      if (ISFLAG("--small"))             smallMode        = True;    else
+      if (ISFLAG("--quiet"))             noisy            = False;   else
+      if (ISFLAG("--version"))           license();                  else
+      if (ISFLAG("--license"))           license();                  else
+      if (ISFLAG("--exponential"))       workFactor = 1;             else 
+      if (ISFLAG("--repetitive-best"))   redundant(aa->name);        else
+      if (ISFLAG("--repetitive-fast"))   redundant(aa->name);        else
+      if (ISFLAG("--fast"))              blockSize100k = 1;          else
+      if (ISFLAG("--best"))              blockSize100k = 9;          else
+      if (ISFLAG("--verbose"))           verbosity++;                else
+      if (ISFLAG("--help"))              { usage ( progName ); exit ( 0 ); }
+         else
+         if (strncmp ( aa->name, "--", 2) == 0) {
+            fprintf ( stderr, "%s: Bad flag `%s'\n", progName, aa->name );
+            usage ( progName );
+            exit ( 1 );
+         }
+   }
+
+   if (verbosity > 4) verbosity = 4;
+   if (opMode == OM_Z && smallMode && blockSize100k > 2) 
+      blockSize100k = 2;
+
+   if (opMode == OM_TEST && srcMode == SM_F2O) {
+      fprintf ( stderr, "%s: -c and -t cannot be used together.\n",
+                progName );
+      exit ( 1 );
+   }
+
+   if (srcMode == SM_F2O && numFileNames == 0)
+      srcMode = SM_I2O;
+
+   if (opMode != OM_Z) blockSize100k = 0;
+
+   if (srcMode == SM_F2F) {
+      signal (SIGINT,  mySignalCatcher);
+      signal (SIGTERM, mySignalCatcher);
+#     if BZ_UNIX
+      signal (SIGHUP,  mySignalCatcher);
+#     endif
+   }
+
+   if (opMode == OM_Z) {
+     if (srcMode == SM_I2O) {
+        compress ( NULL );
+     } else {
+        decode = True;
+        for (aa = argList; aa != NULL; aa = aa->link) {
+           if (ISFLAG("--")) { decode = False; continue; }
+           if (aa->name[0] == '-' && decode) continue;
+           numFilesProcessed++;
+           compress ( aa->name );
+        }
+     }
+   } 
+   else
+
+   if (opMode == OM_UNZ) {
+      unzFailsExist = False;
+      if (srcMode == SM_I2O) {
+         uncompress ( NULL );
+      } else {
+         decode = True;
+         for (aa = argList; aa != NULL; aa = aa->link) {
+            if (ISFLAG("--")) { decode = False; continue; }
+            if (aa->name[0] == '-' && decode) continue;
+            numFilesProcessed++;
+            uncompress ( aa->name );
+         }      
+      }
+      if (unzFailsExist) { 
+         setExit(2); 
+         exit(exitValue);
+      }
+   } 
+
+   else {
+      testFailsExist = False;
+      if (srcMode == SM_I2O) {
+         testf ( NULL );
+      } else {
+         decode = True;
+         for (aa = argList; aa != NULL; aa = aa->link) {
+	    if (ISFLAG("--")) { decode = False; continue; }
+            if (aa->name[0] == '-' && decode) continue;
+            numFilesProcessed++;
+            testf ( aa->name );
+	 }
+      }
+      if (testFailsExist && noisy) {
+         fprintf ( stderr,
+           "\n"
+           "You can use the `bzip2recover' program to attempt to recover\n"
+           "data from undamaged sections of corrupted files.\n\n"
+         );
+         setExit(2);
+         exit(exitValue);
+      }
+   }
+
+   /* Free the argument list memory to mollify leak detectors 
+      (eg) Purify, Checker.  Serves no other useful purpose.
+   */
+   aa = argList;
+   while (aa != NULL) {
+      Cell* aa2 = aa->link;
+      if (aa->name != NULL) free(aa->name);
+      free(aa);
+      aa = aa2;
+   }
+
+   return exitValue;
+}
+
+
+/*-----------------------------------------------------------*/
+/*--- end                                         bzip2.c ---*/
+/*-----------------------------------------------------------*/

+ 6 - 35
LibOS/shim/test/apps/lighttpd/Makefile

@@ -3,6 +3,7 @@ SRCDIR = lighttpd-1.4.30
 HOST = $(firstword $(shell ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' -m 1 | cut -d: -f2))
 PORT = 8000
 CORES = 4
+THREADS = 25
 
 conf_files = lighttpd-server.conf lighttpd.conf lighttpd-multithreaded.conf
 
@@ -40,7 +41,7 @@ lighttpd.conf:
 lighttpd-multithreaded.conf:
 	rm -rf $@
 	echo "include \"lighttpd-server.conf\""                >> $@
-	echo "server.max-worker          = $(CORES)"           >> $@
+	echo "server.max-worker          = $(THREADS)"         >> $@
 	echo "include \"lighttpd-generic.conf\""               >> $@
 
 start-native-server:
@@ -52,11 +53,11 @@ start-multithreaded-native-server:
                 $(if $(CONF),$(CONF),lighttpd-multithreaded.conf)
 
 start-graphene-server:
-	$(PREFIX) ./lighttpd.manifest$(if $(SGX_RUN),.sgx,) -D -m /lighttpd -f \
+	$(PREFIX) ./lighttpd.manifest -D -m /lighttpd -f \
 		$(if $(CONF),$(CONF),lighttpd.conf)
 
 start-multithreaded-graphene-server:
-	$(PREFIX) ../pal_loader build/sbin/lighttpd -D -m /lighttpd -f \
+	$(PREFIX) ./lighttpd.manifest -D -m /lighttpd -f \
                 $(if $(CONF),$(CONF),lighttpd-multithreaded.conf)
 
 
@@ -70,41 +71,11 @@ random-data = $(foreach n,1 2 3 4 5 6 7 8 9 10,2K.$n.html) \
 test-data = html/oscar-web html/oscar-web-static html/random \
 	    $(addprefix html/random/,$(random-data))
 
-try-download:
-	echo "download random files ($(TESTNAME)):" >> result; \
-	time -a -o result -p wget $(addprefix http://$(HOST):$(PORT)/random/,$(random-data)) -O wget-out; \
-	echo "download random files again ($(TESTNAME)):" >> result; \
-	time -a -o result -p wget $(addprefix http://$(HOST):$(PORT)/random/,$(random-data)) -O wget-out; \
-	echo "download static oscar-web ($(TESTNAME)):" >> result; \
-	time -a -o result -p wget -r http://$(HOST):$(PORT)/oscar-web-static/index.php -O wget-out;
-
-test-native: $(test-data)
-	$(MAKE) start-native-server & serverid=$$!; sleep 1; \
-	$(MAKE) try-download TESTNAME=native; \
-	rm wget-out; kill $$serverid
-
-test-multithreaded-native: $(test-data)
-	$(MAKE) start-multithreaded-native-server & serverid=$$!; sleep 1; \
-	$(MAKE) try-download TESTNAME=native; \
-        rm wget-out; kill $$serverid
-
-
-test-graphene: $(test-data)
-	$(MAKE) start-graphene-server & serverid=$$!; sleep 1; \
-	$(MAKE) try-download TESTNAME=graphene; \
-	rm wget-out; kill $$serverid
-
-test-multithreaded-graphene: $(test-data)
-	$(MAKE) start-multithreaded-graphene-server & serverid=$$!; sleep 1; \
-        $(MAKE) try-download TESTNAME=graphene; \
-        rm wget-out; kill $$serverid
-
-
 html/%: ../web-data/%.tar.gz
-	[ -d "$@" ] || (cd html && tar -xzf ../$^)
+	[ -d "$@" ] || (cd $(dir $@) && tar -xzf ../$^)
 
 html/random:
-	mkdir -p html/random
+	mkdir -p $@
 
 html/random/%.html: html/random
 	dd if=/dev/urandom of=$@ count=1 bs=$(basename $(basename $(notdir $@)))

+ 1 - 1
LibOS/shim/test/apps/lighttpd/lighttpd-angel.manifest.template

@@ -32,7 +32,7 @@ sys.stack.size = 256K
 sys.brk.size = 4M
 glibc.heap_size = 16M
 
-sgx.thread_num = 8
+sgx.thread_num = 28
 
 sgx.trusted_files.ld = file:$(LIBCDIR)/ld-linux-x86-64.so.2
 sgx.trusted_files.libc = file:$(LIBCDIR)/libc.so.6

+ 1 - 1
LibOS/shim/test/apps/lighttpd/lighttpd.manifest.template

@@ -32,7 +32,7 @@ sys.stack.size = 256K
 sys.brk.size = 4M
 glibc.heap_size = 16M
 
-sgx.thread_num = 8
+sgx.thread_num = 28
 
 sgx.trusted_files.ld = file:$(LIBCDIR)/ld-linux-x86-64.so.2
 sgx.trusted_files.libc = file:$(LIBCDIR)/libc.so.6

+ 1 - 4
LibOS/shim/test/apps/lmbench/Makefile.lmbench

@@ -7,13 +7,10 @@ lmbench_tests = lat_syscall lat_connect lat_fcntl \
 		bw_pipe bw_file_rd bw_mmap_rd lmhttp lmdd sh.manifest
 
 exec_target = $(lmbench_tests)
-target = $(lmbench_tests) $(manifests) pal_loader
+target = $(manifests)
 
 level = ../../../../../
 include ../../../../../Makefile
 
 $(addsuffix .template,$(manifests)): %: ../../../%
 	ln -sf $< $@
-
-pal_loader: ../../../../../pal_loader
-	ln -sf $< $@

+ 6 - 0
LibOS/shim/test/apps/lmbench/manifest.template

@@ -40,5 +40,11 @@ sgx.allowed_files.tmp1 = file:/tmp
 sgx.allowed_files.tmp2 = file:/var/tmp
 sgx.allowed_files.inc = file:/usr/include/x86_64-linux-gnu/sys/types.h
 
+sgx.trusted_files.test1 = file:random.64K
+sgx.trusted_files.test2 = file:random.256K
+sgx.trusted_files.test3 = file:random.1M
+sgx.trusted_files.test4 = file:random.4M
+sgx.trusted_files.test5 = file:random.16M
+
 sgx.trusted_children.hello = file:hello.sig
 sgx.trusted_children.sh = file:sh.sig

+ 8 - 2
LibOS/shim/test/apps/ltp/Makefile

@@ -2,7 +2,7 @@ SRCDIR = ltp-master
 BUILDDIR = opt/ltp
 TESTCASEDIR = $(BUILDDIR)/testcases/bin
 
-target = $(BUILDDIR)/bin/run_in_graphene.awk $(TESTCASEDIR)/pal_loader $(TESTCASEDIR)/manifest
+target = $(BUILDDIR)/bin/run_in_graphene.awk $(TESTCASEDIR)/pal_loader build-manifest
 exec_target =
 
 clean-extra = clean-build
@@ -27,10 +27,16 @@ $(BUILDDIR)/bin/run_in_graphene.awk: run_in_graphene.awk $(BUILDDIR)/runltp
 	cp -f $< $@
 
 $(TESTCASEDIR)/pal_loader: $(BUILDDIR)/runltp
-	ln -sf $(call relative-to,$(dir $@),../../pal_loader) $@
+	ln -sf $(call relative-to,$(dir $@),../../../Runtime/pal_loader) $@
+
+build-manifest: $(TESTCASEDIR)/manifest.template $(TESTCASEDIR)/Makefile
+	cd $(TESTCASEDIR) && make
 
 $(TESTCASEDIR)/manifest.template: manifest.template
 	cp -f $< $@
 
+$(TESTCASEDIR)/Makefile: Makefile.testcases
+	ln -sf ../../../../$< $@
+
 clean-build:
 	rm -rf $(BUILDDIR)

+ 11 - 0
LibOS/shim/test/apps/ltp/Makefile.testcases

@@ -0,0 +1,11 @@
+manifests = $(wildcard ../../../../*.manifest.template) manifest
+testcases = $(filter-out $(wildcard *.*) $(patsubst %/,%,$(wildcard */)) Makefile manifest pal_loader,$(wildcard *))
+
+exec_target = $(testcases)
+target = $(manifests) $(testcases)
+
+level = ../../../../../../
+include ../../../../../../Makefile
+
+$(addsuffix .template,$(manifests)): %: ../../../../%
+	ln -sf $< $@

+ 6 - 4
LibOS/shim/test/apps/ltp/manifest.template

@@ -2,10 +2,6 @@ loader.preload = file:$(SHIMPATH)
 loader.env.LD_LIBRARY_PATH = /lib:/lib64:/usr/lib:/usr/lib64
 loader.debug_type = none
 
-fs.mount.tmp1.type = chroot
-fs.mount.tmp1.path = /tmp
-fs.mount.tmp1.uri = file:/tmp
-
 fs.mount.lib.type = chroot
 fs.mount.lib.path = /lib
 fs.mount.lib.uri = file:$(LIBCDIR)
@@ -16,3 +12,9 @@ fs.mount.usr.uri = file:/usr
 
 sys.brk.size = 32M
 sys.stack.size = 4M
+
+sgx.trusted_files.ld = file:$(LIBCDIR)/ld-linux-x86-64.so.2
+sgx.trusted_files.libc = file:$(LIBCDIR)/libc.so.6
+sgx.trusted_files.libdl = file:$(LIBCDIR)/libdl.so.2
+sgx.trusted_files.libm = file:$(LIBCDIR)/libm.so.6
+sgx.trusted_files.libpthread = file:$(LIBCDIR)/libpthread.so.0

+ 63 - 0
LibOS/shim/test/apps/nginx/Makefile

@@ -0,0 +1,63 @@
+manifests = nginx.manifest
+SRCDIR = nginx-1.10.1
+HOST = $(firstword $(shell ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' -m 1 | cut -d: -f2))
+PORT = 8000
+CORES = 4
+THREADS = 25
+
+conf_files = http.t
+
+target = build-nginx $(conf_files) test-data
+exec_target = $(manifests)
+clean-extra = clean-others
+
+extra_rules = -e 's:\$$(HOST):$(HOST):g' -e 's:\$$(PORT):$(PORT):g'
+
+level = ../../
+include ../../Makefile
+
+build-nginx: build/sbin/nginx
+
+build/sbin/nginx: $(SRCDIR)
+	cd $(SRCDIR) && ./configure --prefix=$(PWD)/build
+	$(MAKE) -C $(SRCDIR)
+	$(MAKE) -C $(SRCDIR) install
+
+$(SRCDIR): %: %.tar.gz
+	tar -xzf $<
+
+$(conf_files): %.t: %.t.template
+	sed $(extra_rules) $< > $@
+
+start-native-server:
+	$(PREFIX) build/sbin/nginx -c $(abspath http.t)
+
+start-graphene-server:
+	$(PREFIX) ./nginx.manifest -c $(abspath http.t) -g "user root;"
+
+random-data = $(foreach n,1 2 3 4 5 6 7 8 9 10,2K.$n.html) \
+	      $(foreach n,1 2 3 4 5,10K.$n.html) \
+	      $(foreach n,1 2 3 4 5,100K.$n.html) \
+	      $(foreach n,1 2 3,1M.$n.html) \
+	      $(foreach n,1 2 3,10M.$n.html) \
+	      $(foreach n,1 2 3,100.$n.html)
+
+test-data = build/html/oscar-web build/html/oscar-web-static build/html/random \
+	    $(addprefix build/html/random/,$(random-data))
+
+build/html/%: ../web-data/%.tar.gz
+	[ -d "$@" ] || (cd $(dir $@) && tar -xzf ../../$^)
+
+build/html/random:
+	mkdir -p $@
+
+build/html/random/%.html: build/html/random
+	dd if=/dev/urandom of=$@ count=1 bs=$(basename $(basename $(notdir $@)))
+
+test-data: $(test-data)
+
+distclean: clean
+	rm -rf build $(SRCDIR)
+
+clean-others:
+	rm -rf $(conf_files) $(test-data)

+ 62 - 0
LibOS/shim/test/apps/nginx/etc/group

@@ -0,0 +1,62 @@
+root:x:0:
+daemon:x:1:
+bin:x:2:
+sys:x:3:
+adm:x:4:syslog
+tty:x:5:
+disk:x:6:
+lp:x:7:
+mail:x:8:
+news:x:9:
+uucp:x:10:
+man:x:12:
+proxy:x:13:
+kmem:x:15:
+dialout:x:20:
+fax:x:21:
+voice:x:22:
+cdrom:x:24:
+floppy:x:25:
+tape:x:26:
+sudo:x:27:
+audio:x:29:pulse
+dip:x:30:
+www-data:x:33:
+backup:x:34:
+operator:x:37:
+list:x:38:
+irc:x:39:
+src:x:40:
+gnats:x:41:
+shadow:x:42:
+utmp:x:43:
+video:x:44:
+sasl:x:45:
+plugdev:x:46:
+staff:x:50:
+games:x:60:
+users:x:100:
+nogroup:x:65534:
+libuuid:x:101:
+netdev:x:102:
+crontab:x:103:
+syslog:x:104:
+fuse:x:105:
+messagebus:x:106:
+ssl-cert:x:107:
+lpadmin:x:108:
+scanner:x:109:saned
+mlocate:x:110:
+ssh:x:111:
+utempter:x:112:
+avahi-autoipd:x:113:
+rtkit:x:114:
+saned:x:115:
+whoopsie:x:116:
+avahi:x:117:
+lightdm:x:118:
+nopasswdlogin:x:119:
+bluetooth:x:120:
+colord:x:121:
+pulse:x:122:
+pulse-access:x:123:

+ 19 - 0
LibOS/shim/test/apps/nginx/etc/nsswitch.conf

@@ -0,0 +1,19 @@
+# /etc/nsswitch.conf
+#
+# Example configuration of GNU Name Service Switch functionality.
+# If you have the `glibc-doc-reference' and `info' packages installed, try:
+# `info libc "Name Service Switch"' for information about this file.
+
+passwd:         compat
+group:          compat
+shadow:         compat
+
+hosts:          files mdns4_minimal [NOTFOUND=return] dns
+networks:       files
+
+protocols:      db files
+services:       db files
+ethers:         db files
+rpc:            db files
+
+netgroup:       nis

+ 36 - 0
LibOS/shim/test/apps/nginx/etc/passwd

@@ -0,0 +1,36 @@
+root:x:0:0:root:/root:/bin/bash
+daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
+bin:x:2:2:bin:/bin:/usr/sbin/nologin
+sys:x:3:3:sys:/dev:/usr/sbin/nologin
+sync:x:4:65534:sync:/bin:/bin/sync
+games:x:5:60:games:/usr/games:/usr/sbin/nologin
+man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
+lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
+mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
+news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
+uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
+proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
+www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
+backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
+list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
+irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
+gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
+nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
+libuuid:x:100:101::/var/lib/libuuid:
+syslog:x:101:104::/home/syslog:/bin/false
+messagebus:x:102:106::/var/run/dbus:/bin/false
+usbmux:x:103:46:usbmux daemon,,,:/home/usbmux:/bin/false
+dnsmasq:x:104:65534:dnsmasq,,,:/var/lib/misc:/bin/false
+avahi-autoipd:x:105:113:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/bin/false
+kernoops:x:106:65534:Kernel Oops Tracking Daemon,,,:/:/bin/false
+rtkit:x:107:114:RealtimeKit,,,:/proc:/bin/false
+saned:x:108:115::/home/saned:/bin/false
+whoopsie:x:109:116::/nonexistent:/bin/false
+speech-dispatcher:x:110:29:Speech Dispatcher,,,:/var/run/speech-dispatcher:/bin/sh
+avahi:x:111:117:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/bin/false
+lightdm:x:112:118:Light Display Manager:/var/lib/lightdm:/bin/false
+colord:x:113:121:colord colour management daemon,,,:/var/lib/colord:/bin/false
+hplip:x:114:7:HPLIP system user,,,:/var/run/hplip:/bin/false
+pulse:x:115:122:PulseAudio daemon,,,:/var/run/pulse:/bin/false
+sshd:x:116:65534::/var/run/sshd:/usr/sbin/nologin
+statd:x:117:65534::/var/lib/nfs:/bin/false

+ 35 - 0
LibOS/shim/test/apps/nginx/http.t.template

@@ -0,0 +1,35 @@
+## user       www www;   ## Default: nobody
+## worker_processes  5;  ## Default: 1
+daemon     off;
+error_log  logs/error.log;
+pid        logs/nginx.pid;
+worker_rlimit_nofile 8192;
+
+events {
+  worker_connections  4096;  ## Default: 1024
+}
+
+http {
+  include    build/conf/mime.types;
+  index      index.html;
+
+  default_type application/octet-stream;
+  log_format   main '$remote_addr - $remote_user [$time_local]  $status '
+    '"$request" $body_bytes_sent "$http_referer" '
+    '"$http_user_agent" "$http_x_forwarded_for"';
+  access_log   logs/access.log  main;
+  sendfile     on;
+  tcp_nopush   on;
+  tcp_nodelay  on;
+  keepalive_timeout 65;
+  types_hash_max_size 2048;
+
+  server {
+    listen       $(HOST):$(PORT);
+    server_name  $(HOST);
+    root         html;
+
+    location / {
+    }
+  }
+}

BIN
LibOS/shim/test/apps/nginx/nginx-1.10.1.tar.gz


+ 24 - 0
LibOS/shim/test/apps/nginx/nginx-tests/LICENSE

@@ -0,0 +1,24 @@
+/* 
+ * Copyright (C) 2008-2011 Maxim Dounin
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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.
+ */

+ 39 - 0
LibOS/shim/test/apps/nginx/nginx-tests/README

@@ -0,0 +1,39 @@
+Test suite for nginx.
+
+Use prove to run tests as one usually do for perl tests.  Individual tests
+may be run as well.
+
+Note: tests run nginx (and backend daemons if needed) listening on localhost
+and may use various ports in 8000 .. 8999 range.
+
+Usage:
+
+    $ TEST_NGINX_BINARY=/path/to/nginx prove .
+
+By default tests expect nginx binary to be at ../nginx/objs/nginx.
+
+Environment variables:
+
+TEST_NGINX_BINARY
+
+    Sets path to nginx binary to be tested, defaults to "../nginx/objs/nginx".
+
+TEST_NGINX_VERBOSE
+
+    Be a bit more verbose (in particular, print requests sent and responses
+    got from nginx).  Note that this requires prove -v (or HARNESS_VERBOSE).
+
+TEST_NGINX_LEAVE
+
+    If set temporary directory with configs and logs won't be deleted on test
+    completion.  Usefull for debugging.
+
+TEST_NGINX_CATLOG
+
+    Cat error log to stdout after test completion.  Usefull for debugging.
+
+TEST_NGINX_UNSAFE
+
+    Run unsafe tests.
+
+Happy testing!

+ 110 - 0
LibOS/shim/test/apps/nginx/nginx-tests/auth_basic.t

@@ -0,0 +1,110 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for auth basic module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+use MIME::Base64;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http auth_basic/)->plan(11)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            auth_basic           "closed site";
+            auth_basic_user_file %%TESTDIR%%/htpasswd;
+        }
+    }
+}
+
+EOF
+
+my $d = $t->testdir();
+
+$t->write_file('index.html', 'SEETHIS');
+
+$t->write_file(
+	'htpasswd',
+	'crypt:' . crypt('password', 'salt') . "\n" .
+	'crypt1:' . crypt('password', '$1$salt$') . "\n" .
+	'apr1:' . '$apr1$salt$Xxd1irWT9ycqoYxGFn4cb.' . "\n" .
+	'plain:' . '{PLAIN}password' . "\n" .
+	'ssha:' . '{SSHA}yI6cZwQadOA1e+/f+T+H3eCQQhRzYWx0' . "\n"
+);
+
+$t->run();
+
+###############################################################################
+
+like(http_get('/'), qr!401 Unauthorized!ms, 'rejects unathorized');
+
+like(http_get_auth('/', 'crypt', 'password'), qr!SEETHIS!, 'normal crypt');
+unlike(http_get_auth('/', 'crypt', '123'), qr!SEETHIS!, 'normal wrong');
+
+like(http_get_auth('/', 'crypt1', 'password'), qr!SEETHIS!, 'crypt $1$ (md5)');
+unlike(http_get_auth('/', 'crypt1', '123'), qr!SEETHIS!, 'crypt $1$ wrong');
+
+like(http_get_auth('/', 'apr1', 'password'), qr!SEETHIS!, 'apr1 md5');
+like(http_get_auth('/', 'plain', 'password'), qr!SEETHIS!, 'plain password');
+
+SKIP: {
+	# SHA1 may not be available unless we have OpenSSL
+
+	skip 'no sha1', 1 unless $t->has_module('--with-http_ssl_module')
+		or $t->has_module('--with-sha1')
+		or $t->has_module('--with-openssl');
+
+	like(http_get_auth('/', 'ssha', 'password'), qr!SEETHIS!, 'ssha');
+}
+
+unlike(http_get_auth('/', 'apr1', '123'), qr!SEETHIS!, 'apr1 md5 wrong');
+unlike(http_get_auth('/', 'plain', '123'), qr!SEETHIS!, 'plain wrong');
+unlike(http_get_auth('/', 'ssha', '123'), qr!SEETHIS!, 'ssha wrong');
+
+###############################################################################
+
+sub http_get_auth {
+	my ($url, $user, $password) = @_;
+
+	my $auth = encode_base64($user . ':' . $password);
+
+        my $r = http(<<EOF);
+GET $url HTTP/1.0
+Host: localhost
+Authorization: Basic $auth
+
+EOF
+}
+
+###############################################################################

+ 127 - 0
LibOS/shim/test/apps/nginx/nginx-tests/autoindex.t

@@ -0,0 +1,127 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for autoindex module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http autoindex/)->plan(16)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            autoindex on;
+        }
+        location /utf8/ {
+            autoindex on;
+            charset utf-8;
+        }
+    }
+}
+
+EOF
+
+my $d = $t->testdir();
+
+mkdir("$d/test-dir");
+symlink("$d/test-dir", "$d/test-dir-link");
+
+$t->write_file('test-file', '');
+symlink("$d/test-file", "$d/test-file-link");
+
+$t->write_file('test-colon:blah', '');
+$t->write_file('test-long-' . ('0' x 50), '');
+$t->write_file('test-long-' . ('>' x 50), '');
+$t->write_file('test-escape-url-%', '');
+$t->write_file('test-escape-url2-?', '');
+$t->write_file('test-escape-html-<>&', '');
+
+mkdir($d . '/utf8');
+$t->write_file('utf8/test-utf8-' . ("\xd1\x84" x 3), '');
+$t->write_file('utf8/test-utf8-' . ("\xd1\x84" x 45), '');
+$t->write_file('utf8/test-utf8-<>&-' . "\xd1\x84", '');
+$t->write_file('utf8/test-utf8-<>&-' . ("\xd1\x84" x 45), '');
+$t->write_file('utf8/test-utf8-' . ("\xd1\x84" x 3) . '-' . ('>' x 45), '');
+
+mkdir($d . '/test-dir-escape-<>&');
+
+$t->run();
+
+###############################################################################
+
+my $r = http_get('/');
+
+like($r, qr!href="test-file"!ms, 'file');
+like($r, qr!href="test-file-link"!ms, 'symlink to file');
+like($r, qr!href="test-dir/"!ms, 'directory');
+like($r, qr!href="test-dir-link/"!ms, 'symlink to directory');
+
+unlike($r, qr!href="test-colon:blah"!ms, 'colon not scheme');
+like($r, qr!test-long-0{37}\.\.&gt;!ms, 'long name');
+
+like($r, qr!href="test-escape-url-%25"!ms, 'escaped url');
+
+{
+local $TODO = 'not fixed yet';
+
+like($r, qr!href="test-escape-url2-%3f"!ms, 'escaped ? in url');
+
+}
+
+{
+local $TODO = 'patch under review';
+
+like($r, qr!test-escape-html-&lt;&gt;&amp;!ms, 'escaped html');
+like($r, qr!test-long-(&gt;){37}\.\.&gt;!ms, 'long escaped html');
+
+}
+
+$r = http_get('/utf8/');
+
+like($r, qr!test-utf8-(\xd1\x84){3}</a>!ms, 'utf8');
+like($r, qr!test-utf8-(\xd1\x84){37}\.\.!ms, 'utf8 long');
+
+{
+local $TODO = 'patch under review';
+
+like($r, qr!test-utf8-&lt;&gt;&amp;-\xd1\x84</a>!ms, 'utf8 escaped');
+like($r, qr!test-utf8-&lt;&gt;&amp;-(\xd1\x84){33}\.\.!ms,
+	'utf8 escaped long');
+like($r, qr!test-utf8-(\xd1\x84){3}-(&gt;){33}\.\.!ms, 'utf8 long escaped');
+
+like(http_get('/test-dir-escape-<>&/'), qr!test-dir-escape-&lt;&gt;&amp;!ms,
+	'escaped title');
+
+}
+
+###############################################################################

+ 146 - 0
LibOS/shim/test/apps/nginx/nginx-tests/dav.t

@@ -0,0 +1,146 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for nginx dav module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http dav/)->plan(13);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            dav_methods PUT DELETE MKCOL COPY MOVE;
+        }
+    }
+}
+
+EOF
+
+$t->run();
+
+###############################################################################
+
+my $r;
+
+$r = http(<<EOF . '0123456789');
+PUT /file HTTP/1.1
+Host: localhost
+Connection: close
+Content-Length: 10
+
+EOF
+
+like($r, qr/201 Created.*(Content-Length|\x0d\0a0\x0d\x0a)/ms, 'put file');
+is(-s $t->testdir() . '/file', 10, 'put file size');
+
+$r = http(<<EOF);
+PUT /file HTTP/1.1
+Host: localhost
+Connection: close
+Content-Length: 0
+
+EOF
+
+like($r, qr/204 No Content/, 'put file again');
+unlike($r, qr/Content-Length|Transfer-Encoding/, 'no length in 204');
+is(-s $t->testdir() . '/file', 0, 'put file again size');
+
+$r = http(<<EOF);
+DELETE /file HTTP/1.1
+Host: localhost
+Connection: close
+Content-Length: 0
+
+EOF
+
+like($r, qr/204 No Content/, 'delete file');
+unlike($r, qr/Content-Length|Transfer-Encoding/, 'no length in 204');
+ok(!-f $t->testdir() . '/file', 'file deleted');
+
+$r = http(<<EOF . '0123456789' . 'extra');
+PUT /file HTTP/1.1
+Host: localhost
+Connection: close
+Content-Length: 10
+
+EOF
+
+like($r, qr/201 Created.*(Content-Length|\x0d\0a0\x0d\x0a)/ms,
+	'put file extra data');
+TODO: {
+local $TODO = 'not yet';
+
+is(-s $t->testdir() . '/file', 10,
+	'put file extra data size');
+}
+
+TODO: {
+local $TODO = 'broken in 0.8.32';
+
+# 201 replies contain body, response should indicate it's empty
+# before 0.8.32 chunked was explicitly disabled for 201 replies so
+# connection was just closed (which isn't perfect but worked)
+
+$r = http(<<EOF);
+MKCOL /test/ HTTP/1.1
+Host: localhost
+Connection: close
+
+EOF
+
+like($r, qr/201 Created.*(Content-Length|\x0d\0a0\x0d\x0a)/ms, 'mkcol');
+
+$r = http(<<EOF);
+COPY /test/ HTTP/1.1
+Host: localhost
+Destination: /test-moved/
+Connection: close
+
+EOF
+
+like($r, qr/201 Created.*(Content-Length|\x0d\0a0\x0d\x0a)/ms, 'copy dir');
+
+$r = http(<<EOF);
+MOVE /test/ HTTP/1.1
+Host: localhost
+Destination: /test-moved/
+Connection: close
+
+EOF
+
+like($r, qr/201.*(Content-Length|\x0d\0a0\x0d\x0a)/ms, 'move dir');
+}
+
+###############################################################################

+ 94 - 0
LibOS/shim/test/apps/nginx/nginx-tests/fastcgi.t

@@ -0,0 +1,94 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Test for fastcgi backend.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+eval { require FCGI; };
+plan(skip_all => 'FCGI not installed') if $@;
+
+my $t = Test::Nginx->new()->has(qw/http fastcgi/)->plan(5)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            fastcgi_pass 127.0.0.1:8081;
+            fastcgi_param REQUEST_URI $request_uri;
+        }
+    }
+}
+
+EOF
+
+$t->run_daemon(\&fastcgi_daemon);
+$t->run();
+
+###############################################################################
+
+like(http_get('/'), qr/SEE-THIS/, 'fastcgi request');
+like(http_get('/redir'), qr/302/, 'fastcgi redirect');
+like(http_get('/'), qr/^3$/m, 'fastcgi third request');
+
+unlike(http_head('/'), qr/SEE-THIS/, 'no data in HEAD');
+
+like(http_get('/stderr'), qr/SEE-THIS/, 'large stderr handled');
+
+###############################################################################
+
+sub fastcgi_daemon {
+	my $socket = FCGI::OpenSocket('127.0.0.1:8081', 5);
+	my $request = FCGI::Request(\*STDIN, \*STDOUT, \*STDERR, \%ENV,
+		$socket);
+
+	my $count;
+	while( $request->Accept() >= 0 ) {
+		$count++;
+
+		if ($ENV{REQUEST_URI} eq '/stderr') {
+			warn "sample stderr text" x 512;
+		}
+		
+		print <<EOF;
+Location: http://127.0.0.1:8080/redirect
+Content-Type: text/html
+
+SEE-THIS
+$count
+EOF
+	}
+
+	FCGI::CloseSocket($socket);
+}
+
+###############################################################################

+ 100 - 0
LibOS/shim/test/apps/nginx/nginx-tests/fastcgi_cache.t

@@ -0,0 +1,100 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Test for fastcgi backend with cache.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+eval { require FCGI; };
+plan(skip_all => 'FCGI not installed') if $@;
+
+my $t = Test::Nginx->new()->has(qw/http fastcgi cache/)->plan(5)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    fastcgi_cache_path   %%TESTDIR%%/cache  levels=1:2
+                         keys_zone=NAME:10m;
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            fastcgi_pass 127.0.0.1:8081;
+            fastcgi_param REQUEST_URI $request_uri;
+            fastcgi_cache NAME;
+            fastcgi_cache_key $request_uri;
+            fastcgi_cache_valid 302 1m;
+        }
+    }
+}
+
+EOF
+
+$t->run_daemon(\&fastcgi_daemon);
+$t->run();
+
+###############################################################################
+
+like(http_get('/'), qr/SEE-THIS/, 'fastcgi request');
+like(http_get('/'), qr/SEE-THIS/, 'fastcgi request cached');
+
+unlike(http_head('/'), qr/SEE-THIS/, 'no data in cached HEAD');
+
+like(http_get('/stderr'), qr/SEE-THIS/, 'large stderr handled');
+like(http_get('/stderr'), qr/SEE-THIS/, 'large stderr cached');
+
+###############################################################################
+
+sub fastcgi_daemon {
+	my $socket = FCGI::OpenSocket('127.0.0.1:8081', 5);
+	my $request = FCGI::Request(\*STDIN, \*STDOUT, \*STDERR, \%ENV,
+		$socket);
+
+	my $count;
+	while( $request->Accept() >= 0 ) {
+		$count++;
+
+		if ($ENV{REQUEST_URI} eq '/stderr') {
+			warn "sample stderr text" x 512;
+		}
+		
+		print <<EOF;
+Location: http://127.0.0.1:8080/redirect
+Content-Type: text/html
+
+SEE-THIS
+$count
+EOF
+	}
+
+	FCGI::CloseSocket($socket);
+}
+
+###############################################################################

+ 122 - 0
LibOS/shim/test/apps/nginx/nginx-tests/fastcgi_header_params.t

@@ -0,0 +1,122 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Test for fastcgi header params.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+eval { require FCGI; };
+plan(skip_all => 'FCGI not installed') if $@;
+
+my $t = Test::Nginx->new()->has(qw/http fastcgi/)->plan(1)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            fastcgi_pass 127.0.0.1:8081;
+            fastcgi_param HTTP_X_BLAH "blah";
+        }
+    }
+}
+
+EOF
+
+$t->run_daemon(\&fastcgi_daemon);
+$t->run();
+
+###############################################################################
+
+SKIP: {
+skip 'unsafe', 1 unless $ENV{TEST_NGINX_UNSAFE};
+
+local $TODO = 'not yet';
+
+like(http_get_headers('/'), qr/SEE-THIS/,
+	'fastcgi request with many ignored headers');
+
+}
+
+###############################################################################
+
+sub http_get_headers {
+        my ($url, %extra) = @_;
+        return http(<<EOF, %extra);
+GET $url HTTP/1.0
+Host: localhost
+X-Blah: ignored header
+X-Blah: ignored header
+X-Blah: ignored header
+X-Blah: ignored header
+X-Blah: ignored header
+X-Blah: ignored header
+X-Blah: ignored header
+X-Blah: ignored header
+X-Blah: ignored header
+X-Blah: ignored header
+X-Blah: ignored header
+X-Blah: ignored header
+X-Blah: ignored header
+X-Blah: ignored header
+X-Blah: ignored header
+X-Blah: ignored header
+X-Blah: ignored header
+X-Blah: ignored header
+X-Blah: ignored header
+
+EOF
+}
+
+###############################################################################
+
+sub fastcgi_daemon {
+	my $socket = FCGI::OpenSocket('127.0.0.1:8081', 5);
+	my $request = FCGI::Request(\*STDIN, \*STDOUT, \*STDERR, \%ENV,
+		$socket);
+
+	my $count;
+	while( $request->Accept() >= 0 ) {
+		$count++;
+
+		print <<EOF;
+Location: http://127.0.0.1:8080/redirect
+Content-Type: text/html
+
+SEE-THIS
+$count
+EOF
+	}
+
+	FCGI::CloseSocket($socket);
+}
+
+###############################################################################

+ 98 - 0
LibOS/shim/test/apps/nginx/nginx-tests/gzip.t

@@ -0,0 +1,98 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for nginx gzip filter module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx qw/ :DEFAULT :gzip /;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http proxy gzip/)->plan(8);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+        location / {
+            gzip on;
+        }
+        location /proxy/ {
+            gzip on;
+            proxy_pass http://127.0.0.1:8080/local/;
+        }
+        location /local/ {
+            gzip off;
+            alias %%TESTDIR%%/;
+        }
+    }
+}
+
+EOF
+
+$t->write_file('index.html', 'X' x 64);
+
+$t->run();
+
+###############################################################################
+
+my $r;
+
+$r = http_gzip_request('/');
+like($r, qr/^Content-Encoding: gzip/m, 'gzip');
+http_gzip_like($r, qr/^X{64}\Z/, 'gzip content correct');
+
+$r = http_gzip_request('/proxy/');
+like($r, qr/^Content-Encoding: gzip/m, 'gzip proxied');
+http_gzip_like($r, qr/^X{64}\Z/, 'gzip proxied content');
+
+# Accept-Ranges headers should be cleared
+
+unlike(http_gzip_request('/'), qr/Accept-Ranges/im, 'cleared accept-ranges');
+unlike(http_gzip_request('/proxy/'), qr/Accept-Ranges/im,
+	'cleared headers from proxy');
+
+# HEAD requests should return correct headers
+
+like(http_gzip_head('/'), qr/Content-Encoding: gzip/, 'gzip head');
+unlike(http_head('/'), qr/Content-Encoding: gzip/, 'no gzip head');
+
+###############################################################################
+
+sub http_gzip_head {
+	my ($uri) = @_;
+	return http(<<EOF);
+HEAD $uri HTTP/1.1
+Host: localhost
+Connection: close
+Accept-Encoding: gzip
+
+EOF
+}
+
+###############################################################################

+ 78 - 0
LibOS/shim/test/apps/nginx/nginx-tests/gzip_flush.t

@@ -0,0 +1,78 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for gzip filter module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx qw/ :DEFAULT :gzip /;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http gzip perl/)->plan(2)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        gzip on;
+        gzip_min_length 0;
+
+        location / {
+            perl 'sub {
+                my $r = shift;
+                $r->send_http_header("text/html");
+                return OK if $r->header_only;
+                $r->print("DA");
+                $r->flush();
+                $r->flush();
+                $r->print("TA");
+                return OK;
+            }';
+        }
+    }
+}
+
+EOF
+
+$t->run();
+
+###############################################################################
+
+like(http_get('/'), qr/DATA/, 'request with flush');
+
+TODO: {
+local $TODO = 'not yet';
+
+# gzip filter doesn't properly handle empty flush buffers, see
+# http://nginx.org/pipermail/nginx/2010-November/023693.html
+
+http_gzip_like(http_gzip_request('/'), qr/DATA/, 'gzip request with flush');
+
+}
+
+###############################################################################

+ 137 - 0
LibOS/shim/test/apps/nginx/nginx-tests/http_error_page.t

@@ -0,0 +1,137 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for error_page directive.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(7)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location /redirect200 {
+            error_page 404 =200 http://example.com/;
+            return 404;
+        }
+
+        location /redirect497 {
+            # 497 implies implicit status code change
+            error_page 497 https://example.com/;
+            return 497;
+        }
+
+        location /error302redirect {
+            error_page 302 http://example.com/;
+            return 302 "first";
+        }
+
+        location /error302return302text {
+            error_page 302 /return302text;
+            return 302 "first";
+        }
+
+        location /return302text {
+            return 302 "http://example.com/";
+        }
+
+        location /error302rewrite {
+            error_page 302 /rewrite;
+            return 302 "first";
+        }
+
+        location /rewrite {
+            rewrite ^ http://example.com/;
+        }
+
+        location /error302directory {
+            error_page 302 /directory;
+            return 302 "first";
+        }
+
+        location /directory {
+        }
+
+        location /error302auto {
+            error_page 302 /auto;
+            return 302 "first";
+        }
+
+        location /auto/ {
+            proxy_pass http://127.0.0.1:8081;
+        }
+    }
+}
+
+EOF
+
+mkdir($t->testdir() . '/directory');
+
+$t->run();
+
+###############################################################################
+
+# tests for error_page status code change for redirects. problems
+# introduced in 0.8.53 and fixed in 0.9.5.
+
+like(http_get('/redirect200'), qr!HTTP!, 'redirect 200');
+like(http_get('/redirect497'), qr!HTTP/1.1 302!, 'redirect 497');
+
+TODO: {
+local $TODO = 'not yet';
+
+# various tests to see if old location cleared if we happen to redirect
+# again in error_page 302
+
+like(http_get('/error302redirect'),
+	qr{HTTP/1.1 302(?!.*Location: first).*Location: http://example.com/}ms,
+	'error 302 redirect - old location cleared');
+
+like(http_get('/error302return302text'),
+	qr{HTTP/1.1 302(?!.*Location: first).*Location: http://example.com/}ms,
+	'error 302 return 302 text - old location cleared');
+
+like(http_get('/error302rewrite'),
+	qr{HTTP/1.1 302(?!.*Location: first).*Location: http://example.com/}ms,
+	'error 302 rewrite - old location cleared');
+
+like(http_get('/error302directory'),
+	qr{HTTP/1.1 301(?!.*Location: first).*Location: http://}ms,
+	'error 302 directory redirect - old location cleared');
+
+like(http_get('/error302auto'),
+	qr{HTTP/1.1 301(?!.*Location: first).*Location: http://}ms,
+	'error 302 auto redirect - old location cleared');
+
+}
+
+###############################################################################

+ 82 - 0
LibOS/shim/test/apps/nginx/nginx-tests/http_expect_100_continue.t

@@ -0,0 +1,82 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for Expect: 100-continue support.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http proxy/)->plan(2);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+        location / {
+            proxy_pass http://localhost:8080/local;
+        }
+        location /local {
+        }
+    }
+}
+
+EOF
+
+$t->run();
+
+###############################################################################
+
+like(http_100_request('/', '1.1'), qr/100/, 'expect 100 continue');
+
+# From RFC 2616, 8.2.3 Use of the 100 (Continue) Status:
+#
+#      - An origin server SHOULD NOT send a 100 (Continue) response if
+#        the request message does not include an Expect request-header
+#        field with the "100-continue" expectation, and MUST NOT send a
+#        100 (Continue) response if such a request comes from an HTTP/1.0
+#        (or earlier) client.
+
+unlike(http_100_request('/', '1.0'), qr/100/, 'no 100 continue via http 1.0');
+
+###############################################################################
+
+sub http_100_request {
+	my ($url, $version) = @_;
+	my $r = http(<<EOF);
+POST $url HTTP/$version
+Host: localhost
+Expect: 100-continue
+Content-Length: 0
+Connection: close
+
+EOF
+}
+
+###############################################################################

+ 85 - 0
LibOS/shim/test/apps/nginx/nginx-tests/http_location.t

@@ -0,0 +1,85 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for location selection.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(8)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location = / {
+            add_header X-Location exactlyroot;
+            return 204;
+        }
+
+        location / {
+            add_header X-Location root;
+            return 204;
+        }
+
+        location ^~ /images/ {
+            add_header X-Location images;
+            return 204;
+        }
+
+        location ~* \.(gif|jpg|jpeg)$ {
+            add_header X-Location regex;
+            return 204;
+        }
+
+        location ~ casefull {
+            add_header X-Location casefull;
+            return 204;
+        }
+    }
+}
+
+EOF
+
+$t->run();
+
+###############################################################################
+
+like(http_get('/'), qr/X-Location: exactlyroot/, 'exactlyroot');
+like(http_get('/x'), qr/X-Location: root/, 'root');
+like(http_get('/images/t.gif'), qr/X-Location: images/, 'images');
+like(http_get('/t.gif'), qr/X-Location: regex/, 'regex');
+like(http_get('/t.GIF'), qr/X-Location: regex/, 'regex with mungled case');
+like(http_get('/casefull/t.gif'), qr/X-Location: regex/, 'first regex wins');
+like(http_get('/casefull/'), qr/X-Location: casefull/, 'casefull regex');
+like(http_get('/CASEFULL/'), qr/X-Location: root/,
+     'casefull regex do not match wrong case');
+
+###############################################################################

+ 130 - 0
LibOS/shim/test/apps/nginx/nginx-tests/http_server_name.t

@@ -0,0 +1,130 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for server_name selection.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(9)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            add_header X-Server $server_name;
+            return 204;
+        }
+    }
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  www.example.com;
+
+        location / {
+            add_header X-Server $server_name;
+            return 204;
+        }
+    }
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  ~^EXAMPLE\.COM$;
+
+        location / {
+            add_header X-Server $server_name;
+            return 204;
+        }
+    }
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  ~^(?P<name>.+)\Q.example.com\E$;
+
+        location / {
+            add_header X-Server $server_name;
+            add_header X-Match  $name;
+            return 204;
+        }
+    }
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  "~^(?<name>www\p{N}+)\.example\.com$";
+
+        location / {
+            add_header X-Server $server_name;
+            add_header X-Match  $name;
+            return 204;
+        }
+    }
+}
+
+EOF
+
+$t->run();
+
+###############################################################################
+
+sub http_server($) {
+	my ($host) = @_;
+	return http(<<EOF);
+GET / HTTP/1.0
+Host: $host
+
+EOF
+}
+
+###############################################################################
+
+like(http_server('xxx'), qr/X-Server: localhost/, 'default');
+
+like(http_server('www.example.com'), qr/\QX-Server: www.example.com/,
+	'www.example.com');
+like(http_server('WWW.EXAMPLE.COM'), qr/\QX-Server: www.example.com/,
+	'www.example.com uppercase');
+
+like(http_server('example.com'), qr/\QX-Server: ~^EXAMPLE\.COM$/,
+	'example.com regex');
+like(http_server('EXAMPLE.COM'), qr/\QX-Server: ~^EXAMPLE\.COM$/,
+	'example.com regex uppercase');
+
+like(http_server('blah.example.com'), qr/X-Match: blah/,
+	'(P<name>.*).example.com named capture');
+like(http_server('BLAH.EXAMPLE.COM'), qr/X-Match: blah/,
+	'(P<name>.*).example.com named capture uppercase');
+
+like(http_server('www01.example.com'), qr/X-Match: www01/,
+	'\p{N} in named capture');
+like(http_server('WWW01.EXAMPLE.COM'), qr/X-Match: www01/,
+	'\p{N} in named capture uppercase');
+
+###############################################################################

+ 446 - 0
LibOS/shim/test/apps/nginx/nginx-tests/lib/Test/Nginx.pm

@@ -0,0 +1,446 @@
+package Test::Nginx;
+
+# (C) Maxim Dounin
+
+# Generic module for nginx tests.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use base qw/ Exporter /;
+
+our @EXPORT = qw/ log_in log_out http http_get http_head /;
+our @EXPORT_OK = qw/ http_gzip_request http_gzip_like /;
+our %EXPORT_TAGS = (
+	gzip => [ qw/ http_gzip_request http_gzip_like / ]
+);
+
+###############################################################################
+
+use File::Temp qw/ tempdir /;
+use IO::Socket;
+use Socket qw/ CRLF /;
+use Test::More qw//;
+
+###############################################################################
+
+our $NGINX = defined $ENV{TEST_NGINX_BINARY} ? $ENV{TEST_NGINX_BINARY}
+	: '../nginx/objs/nginx';
+
+sub new {
+	my $self = {};
+	bless $self;
+
+	$self->{_testdir} = tempdir(
+		'nginx-test-XXXXXXXXXX',
+		TMPDIR => 1,
+		CLEANUP => not $ENV{TEST_NGINX_LEAVE}
+	)
+		or die "Can't create temp directory: $!\n";
+
+	return $self;
+}
+
+sub DESTROY {
+	my ($self) = @_;
+	$self->stop();
+	$self->stop_daemons();
+	if ($ENV{TEST_NGINX_CATLOG}) {
+		system("cat $self->{_testdir}/error.log");
+	}
+}
+
+sub has($;) {
+	my ($self, @features) = @_;
+
+	foreach my $feature (@features) {
+		Test::More::plan(skip_all => "$feature not compiled in")
+			unless $self->has_module($feature);
+	}
+
+	return $self;
+}
+
+sub has_module($) {
+	my ($self, $feature) = @_;
+
+	my %regex = (
+		mail	=> '--with-mail(?!\S)',
+		flv	=> '--with-http_flv_module',
+		perl	=> '--with-http_perl_module',
+		charset	=> '(?s)^(?!.*--without-http_charset_module)',
+		gzip	=> '(?s)^(?!.*--without-http_gzip_module)',
+		ssi	=> '(?s)^(?!.*--without-http_ssi_module)',
+		userid	=> '(?s)^(?!.*--without-http_userid_module)',
+		access	=> '(?s)^(?!.*--without-http_access_module)',
+		auth_basic
+			=> '(?s)^(?!.*--without-http_auth_basic_module)',
+		autoindex
+			=> '(?s)^(?!.*--without-http_autoindex_module)',
+		geo	=> '(?s)^(?!.*--without-http_geo_module)',
+		map	=> '(?s)^(?!.*--without-http_map_module)',
+		referer	=> '(?s)^(?!.*--without-http_referer_module)',
+		rewrite	=> '(?s)^(?!.*--without-http_rewrite_module)',
+		proxy	=> '(?s)^(?!.*--without-http_proxy_module)',
+		fastcgi	=> '(?s)^(?!.*--without-http_fastcgi_module)',
+		uwsgi	=> '(?s)^(?!.*--without-http_uwsgi_module)',
+		scgi	=> '(?s)^(?!.*--without-http_scgi_module)',
+		memcached
+			=> '(?s)^(?!.*--without-http_memcached_module)',
+		limit_zone
+			=> '(?s)^(?!.*--without-http_limit_zone_module)',
+		limit_req
+			=> '(?s)^(?!.*--without-http_limit_req_module)',
+		empty_gif
+			=> '(?s)^(?!.*--without-http_empty_gif_module)',
+		browser	=> '(?s)^(?!.*--without-http_browser_module)',
+		upstream_ip_hash
+			=> '(?s)^(?!.*--without-http_upstream_ip_hash_module)',
+		http	=> '(?s)^(?!.*--without-http(?!\S))',
+		cache	=> '(?s)^(?!.*--without-http-cache)',
+		pop3	=> '(?s)^(?!.*--without-mail_pop3_module)',
+		imap	=> '(?s)^(?!.*--without-mail_imap_module)',
+		smtp	=> '(?s)^(?!.*--without-mail_smtp_module)',
+		pcre	=> '(?s)^(?!.*--without-pcre)',
+	);
+
+	my $re = $regex{$feature};
+	$re = $feature if !defined $re;
+
+	$self->{_configure_args} = `$NGINX -V 2>&1`
+		if !defined $self->{_configure_args};
+
+	return ($self->{_configure_args} =~ $re) ? 1 : 0;
+}
+
+sub has_daemon($) {
+	my ($self, $daemon) = @_;
+
+	Test::More::plan(skip_all => "$daemon not found")
+		unless `which $daemon`;
+
+	return $self;
+}
+
+sub plan($) {
+	my ($self, $plan) = @_;
+
+	Test::More::plan(tests => $plan);
+
+	return $self;
+}
+
+sub run(;$) {
+	my ($self, $conf) = @_;
+
+	my $testdir = $self->{_testdir};
+
+	if (defined $conf) {
+		my $c = `cat $conf`;
+		$self->write_file_expand('nginx.conf', $c);
+	}
+
+	my $pid = fork();
+	die "Unable to fork(): $!\n" unless defined $pid;
+
+	if ($pid == 0) {
+		my @globals = $self->{_test_globals} ?
+			() : ('-g', "pid $testdir/nginx.pid; "
+			. "error_log $testdir/error.log debug;");
+		exec($NGINX, '-c', "$testdir/nginx.conf", @globals)
+			or die "Unable to exec(): $!\n";
+	}
+
+	# wait for nginx to start
+
+	$self->waitforfile("$testdir/nginx.pid")
+		or die "Can't start nginx";
+
+	$self->{_started} = 1;
+	return $self;
+}
+
+sub waitforfile($) {
+	my ($self, $file) = @_;
+
+	# wait for file to appear
+
+	for (1 .. 30) {
+		return 1 if -e $file;
+		select undef, undef, undef, 0.1;
+	}
+
+	return undef;
+}
+
+sub waitforsocket($) {
+	my ($self, $peer) = @_;
+
+	# wait for socket to accept connections
+
+	for (1 .. 30) {
+		my $s = IO::Socket::INET->new(
+			Proto => 'tcp',
+			PeerAddr => $peer
+		);
+
+		return 1 if defined $s;
+
+		select undef, undef, undef, 0.1;
+	}
+
+	return undef;
+}
+
+sub stop() {
+	my ($self) = @_;
+
+	return $self unless $self->{_started};
+
+	kill 'QUIT', `cat $self->{_testdir}/nginx.pid`;
+	wait;
+
+	$self->{_started} = 0;
+
+	return $self;
+}
+
+sub stop_daemons() {
+	my ($self) = @_;
+
+	while ($self->{_daemons} && scalar @{$self->{_daemons}}) {
+		my $p = shift @{$self->{_daemons}};
+		kill 'TERM', $p;
+		wait;
+	}
+
+	return $self;
+}
+
+sub write_file($$) {
+	my ($self, $name, $content) = @_;
+
+	open F, '>' . $self->{_testdir} . '/' . $name
+		or die "Can't create $name: $!";
+	print F $content;
+	close F;
+
+	return $self;
+}
+
+sub write_file_expand($$) {
+	my ($self, $name, $content) = @_;
+
+	$content =~ s/%%TEST_GLOBALS%%/$self->test_globals()/gmse;
+	$content =~ s/%%TEST_GLOBALS_HTTP%%/$self->test_globals_http()/gmse;
+	$content =~ s/%%TESTDIR%%/$self->{_testdir}/gms;
+
+	return $self->write_file($name, $content);
+}
+
+sub run_daemon($;@) {
+	my ($self, $code, @args) = @_;
+
+	my $pid = fork();
+	die "Can't fork daemon: $!\n" unless defined $pid;
+
+	if ($pid == 0) {
+		if (ref($code) eq 'CODE') {
+			$code->(@args);
+			exit 0;
+		} else {
+			exec($code, @args);
+		}
+	}
+
+	$self->{_daemons} = [] unless defined $self->{_daemons};
+	push @{$self->{_daemons}}, $pid;
+
+	return $self;
+}
+
+sub testdir() {
+	my ($self) = @_;
+	return $self->{_testdir};
+}
+
+sub test_globals() {
+	my ($self) = @_;
+
+	return $self->{_test_globals}
+		if defined $self->{_test_globals};
+
+	my $s = '';
+
+	$s .= "pid $self->{_testdir}/nginx.pid;\n";
+	$s .= "error_log $self->{_testdir}/error.log debug;\n";
+
+	$self->{_test_globals} = $s;
+}
+
+sub test_globals_http() {
+	my ($self) = @_;
+
+	return $self->{_test_globals_http}
+		if defined $self->{_test_globals_http};
+
+	my $s = '';
+
+	$s .= "root $self->{_testdir};\n";
+	$s .= "access_log $self->{_testdir}/access.log;\n";
+	$s .= "client_body_temp_path $self->{_testdir}/client_body_temp;\n";
+
+	$s .= "fastcgi_temp_path $self->{_testdir}/fastcgi_temp;\n" 
+		if $self->has_module('fastcgi');
+
+	$s .= "proxy_temp_path $self->{_testdir}/proxy_temp;\n"
+		if $self->has_module('proxy');
+
+	$s .= "uwsgi_temp_path $self->{_testdir}/uwsgi_temp;\n"
+		if $self->has_module('uwsgi');
+
+	$s .= "scgi_temp_path $self->{_testdir}/scgi_temp;\n"
+		if $self->has_module('scgi');
+
+	$self->{_test_globals_http} = $s;
+}
+
+###############################################################################
+
+sub log_core {
+	return unless $ENV{TEST_NGINX_VERBOSE};
+	my ($prefix, $msg) = @_;
+	($prefix, $msg) = ('', $prefix) unless defined $msg;
+	$prefix .= ' ' if length($prefix) > 0;
+
+	if (length($msg) > 4096) {
+		$msg = substr($msg, 0, 4096)
+			. "(...logged only 4096 of " . length($msg)
+			. " bytes)";
+	}
+
+	$msg =~ s/^/# $prefix/gm;
+	$msg =~ s/([^\x20-\x7e])/sprintf('\\x%02x', ord($1)) . (($1 eq "\n") ? "\n" : '')/gmxe;
+	$msg .= "\n" unless $msg =~ /\n\Z/;
+	print $msg;
+}
+
+sub log_out {
+	log_core('>>', @_);
+}
+
+sub log_in {
+	log_core('<<', @_);
+}
+
+###############################################################################
+
+sub http_get($;%) {
+	my ($url, %extra) = @_;
+	return http(<<EOF, %extra);
+GET $url HTTP/1.0
+Host: localhost
+
+EOF
+}
+
+sub http_head($;%) {
+	my ($url, %extra) = @_;
+	return http(<<EOF, %extra);
+HEAD $url HTTP/1.0
+Host: localhost
+
+EOF
+}
+
+sub http($;%) {
+	my ($request, %extra) = @_;
+	my $reply;
+	eval {
+		local $SIG{ALRM} = sub { die "timeout\n" };
+		local $SIG{PIPE} = sub { die "sigpipe\n" };
+		alarm(2);
+		my $s = IO::Socket::INET->new(
+			Proto => 'tcp',
+			PeerAddr => '127.0.0.1:8080'
+		);
+		log_out($request);
+		$s->print($request);
+		local $/;
+		select undef, undef, undef, $extra{sleep} if $extra{sleep};
+		return '' if $extra{aborted};
+		$reply = $s->getline();
+		alarm(0);
+	};
+	alarm(0);
+	if ($@) {
+		log_in("died: $@");
+		return undef;
+	} else {
+		log_in($reply);
+	}
+	return $reply;
+}
+
+###############################################################################
+
+sub http_gzip_request {
+	my ($url) = @_;
+	my $r = http(<<EOF);
+GET $url HTTP/1.1
+Host: localhost
+Connection: close
+Accept-Encoding: gzip
+
+EOF
+}
+
+sub http_content {
+	my ($text) = @_;
+
+	return undef if !defined $text;
+
+	if ($text !~ /(.*?)\x0d\x0a?\x0d\x0a?(.*)/ms) {
+		return undef;
+	}
+
+	my ($headers, $body) = ($1, $2);
+
+	if ($headers !~ /Transfer-Encoding: chunked/i) {
+		return $body;
+	}
+
+	my $content = '';
+	while ($body =~ /\G\x0d?\x0a?([0-9a-f]+)\x0d\x0a?/gcmsi) {
+		my $len = hex($1);
+		$content .= substr($body, pos($body), $len);
+		pos($body) += $len;
+	}
+
+	return $content;
+}
+
+sub http_gzip_like {
+	my ($text, $re, $name) = @_;
+
+	SKIP: {
+		eval { require IO::Uncompress::Gunzip; };
+		Test::More->builder->skip(
+			"IO::Uncompress::Gunzip not installed", 1) if $@;
+
+		my $in = http_content($text);
+		my $out;
+
+		IO::Uncompress::Gunzip::gunzip(\$in => \$out);
+
+		Test::More->builder->like($out, $re, $name);
+	}
+}
+
+###############################################################################
+
+1;
+
+###############################################################################

+ 109 - 0
LibOS/shim/test/apps/nginx/nginx-tests/lib/Test/Nginx/IMAP.pm

@@ -0,0 +1,109 @@
+package Test::Nginx::IMAP;
+
+# (C) Maxim Dounin
+
+# Module for nginx imap tests.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More qw//;
+use IO::Socket;
+use Socket qw/ CRLF /;
+
+use Test::Nginx;
+
+use base qw/ IO::Socket::INET /;
+
+sub new {
+	my $class = shift;
+
+	my $self = return $class->SUPER::new(
+		Proto => "tcp",
+		PeerAddr => "127.0.0.1:8143",
+		@_
+	)
+		or die "Can't connect to nginx: $!\n";
+
+	$self->autoflush(1);
+
+	return $self;
+}
+
+sub send {
+	my ($self, $cmd) = @_;
+	log_out($cmd);
+	$self->print($cmd . CRLF);
+}
+
+sub read {
+	my ($self) = @_;
+	eval {
+		local $SIG{ALRM} = sub { die "alarm\n" };
+		alarm(2);
+		while (<$self>) {
+			log_in($_);
+			# XXX
+			next if m/^\d\d\d-/;
+			last;
+		}
+		alarm(0);
+	};
+	alarm(0);
+	if ($@) {
+		return undef;
+	}
+	return $_;
+}
+
+sub check {
+	my ($self, $regex, $name) = @_;
+	Test::More->builder->like($self->read(), $regex, $name);
+}
+
+sub ok {
+	my $self = shift; 
+	Test::More->builder->like($self->read(), qr/^\S+ OK/, @_);
+}
+
+###############################################################################
+
+sub imap_test_daemon {
+	my $server = IO::Socket::INET->new(
+		Proto => 'tcp',
+		LocalAddr => '127.0.0.1:8144',
+		Listen => 5,
+		Reuse => 1
+	)
+		or die "Can't create listening socket: $!\n";
+
+	while (my $client = $server->accept()) {
+		$client->autoflush(1);
+		print $client "* OK fake imap server ready" . CRLF;
+
+		while (<$client>) {
+			my $tag = '';
+
+			$tag = $1 if m/^(\S+)/;
+			s/^(\S+)\s+//;
+
+			if (/^logout/i) {
+				print $client $tag . ' OK logout ok' . CRLF;
+			} elsif (/^login /i) {
+				print $client $tag . ' OK login ok' . CRLF;
+			} else {
+				print $client $tag . ' ERR unknown command' . CRLF;
+			}
+                }
+
+		close $client;
+	}
+}
+
+###############################################################################
+
+1;
+
+###############################################################################

+ 106 - 0
LibOS/shim/test/apps/nginx/nginx-tests/lib/Test/Nginx/POP3.pm

@@ -0,0 +1,106 @@
+package Test::Nginx::POP3;
+
+# (C) Maxim Dounin
+
+# Module for nginx pop3 tests.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More qw//;
+use IO::Socket;
+use Socket qw/ CRLF /;
+
+use Test::Nginx;
+
+use base qw/ IO::Socket::INET /;
+
+sub new {
+	my $class = shift;
+
+	my $self = return $class->SUPER::new(
+		Proto => "tcp",
+		PeerAddr => "127.0.0.1:8110",
+		@_
+	)
+		or die "Can't connect to nginx: $!\n";
+
+	$self->autoflush(1);
+
+	return $self;
+}
+
+sub send {
+	my ($self, $cmd) = @_;
+	log_out($cmd);
+	$self->print($cmd . CRLF);
+}
+
+sub read {
+	my ($self) = @_;
+	eval {
+		local $SIG{ALRM} = sub { die "alarm\n" };
+		alarm(2);
+		while (<$self>) {
+			log_in($_);
+			# XXX
+			next if m/^\d\d\d-/;
+			last;
+		}
+		alarm(0);
+	};
+	alarm(0);
+	if ($@) {
+		return undef;
+	}
+	return $_;
+}
+
+sub check {
+	my ($self, $regex, $name) = @_;
+	Test::More->builder->like($self->read(), $regex, $name);
+}
+
+sub ok {
+	my $self = shift; 
+	Test::More->builder->like($self->read(), qr/^\+OK/, @_);
+}
+
+###############################################################################
+
+sub pop3_test_daemon {
+	my $server = IO::Socket::INET->new(
+		Proto => 'tcp',
+		LocalAddr => '127.0.0.1:8111',
+		Listen => 5,
+		Reuse => 1
+	)
+		or die "Can't create listening socket: $!\n";
+
+	while (my $client = $server->accept()) {
+		$client->autoflush(1);
+		print $client "+OK fake pop3 server ready" . CRLF;
+
+		while (<$client>) {
+			if (/^quit/i) {
+				print $client '+OK quit ok' . CRLF;
+			} elsif (/^user test\@example.com/i) {
+				print $client '+OK user ok' . CRLF;
+			} elsif (/^pass secret/i) {
+				print $client '+OK pass ok' . CRLF;
+			} else {
+				print $client "-ERR unknown command" . CRLF;
+			}
+                }
+
+		close $client;
+	}
+}
+
+###############################################################################
+
+1;
+
+###############################################################################

+ 122 - 0
LibOS/shim/test/apps/nginx/nginx-tests/lib/Test/Nginx/SMTP.pm

@@ -0,0 +1,122 @@
+package Test::Nginx::SMTP;
+
+# (C) Maxim Dounin
+
+# Module for nginx smtp tests.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More qw//;
+use IO::Socket;
+use Socket qw/ CRLF /;
+
+use Test::Nginx;
+
+use base qw/ IO::Socket::INET /;
+
+sub new {
+	my $class = shift;
+
+	my $self = return $class->SUPER::new(
+		Proto => "tcp",
+		PeerAddr => "127.0.0.1:8025",
+		@_
+	)
+		or die "Can't connect to nginx: $!\n";
+
+	$self->autoflush(1);
+
+	return $self;
+}
+
+sub send {
+	my ($self, $cmd) = @_;
+	log_out($cmd);
+	$self->print($cmd . CRLF);
+}
+
+sub read {
+	my ($self) = @_;
+	eval {
+		local $SIG{ALRM} = sub { die "alarm\n" };
+		alarm(2);
+		while (<$self>) {
+			log_in($_);
+			next if m/^\d\d\d-/;
+			last;
+		}
+		alarm(0);
+	};
+	alarm(0);
+	if ($@) {
+		return undef;
+	}
+	return $_;
+}
+
+sub check {
+	my ($self, $regex, $name) = @_;
+	Test::More->builder->like($self->read(), $regex, $name);
+}
+
+sub ok {
+	my $self = shift; 
+	Test::More->builder->like($self->read(), qr/^2\d\d /, @_);
+}
+
+sub authok {
+	my $self = shift; 
+	Test::More->builder->like($self->read(), qr/^235 /, @_);
+}
+
+###############################################################################
+
+sub smtp_test_daemon {
+	my $server = IO::Socket::INET->new(
+		Proto => 'tcp',
+		LocalAddr => '127.0.0.1:8026',
+		Listen => 5,
+		Reuse => 1
+	)
+		or die "Can't create listening socket: $!\n";
+
+	while (my $client = $server->accept()) {
+		$client->autoflush(1);
+		print $client "220 fake esmtp server ready" . CRLF;
+
+		while (<$client>) {
+			Test::Nginx::log_core('||', $_);
+
+			if (/^quit/i) {
+				print $client '221 quit ok' . CRLF;
+			} elsif (/^(ehlo|helo)/i) {
+				print $client '250 hello ok' . CRLF;
+			} elsif (/^rset/i) {
+				print $client '250 rset ok' . CRLF;
+			} elsif (/^mail from:[^@]+$/i) {
+				print $client '500 mail from error' . CRLF;
+			} elsif (/^mail from:/i) {
+				print $client '250 mail from ok' . CRLF;
+			} elsif (/^rcpt to:[^@]+$/i) {
+				print $client '500 rcpt to error' . CRLF;
+			} elsif (/^rcpt to:/i) {
+				print $client '250 rcpt to ok' . CRLF;
+			} elsif (/^xclient/i) {
+				print $client '220 xclient ok' . CRLF;
+			} else {
+				print $client "500 unknown command" . CRLF;
+			}
+                }
+
+		close $client;
+	}
+}
+
+###############################################################################
+
+1;
+
+###############################################################################

+ 92 - 0
LibOS/shim/test/apps/nginx/nginx-tests/limit_req.t

@@ -0,0 +1,92 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for nginx limit_req module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http limit_req/)->plan(5);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    limit_req_zone  $binary_remote_addr  zone=one:10m   rate=1r/s;
+    limit_req_zone  $binary_remote_addr  zone=long:10m  rate=1r/s;
+    limit_req_zone  $binary_remote_addr  zone=fast:10m  rate=1000r/s;
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+        location / {
+            limit_req    zone=one  burst=1  nodelay;
+        }
+        location /long {
+            limit_req    zone=long  burst=5;
+        }
+        location /fast {
+            limit_req    zone=fast  burst=1;
+        }
+    }
+}
+
+EOF
+
+$t->write_file('test1.html', 'XtestX');
+$t->write_file('long.html', "1234567890\n" x (1 << 16));
+$t->write_file('fast.html', 'XtestX');
+$t->run();
+
+###############################################################################
+
+like(http_get('/test1.html'), qr/^HTTP\/1.. 200 /m, 'request');
+http_get('/test1.html');
+like(http_get('/test1.html'), qr/^HTTP\/1.. 503 /m, 'request rejected');
+http_get('/test1.html');
+http_get('/test1.html');
+
+# Second request will be delayed by limit_req, make sure it isn't truncated.
+# The bug only manifests itself if buffer will be filled, so sleep for a while
+# before reading response.
+
+my $l1 = length(http_get('/long.html'));
+my $l2 = length(http_get('/long.html', sleep => 1.1));
+is($l2, $l1, 'delayed big request not truncated');
+
+# make sure rejected requests are not counted, and access is again allowed
+# after 1/rate seconds
+
+like(http_get('/test1.html'), qr/^HTTP\/1.. 200 /m, 'rejects not counted');
+
+# make sure negative excess values are handled properly
+
+http_get('/fast.html');
+select undef, undef, undef, 0.1;
+like(http_get('/fast.html'), qr/^HTTP\/1.. 200 /m, 'negative excess');
+
+###############################################################################

+ 127 - 0
LibOS/shim/test/apps/nginx/nginx-tests/mail_imap.t

@@ -0,0 +1,127 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for nginx mail imap module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+use IO::Socket;
+use MIME::Base64;
+use Socket qw/ CRLF /;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+use Test::Nginx::IMAP;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+local $SIG{PIPE} = 'IGNORE';
+
+my $t = Test::Nginx->new()
+	->has(qw/mail imap http rewrite/)->plan(9)
+	->run_daemon(\&Test::Nginx::IMAP::imap_test_daemon)
+	->write_file_expand('nginx.conf', <<'EOF')->run();
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+mail {
+    proxy_pass_error_message  on;
+    auth_http  http://127.0.0.1:8080/mail/auth;
+
+    server {
+        listen     127.0.0.1:8143;
+        protocol   imap;
+    }
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location = /mail/auth {
+            set $reply ERROR;
+
+            if ($http_auth_smtp_to ~ example.com) {
+                set $reply OK;
+            }
+
+            set $userpass "$http_auth_user:$http_auth_pass";
+            if ($userpass ~ '^test@example.com:secret$') {
+                set $reply OK;
+            }
+
+            add_header Auth-Status $reply;
+            add_header Auth-Server 127.0.0.1;
+            add_header Auth-Port 8144;
+            add_header Auth-Wait 1;
+            return 204;
+        }
+    }
+}
+
+EOF
+
+###############################################################################
+
+my $s = Test::Nginx::IMAP->new();
+$s->ok('greeting');
+
+# bad auth
+
+$s->send('1 AUTHENTICATE');
+$s->check(qr/^\S+ BAD/, 'auth without arguments');
+
+# auth plain
+
+$s->send('1 AUTHENTICATE PLAIN ' . encode_base64("\0test\@example.com\0bad", ''));
+$s->check(qr/^\S+ NO/, 'auth plain with bad password');
+
+$s->send('1 AUTHENTICATE PLAIN ' . encode_base64("\0test\@example.com\0secret", ''));
+$s->ok('auth plain');
+
+# auth login simple
+
+$s = Test::Nginx::IMAP->new();
+$s->read();
+
+$s->send('1 AUTHENTICATE LOGIN');
+$s->check(qr/\+ VXNlcm5hbWU6/, 'auth login username challenge');
+
+$s->send(encode_base64('test@example.com', ''));
+$s->check(qr/\+ UGFzc3dvcmQ6/, 'auth login password challenge');
+
+$s->send(encode_base64('secret', ''));
+$s->ok('auth login simple');
+
+# auth login with username
+
+$s = Test::Nginx::IMAP->new();
+$s->read();
+
+$s->send('1 AUTHENTICATE LOGIN ' . encode_base64('test@example.com', ''));
+$s->check(qr/\+ UGFzc3dvcmQ6/, 'auth login with username password challenge');
+
+$s->send(encode_base64('secret', ''));
+$s->ok('auth login with username');
+
+###############################################################################

+ 122 - 0
LibOS/shim/test/apps/nginx/nginx-tests/mail_pop3.t

@@ -0,0 +1,122 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for nginx mail pop3 module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+use IO::Socket;
+use MIME::Base64;
+use Socket qw/ CRLF /;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+use Test::Nginx::POP3;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+local $SIG{PIPE} = 'IGNORE';
+
+my $t = Test::Nginx->new()
+	->has(qw/mail pop3 http rewrite/)->plan(8)
+	->run_daemon(\&Test::Nginx::POP3::pop3_test_daemon)
+	->write_file_expand('nginx.conf', <<'EOF')->run();
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+mail {
+    proxy_pass_error_message  on;
+    auth_http  http://127.0.0.1:8080/mail/auth;
+
+    server {
+        listen     127.0.0.1:8110;
+        protocol   pop3;
+    }
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location = /mail/auth {
+            set $reply ERROR;
+
+            if ($http_auth_smtp_to ~ example.com) {
+                set $reply OK;
+            }
+
+            set $userpass "$http_auth_user:$http_auth_pass";
+            if ($userpass ~ '^test@example.com:secret$') {
+                set $reply OK;
+            }
+
+            add_header Auth-Status $reply;
+            add_header Auth-Server 127.0.0.1;
+            add_header Auth-Port 8111;
+            add_header Auth-Wait 1;
+            return 204;
+        }
+    }
+}
+
+EOF
+
+###############################################################################
+
+my $s = Test::Nginx::POP3->new();
+$s->ok('greeting');
+
+# auth plain
+
+$s->send('AUTH PLAIN ' . encode_base64("\0test\@example.com\0bad", ''));
+$s->check(qr/^-ERR/, 'auth plain with bad password');
+
+$s->send('AUTH PLAIN ' . encode_base64("\0test\@example.com\0secret", ''));
+$s->ok('auth plain');
+
+# auth login simple
+
+$s = Test::Nginx::POP3->new();
+$s->read();
+
+$s->send('AUTH LOGIN');
+$s->check(qr/\+ VXNlcm5hbWU6/, 'auth login username challenge');
+
+$s->send(encode_base64('test@example.com', ''));
+$s->check(qr/\+ UGFzc3dvcmQ6/, 'auth login password challenge');
+
+$s->send(encode_base64('secret', ''));
+$s->ok('auth login simple');
+
+# auth login with username
+
+$s = Test::Nginx::POP3->new();
+$s->read();
+
+$s->send('AUTH LOGIN ' . encode_base64('test@example.com', ''));
+$s->check(qr/\+ UGFzc3dvcmQ6/, 'auth login with username password challenge');
+
+$s->send(encode_base64('secret', ''));
+$s->ok('auth login with username');
+
+###############################################################################

+ 236 - 0
LibOS/shim/test/apps/nginx/nginx-tests/mail_smtp.t

@@ -0,0 +1,236 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for nginx mail smtp module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+use MIME::Base64;
+use Socket qw/ CRLF /;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+use Test::Nginx::SMTP;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+local $SIG{PIPE} = 'IGNORE';
+
+my $t = Test::Nginx->new()
+	->has(qw/mail smtp http rewrite/)->plan(25)
+	->run_daemon(\&Test::Nginx::SMTP::smtp_test_daemon)
+	->write_file_expand('nginx.conf', <<'EOF')->run();
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+mail {
+    proxy_pass_error_message  on;
+    auth_http  http://127.0.0.1:8080/mail/auth;
+    xclient    off;
+
+    server {
+        listen     127.0.0.1:8025;
+        protocol   smtp;
+        smtp_auth  login plain none;
+    }
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location = /mail/auth {
+            set $reply ERROR;
+
+            if ($http_auth_smtp_to ~ example.com) {
+                set $reply OK;
+            }
+
+            set $userpass "$http_auth_user:$http_auth_pass";
+            if ($userpass ~ '^test@example.com:secret$') {
+                set $reply OK;
+            }
+
+            add_header Auth-Status $reply;
+            add_header Auth-Server 127.0.0.1;
+            add_header Auth-Port 8026;
+            add_header Auth-Wait 1;
+            return 204;
+        }
+    }
+}
+
+EOF
+
+###############################################################################
+
+my $s = Test::Nginx::SMTP->new();
+$s->check(qr/^220 /, "greeting");
+
+$s->send('EHLO example.com');
+$s->check(qr/^250 /, "ehlo");
+
+$s->send('AUTH PLAIN ' . encode_base64("\0test\@example.com\0bad", ''));
+$s->check(qr/^5.. /, 'auth plain with bad password');
+
+$s->send('AUTH PLAIN ' . encode_base64("\0test\@example.com\0secret", ''));
+$s->authok('auth plain');
+
+# We are talking to backend from this point
+
+$s->send('MAIL FROM:<test@example.com> SIZE=100');
+$s->ok('mail from after auth');
+
+$s->send('RSET');
+$s->ok('rset');
+
+$s->send('MAIL FROM:<test@xn--e1afmkfd.xn--80akhbyknj4f> SIZE=100');
+$s->ok("idn mail from (example.test in russian)");
+
+$s->send('QUIT');
+$s->ok("quit");
+
+# Try auth login in simple form
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('EHLO example.com');
+$s->read();
+
+$s->send('AUTH LOGIN');
+$s->check(qr/^334 VXNlcm5hbWU6/, 'auth login simple username challenge');
+$s->send(encode_base64('test@example.com', ''));
+$s->check(qr/^334 UGFzc3dvcmQ6/, 'auth login simple password challenge');
+$s->send(encode_base64('secret', ''));
+$s->authok('auth login simple');
+
+# Try auth plain with username.  Details:
+#
+# [MS-XLOGIN]: SMTP Protocol AUTH LOGIN Extension Specification
+# http://download.microsoft.com/download/5/D/D/5DD33FDF-91F5-496D-9884-0A0B0EE698BB/%5BMS-XLOGIN%5D.pdf
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('EHLO example.com');
+$s->read();
+
+$s->send('AUTH LOGIN ' . encode_base64('test@example.com', ''));
+$s->check(qr/^334 UGFzc3dvcmQ6/, 'auth login with username password challenge');
+$s->send(encode_base64('secret', ''));
+$s->authok('auth login with username');
+
+# Try auth plain with pipelining
+
+TODO: {
+local $TODO = 'pipelining not in official nginx';
+local $SIG{__WARN__} = sub {};
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('EHLO example.com');
+$s->read();
+
+$s->send('INVALID COMMAND WITH ARGUMENTS' . CRLF
+	. 'RSET');
+$s->read();
+$s->ok('pipelined rset after invalid command');
+
+$s->send('AUTH PLAIN '
+	. encode_base64("\0test\@example.com\0bad", '') . CRLF
+	. 'MAIL FROM:<test@example.com> SIZE=100');
+$s->read();
+$s->ok('mail from after failed pipelined auth');
+
+$s->send('AUTH PLAIN '
+	. encode_base64("\0test\@example.com\0secret", '') . CRLF
+	. 'MAIL FROM:<test@example.com> SIZE=100');
+$s->read();
+$s->ok('mail from after pipelined auth');
+
+}
+
+# Try auth none
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('EHLO example.com');
+$s->read();
+
+$s->send('MAIL FROM:<test@example.com> SIZE=100');
+$s->ok('auth none - mail from');
+
+$s->send('RCPT TO:<test@example.com>');
+$s->ok('auth none - rcpt to');
+
+$s->send('RSET');
+$s->ok('auth none - rset, should go to backend');
+
+# Auth none with pipelining
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('EHLO example.com');
+$s->read();
+
+$s->send('MAIL FROM:<test@example.com> SIZE=100' . CRLF
+	. 'RCPT TO:<test@example.com>' . CRLF
+	. 'RSET');
+
+$s->ok('pipelined mail from');
+
+TODO: {
+local $TODO = 'pipelining not in official nginx';
+local $SIG{__WARN__} = sub {};
+
+$s->ok('pipelined rcpt to');
+$s->ok('pipelined rset');
+
+}
+
+# Connection must stay even if error returned to rcpt to command
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('EHLO example.com');
+$s->read();
+
+$s->send('MAIL FROM:<test@example.com> SIZE=100');
+$s->read(); # skip mail from reply
+
+$s->send('RCPT TO:<example.com>');
+$s->check(qr/^5.. /, "bad rcpt to");
+
+$s->send('RCPT TO:<test@example.com>');
+$s->ok('good rcpt to');
+
+# Make sure command splitted into many packets processed correctly
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
+
+log_out('HEL');
+$s->print('HEL');
+$s->send('O example.com');
+$s->ok('splitted command');
+
+###############################################################################

+ 70 - 0
LibOS/shim/test/apps/nginx/nginx-tests/mail_smtp_greeting_delay.t

@@ -0,0 +1,70 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+use Test::Nginx::SMTP;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+local $SIG{PIPE} = 'IGNORE';
+
+my $t = Test::Nginx->new()->has(qw/mail smtp http/)->plan(2)
+	->write_file_expand('nginx.conf', <<'EOF')->run();
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+mail {
+    proxy_pass_error_message  on;
+    auth_http  http://127.0.0.1:8080/mail/auth;
+    xclient    off;
+
+    server {
+        listen     127.0.0.1:8025;
+        protocol   smtp;
+        smtp_greeting_delay  100ms;
+    }
+}
+
+http {
+    # stub to avoid SIGSEGV when perl module compiled in, <= 0.7.30
+}
+
+EOF
+
+###############################################################################
+
+# With smtp_greeting_delay session expected to be closed after first error
+# message if client sent something before greeting.
+
+my $s = Test::Nginx::SMTP->new();
+$s->send('HELO example.com');
+$s->check(qr/^5.. /, "command before greeting - session must be rejected");
+
+TODO: {
+local $TODO = 'not in official nginx yet';
+
+ok($s->eof(), "session have to be closed");
+
+}
+
+###############################################################################

+ 141 - 0
LibOS/shim/test/apps/nginx/nginx-tests/mail_smtp_xclient.t

@@ -0,0 +1,141 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+use MIME::Base64;
+use Socket qw/ CRLF /;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+use Test::Nginx::SMTP;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+local $SIG{PIPE} = 'IGNORE';
+
+my $t = Test::Nginx->new()->has(qw/mail smtp http rewrite/)->plan(6)
+	->run_daemon(\&Test::Nginx::SMTP::smtp_test_daemon)
+	->write_file_expand('nginx.conf', <<'EOF')->run();
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+mail {
+    proxy_pass_error_message  on;
+    auth_http  http://127.0.0.1:8080/mail/auth;
+    xclient    on;
+
+    server {
+        listen     127.0.0.1:8025;
+        protocol   smtp;
+        smtp_auth  login plain none;
+    }
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location = /mail/auth {
+            add_header Auth-Status OK;
+            add_header Auth-Server 127.0.0.1;
+            add_header Auth-Port   8026;
+            add_header Auth-Wait   1;
+            return 204;
+        }
+    }
+}
+
+EOF
+
+###############################################################################
+
+# When XCLIENT's HELO= argument isn't used, the  following combinations may be
+# send to backend with xclient on:
+#
+# xclient
+# xclient, helo
+# xclient, ehlo
+# xclient, from, rcpt
+# xclient, helo, from, rcpt
+# xclient, ehlo, from, rcpt
+#
+# Test them in order.
+
+# xclient
+
+my $s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('AUTH PLAIN ' . encode_base64("\0test\@example.com\0secret", ''));
+$s->authok('xclient');
+
+# xclient, helo
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('HELO example.com');
+$s->read();
+$s->send('AUTH PLAIN ' . encode_base64("\0test\@example.com\0secret", ''));
+$s->authok('xclient, helo');
+
+# xclient, ehlo
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('EHLO example.com');
+$s->read();
+$s->send('AUTH PLAIN ' . encode_base64("\0test\@example.com\0secret", ''));
+$s->authok('xclient, ehlo');
+
+# xclient, from, rcpt
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('MAIL FROM:<test@example.com>');
+$s->read();
+$s->send('RCPT TO:<test@example.com>');
+$s->ok('xclient, from');
+
+# xclient, helo, from, rcpt
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('HELO example.com');
+$s->read();
+$s->send('MAIL FROM:<test@example.com>');
+$s->read();
+$s->send('RCPT TO:<test@example.com>');
+$s->ok('xclient, helo, from');
+
+# xclient, ehlo, from, rcpt
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('EHLO example.com');
+$s->read();
+$s->send('MAIL FROM:<test@example.com>');
+$s->read();
+$s->send('RCPT TO:<test@example.com>');
+$s->ok('xclient, ehlo, from');
+
+###############################################################################

+ 93 - 0
LibOS/shim/test/apps/nginx/nginx-tests/memcached.t

@@ -0,0 +1,93 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Test for memcached backend.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+eval { require Cache::Memcached; };
+plan(skip_all => 'Cache::Memcached not installed') if $@;
+
+my $t = Test::Nginx->new()->has(qw/http rewrite memcached/)
+	->has_daemon('memcached')->plan(4)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            set $memcached_key $uri;
+            memcached_pass 127.0.0.1:8081;
+        }
+
+        location /next {
+            set $memcached_key $uri;
+            memcached_next_upstream  not_found;
+            memcached_pass 127.0.0.1:8081;
+        }
+    }
+}
+
+EOF
+
+my $memhelp = `memcached -h`;
+my @memopts = ();
+
+if ($memhelp =~ /repcached/) {
+	# repcached patch adds additional listen socket
+	push @memopts, '-X', '8082';
+}
+if ($memhelp =~ /-U/) {
+	# UDP port is on by default in memcached 1.2.7+
+	push @memopts, '-U', '0';
+}
+
+$t->run_daemon('memcached', '-l', '127.0.0.1', '-p', '8081', @memopts);
+$t->run();
+
+$t->waitforsocket('127.0.0.1:8081')
+	or die "Can't start memcached";
+
+###############################################################################
+
+my $memd = Cache::Memcached->new(servers => [ '127.0.0.1:8081' ]);
+$memd->set('/', 'SEE-THIS')
+	or die "can't put value into memcached: $!";
+
+like(http_get('/'), qr/SEE-THIS/, 'memcached request');
+
+like(http_get('/notfound'), qr/404/, 'memcached not found');
+
+like(http_get('/next'), qr/404/, 'not found with memcached_next_upstream');
+
+unlike(http_head('/'), qr/SEE-THIS/, 'memcached no data in HEAD');
+
+###############################################################################

+ 99 - 0
LibOS/shim/test/apps/nginx/nginx-tests/memcached_fake.t

@@ -0,0 +1,99 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Test for memcached backend with fake daemon.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+use Socket qw/ CRLF /;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http rewrite memcached ssi/)->plan(3)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            set $memcached_key $uri;
+            memcached_pass 127.0.0.1:8081;
+        }
+
+        location /ssi {
+            default_type text/html;
+            ssi on;
+        }
+    }
+}
+
+EOF
+
+$t->write_file('ssi.html', '<!--#include virtual="/" set="blah" -->blah: <!--#echo var="blah" -->');
+$t->run_daemon(\&memcached_fake_daemon);
+$t->run();
+
+###############################################################################
+
+like(http_get('/'), qr/SEE-THIS/, 'memcached split trailer');
+
+like(http_get('/ssi.html'), qr/SEE-THIS/, 'memcached ssi var');
+
+like(`grep -F '[error]' ${\($t->testdir())}/error.log`, qr/^$/s, 'no error');
+
+###############################################################################
+
+sub memcached_fake_daemon {
+	my $server = IO::Socket::INET->new(
+		Proto => 'tcp',
+		LocalAddr => '127.0.0.1:8081',
+		Listen => 5,
+		Reuse => 1
+	)
+		or die "Can't create listening socket: $!\n";
+
+	while (my $client = $server->accept()) {
+		$client->autoflush(1);
+
+		while (<$client>) {
+			last if (/\x0d\x0a$/);
+		}
+
+		print $client 'VALUE / 0 8' . CRLF;
+		print $client 'SEE-TH';
+		select(undef, undef, undef, 0.1);
+		print $client 'IS';
+		select(undef, undef, undef, 0.1);
+		print $client CRLF . 'EN';
+		select(undef, undef, undef, 0.1);
+		print $client 'D' . CRLF;
+		close $client;
+	}
+}
+
+###############################################################################

+ 82 - 0
LibOS/shim/test/apps/nginx/nginx-tests/not_modified.t

@@ -0,0 +1,82 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for not modified filter module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has('http')->plan(4)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            if_modified_since before;
+        }
+    }
+}
+
+EOF
+
+$t->write_file('t', '');
+
+$t->run();
+
+###############################################################################
+
+like(http_get_ims('/t', 'Wed, 08 Jul 2037 22:53:52 GMT'), qr/304/,
+	'0x7F000000');
+like(http_get_ims('/t', 'Tue, 19 Jan 2038 03:14:07 GMT'), qr/304/,
+	'0x7FFFFFFF');
+
+SKIP: {
+	skip "only for 32-bit time_t", 2 if (gmtime(0xFFFFFFFF))[5] == 206;
+
+	like(http_get_ims('/t', 'Tue, 19 Jan 2038 03:14:08 GMT'), qr/200/,
+		'0x7FFFFFFF + 1');
+	like(http_get_ims('/t', 'Fri, 25 Feb 2174 09:42:23 GMT'), qr/200/,
+		'0x17FFFFFFF');
+}
+
+###############################################################################
+
+sub http_get_ims {
+        my ($url, $ims) = @_;
+        return http(<<EOF);
+GET $url HTTP/1.0
+Host: localhost
+If-Modified-Since: $ims
+
+EOF
+}
+
+###############################################################################

+ 72 - 0
LibOS/shim/test/apps/nginx/nginx-tests/perl.t

@@ -0,0 +1,72 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for embedded perl module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http perl rewrite/)->plan(1)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            set $testvar "TEST";
+            perl 'sub {
+                use warnings;
+                use strict;
+
+                my $r = shift;
+
+                $r->send_http_header("text/plain");
+
+                return OK if $r->header_only;
+
+                my $v = $r->variable("testvar");
+
+                $r->print("$v");
+
+                return OK;
+            }';
+        }
+    }
+}
+
+EOF
+
+$t->run();
+
+###############################################################################
+
+like(http_get('/'), qr/TEST/, 'perl response');
+
+###############################################################################

+ 90 - 0
LibOS/shim/test/apps/nginx/nginx-tests/perl_gzip.t

@@ -0,0 +1,90 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for embedded perl module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx qw/ :DEFAULT :gzip /;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+eval { require IO::Compress::Gzip; };
+Test::More::plan(skip_all => "IO::Compress::Gzip not found") if $@;
+
+my $t = Test::Nginx->new()->has(qw/http perl gzip/)->plan(2)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        gzip on;
+        gzip_types text/plain;
+
+        location / {
+            perl 'sub {
+                my $r = shift;
+                $r->send_http_header("text/plain");
+                return OK if $r->header_only;
+                $r->print("TEST");
+                return OK;
+            }';
+        }
+
+        location /gz {
+            perl 'sub {
+                my $r = shift;
+                $r->header_out("Content-Encoding", "gzip");
+                $r->send_http_header("text/plain");
+                return OK if $r->header_only;
+                use IO::Compress::Gzip;
+                my $in = "TEST";
+                my $out;
+                IO::Compress::Gzip::gzip(\\$in => \\$out);
+                $r->print($out);
+                return OK;
+            }';
+        }
+    }
+}
+
+EOF
+
+$t->run();
+
+###############################################################################
+
+http_gzip_like(http_gzip_request('/'), qr/TEST/, 'perl response gzipped');
+
+TODO: {
+local $TODO = 'patch pending';
+
+http_gzip_like(http_gzip_request('/gz'), qr/TEST/, 'not doublegzipped');
+
+}
+
+###############################################################################

+ 121 - 0
LibOS/shim/test/apps/nginx/nginx-tests/proxy.t

@@ -0,0 +1,121 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for http proxy module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http proxy/)->plan(3);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            proxy_pass http://127.0.0.1:8081;
+            proxy_read_timeout 1s;
+        }
+    }
+}
+
+EOF
+
+$t->run_daemon(\&http_daemon);
+$t->run();
+
+###############################################################################
+
+like(http_get('/'), qr/SEE-THIS/, 'proxy request');
+like(http_get('/multi'), qr/AND-THIS/, 'proxy request with multiple packets');
+
+unlike(http_head('/'), qr/SEE-THIS/, 'proxy head request');
+
+###############################################################################
+
+sub http_daemon {
+	my $server = IO::Socket::INET->new(
+		Proto => 'tcp',
+		LocalHost => '127.0.0.1:8081',
+		Listen => 5,
+		Reuse => 1
+	)
+		or die "Can't create listening socket: $!\n";
+
+	while (my $client = $server->accept()) {
+		$client->autoflush(1);
+
+		my $headers = '';
+		my $uri = '';
+
+		while (<$client>) {
+			$headers .= $_;
+			last if (/^\x0d?\x0a?$/);
+		}
+
+		$uri = $1 if $headers =~ /^\S+\s+([^ ]+)\s+HTTP/i;
+
+		if ($uri eq '/') {
+			print $client <<'EOF';
+HTTP/1.1 200 OK
+Connection: close
+
+EOF
+			print $client "TEST-OK-IF-YOU-SEE-THIS"
+				unless $headers =~ /^HEAD/i;
+
+		} elsif ($uri eq '/multi') {
+
+			print $client <<"EOF";
+HTTP/1.1 200 OK
+Connection: close
+
+TEST-OK-IF-YOU-SEE-THIS
+EOF
+
+			select undef, undef, undef, 0.1;
+			print $client 'AND-THIS';
+
+		} else {
+
+			print $client <<"EOF";
+HTTP/1.1 404 Not Found
+Connection: close
+
+Oops, '$uri' not found
+EOF
+		}
+
+		close $client;
+	}
+}
+
+###############################################################################

+ 174 - 0
LibOS/shim/test/apps/nginx/nginx-tests/proxy_cache.t

@@ -0,0 +1,174 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for http proxy cache.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx qw/ :DEFAULT :gzip /;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http proxy cache gzip/)->plan(12)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2
+                       keys_zone=NAME:10m;
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        gzip on;
+        gzip_min_length 0;
+
+        location / {
+            proxy_pass    http://127.0.0.1:8081;
+
+            proxy_cache   NAME;
+
+            proxy_cache_valid   200 302  1s;
+            proxy_cache_valid   301      1d;
+            proxy_cache_valid   any      1m;
+
+            proxy_cache_min_uses  1;
+
+            proxy_cache_use_stale  error timeout invalid_header http_500
+                                   http_404;
+        }
+
+        location /fake/ {
+            proxy_pass    http://127.0.0.1:8082;
+            proxy_cache   NAME;
+        }
+    }
+    server {
+        listen       127.0.0.1:8081;
+        server_name  localhost;
+
+        location / {
+        }
+    }
+}
+
+EOF
+
+$t->write_file('t.html', 'SEE-THIS');
+$t->write_file('t2.html', 'SEE-THIS');
+$t->write_file('empty.html', '');
+$t->run_daemon(\&http_fake_daemon);
+$t->run();
+
+###############################################################################
+
+like(http_get('/t.html'), qr/SEE-THIS/, 'proxy request');
+
+$t->write_file('t.html', 'NOOP');
+like(http_get('/t.html'), qr/SEE-THIS/, 'proxy request cached');
+
+unlike(http_head('/t2.html'), qr/SEE-THIS/, 'head request');
+like(http_get('/t2.html'), qr/SEE-THIS/, 'get after head');
+unlike(http_head('/t2.html'), qr/SEE-THIS/, 'head after get');
+
+like(http_get_range('/t.html', 'Range: bytes=4-'), qr/^THIS/m, 'cached range');
+like(http_get_range('/t.html', 'Range: bytes=0-2,4-'), qr/^SEE.*^THIS/ms,
+	'cached multipart range');
+
+like(http_get('/empty.html'), qr/HTTP/, 'empty get first');
+like(http_get('/empty.html'), qr/HTTP/, 'empty get second');
+
+{
+local $TODO = 'not fixed yet';
+
+sleep(2);
+unlink $t->testdir() . '/t.html';
+like(http_gzip_request('/t.html'),
+	qr/HTTP.*1c\x0d\x0a.{28}\x0d\x0a0\x0d\x0a\x0d\x0a\z/s,
+	'non-empty get stale');
+}
+
+{
+local $TODO = 'broken in 0.8.31';
+
+unlink $t->testdir() . '/empty.html';
+like(http_gzip_request('/empty.html'),
+	qr/HTTP.*14\x0d\x0a.{20}\x0d\x0a0\x0d\x0a\x0d\x0a\z/s,
+	'empty get stale');
+}
+
+{
+local $TODO = 'patch pending';
+
+http_get('/fake/unfinished');
+like(http_get('/fake/unfinished'), qr/unfinished 2/, 'unfinished not cached');
+}
+
+###############################################################################
+
+sub http_get_range {
+        my ($url, $extra) = @_;
+        return http(<<EOF);
+GET $url HTTP/1.1
+Host: localhost
+Connection: close
+$extra
+
+EOF
+}
+
+###############################################################################
+
+sub http_fake_daemon {
+	my $server = IO::Socket::INET->new(
+		Proto => 'tcp',
+		LocalAddr => '127.0.0.1:8082',
+		Listen => 5,
+		Reuse => 1
+	)
+		or die "Can't create listening socket: $!\n";
+
+	my $num = 0;
+
+	while (my $client = $server->accept()) {
+		$client->autoflush(1);
+
+		while (<$client>) {
+			last if (/^\x0d?\x0a?$/);
+		}
+
+		$num++;
+		print $client <<"EOF";
+HTTP/1.1 200 OK
+Content-Length: 100
+Cache-Control: max-age=300
+Connection: close
+
+unfinished $num
+EOF
+	}
+}
+
+###############################################################################

+ 116 - 0
LibOS/shim/test/apps/nginx/nginx-tests/proxy_chunked.t

@@ -0,0 +1,116 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Test for http backend returning response with Transfer-Encoding: chunked.
+
+# Since nginx uses HTTP/1.0 in requests to backend it's backend bug, but we
+# want to handle this gracefully.  And anyway chunked support will be required
+# for HTTP/1.1 backend connections.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+use IO::Select;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http proxy ssi/)->plan(3);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            proxy_pass http://127.0.0.1:8081;
+            proxy_read_timeout 1s;
+        }
+        location /nobuffering {
+            proxy_pass http://127.0.0.1:8081;
+            proxy_read_timeout 1s;
+            proxy_buffering off;
+        }
+        location /inmemory.html {
+            ssi on;
+        }
+    }
+}
+
+EOF
+
+$t->write_file('inmemory.html',
+	'<!--#include virtual="/" set="one" --><!--#echo var="one" -->');
+
+$t->run_daemon(\&http_chunked_daemon);
+$t->run();
+
+###############################################################################
+
+{
+local $TODO = 'not yet';
+
+like(http_get('/'), qr/\x0d\x0aSEE-THIS$/s, 'chunked');
+like(http_get('/nobuffering'), qr/\x0d\x0aSEE-THIS$/s, 'chunked nobuffering');
+like(http_get('/inmemory.html'), qr/\x0d\x0aSEE-THIS$/s, 'chunked inmemory');
+}
+
+###############################################################################
+
+sub http_chunked_daemon {
+	my $server = IO::Socket::INET->new(
+		Proto => 'tcp',
+		LocalAddr => '127.0.0.1:8081',
+		Listen => 5,
+		Reuse => 1
+	)
+		or die "Can't create listening socket: $!\n";
+
+	while (my $client = $server->accept()) {
+		$client->autoflush(1);
+
+		while (<$client>) {
+			last if (/^\x0d?\x0a?$/);
+		}
+
+		print $client <<'EOF';
+HTTP/1.1 200 OK
+Connection: close
+Transfer-Encoding: chunked
+
+9
+SEE-THIS
+
+0
+
+EOF
+
+		close $client;
+	}
+}
+
+###############################################################################

+ 149 - 0
LibOS/shim/test/apps/nginx/nginx-tests/proxy_noclose.t

@@ -0,0 +1,149 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Test for http backend not closing connection properly after sending full
+# reply.  This is in fact backend bug, but it seems common, and anyway
+# correct handling is required to support persistent connections.
+
+# There are actually 2 nginx problems here:
+#
+# 1. It doesn't send reply in-time even if got Content-Length and all the data.
+#
+# 2. If upstream times out some data may be left in input buffer and won't be
+#    sent to downstream.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+use IO::Select;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http proxy/)->plan(4);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            proxy_pass http://127.0.0.1:8081;
+            proxy_read_timeout 1s;
+        }
+
+        location /uselen {
+            proxy_pass http://127.0.0.1:8081;
+
+            # test will wait only 2s for reply, we it will fail if
+            # Content-Length not used as a hint
+
+            proxy_read_timeout 10s;
+        }
+    }
+}
+
+EOF
+
+$t->run_daemon(\&http_noclose_daemon);
+$t->run();
+
+###############################################################################
+
+TODO: {
+local $TODO = 'not fixed yet, patches under review';
+local $SIG{__WARN__} = sub {};
+
+like(http_get('/'), qr/SEE-THIS/, 'request to bad backend');
+like(http_get('/multi'), qr/AND-THIS/, 'bad backend - multiple packets');
+like(http_get('/nolen'), qr/SEE-THIS/, 'bad backend - no content length');
+like(http_get('/uselen'), qr/SEE-THIS/, 'content-length actually used');
+
+}
+
+###############################################################################
+
+sub http_noclose_daemon {
+	my $server = IO::Socket::INET->new(
+		Proto => 'tcp',
+		LocalAddr => '127.0.0.1:8081',
+		Listen => 5,
+		Reuse => 1
+	)
+		or die "Can't create listening socket: $!\n";
+
+	while (my $client = $server->accept()) {
+		$client->autoflush(1);
+
+		my $multi = 0;
+		my $nolen = 0;
+
+		while (<$client>) {
+			$multi = 1 if /multi/;
+			$nolen = 1 if /nolen/;
+			last if (/^\x0d?\x0a?$/);
+		}
+
+		if ($nolen) {
+
+			print $client <<'EOF';
+HTTP/1.1 200 OK
+Connection: close
+
+TEST-OK-IF-YOU-SEE-THIS
+EOF
+		} elsif ($multi) {
+
+			print $client <<"EOF";
+HTTP/1.1 200 OK
+Content-Length: 32
+Connection: close
+
+TEST-OK-IF-YOU-SEE-THIS
+EOF
+
+			select undef, undef, undef, 0.1;
+			print $client 'AND-THIS';
+
+		} else {
+
+			print $client <<"EOF";
+HTTP/1.1 200 OK
+Content-Length: 24
+Connection: close
+
+TEST-OK-IF-YOU-SEE-THIS
+EOF
+		}
+
+		my $select = IO::Select->new($client);
+		$select->can_read(10);
+		close $client;
+	}
+}
+
+###############################################################################

+ 103 - 0
LibOS/shim/test/apps/nginx/nginx-tests/proxy_store.t

@@ -0,0 +1,103 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for proxy_store functionality.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new();
+
+$t->write_file_expand('nginx.conf', <<'EOF')->has(qw/http proxy ssi/)->plan(7);
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location /store- {
+            proxy_pass http://127.0.0.1:8080/;
+            proxy_store on;
+        }
+        location /ssi.html {
+            ssi on;
+        }
+        location /index-nostore.html {
+            add_header  X-Accel-Expires  0;
+        }
+        location /index-big.html {
+            limit_rate  200k;
+        }
+    }
+}
+
+EOF
+
+$t->write_file('index.html', 'SEE-THIS');
+$t->write_file('index-nostore.html', 'SEE-THIS');
+$t->write_file('index-big.html', 'x' x (100 << 10));
+$t->write_file('ssi.html',
+	'<!--#include virtual="/store-index-big.html?1" -->' .
+	'<!--#include virtual="/store-index-big.html?2" -->'
+);
+$t->run();
+
+###############################################################################
+
+like(http_get('/store-index.html'), qr/SEE-THIS/, 'proxy request');
+ok(-e $t->testdir() . '/store-index.html', 'result stored');
+
+like(http_get('/store-index-nostore.html'), qr/SEE-THIS/,
+	'proxy request with x-accel-expires');
+
+TODO: {
+local $TODO = 'patch under review';
+
+ok(!-e $t->testdir() . '/store-index-nostore.html', 'result not stored');
+}
+
+ok(scalar @{[ glob $t->testdir() . '/proxy_temp/*' ]} == 0, 'no temp files');
+
+http_get('/store-index-big.html', aborted => 1, sleep => 0.1);
+sleep(1);
+
+ok(scalar @{[ glob $t->testdir() . '/proxy_temp/*' ]} == 0,
+	'no temp files after aborted request');
+
+TODO: {
+local $TODO = 'not fixed yet';
+
+http_get('/ssi.html', aborted => 1, sleep => 0.1);
+sleep(1);
+
+ok(scalar @{[ glob $t->testdir() . '/proxy_temp/*' ]} == 0,
+	'no temp files after aborted ssi');
+
+}
+
+###############################################################################

+ 84 - 0
LibOS/shim/test/apps/nginx/nginx-tests/proxy_xar.t

@@ -0,0 +1,84 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for proxy X-Accel-Redirect functionality.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http proxy rewrite/)->plan(8);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location /proxy {
+            proxy_pass http://127.0.0.1:8080/return-xar;
+        }
+        location /return-xar {
+            add_header  X-Accel-Redirect     /index.html;
+
+            # this headers will be preserved on
+            # X-Accel-Redirect
+
+            add_header  Content-Type         text/blah;
+            add_header  Set-Cookie           blah=blah;
+            add_header  Content-Disposition  attachment;
+            add_header  Cache-Control        no-cache;
+            add_header  Expires              fake;
+            add_header  Accept-Ranges        parrots;
+
+            # others won't be
+            add_header  Something            other;
+
+            return 204;
+        }
+    }
+}
+
+EOF
+
+$t->write_file('index.html', 'SEE-THIS');
+$t->run();
+
+###############################################################################
+
+my $r = http_get('/proxy');
+like($r, qr/SEE-THIS/, 'X-Accel-Redirect works');
+like($r, qr/^Content-Type: text\/blah/m, 'Content-Type preserved');
+like($r, qr/^Set-Cookie: blah=blah/m, 'Set-Cookie preserved');
+like($r, qr/^Content-Disposition: attachment/m, 'Content-Disposition preserved');
+like($r, qr/^Cache-Control: no-cache/m, 'Cache-Control preserved');
+like($r, qr/^Expires: fake/m, 'Expires preserved');
+like($r, qr/^Accept-Ranges: parrots/m, 'Accept-Ranges preserved');
+unlike($r, qr/^Something/m, 'other headers stripped');
+
+###############################################################################

+ 65 - 0
LibOS/shim/test/apps/nginx/nginx-tests/random_index.t

@@ -0,0 +1,65 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for random index module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http random_index/)->plan(1)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            random_index on;
+        }
+    }
+}
+
+EOF
+
+my $d = $t->testdir();
+
+mkdir("$d/x");
+mkdir("$d/x/test-dir");
+symlink("$d/x/test-dir", "$d/x/test-dir-link");
+
+$t->write_file('test-file', 'RIGHT');
+symlink("$d/test-file", "$d/x/test-file-link");
+
+$t->run();
+
+###############################################################################
+
+like(http_get('/x/'), qr/RIGHT/s, 'file');
+
+###############################################################################

+ 120 - 0
LibOS/shim/test/apps/nginx/nginx-tests/range.t

@@ -0,0 +1,120 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for range filter module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http charset/)->plan(25);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    charset_map B A {
+        58 59; # X -> Y
+    }
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location /t2.html {
+            charset A;
+            source_charset B;
+        }
+    }
+}
+
+EOF
+
+$t->write_file('t1.html',
+	join('', map { sprintf "X%03dXXXXXX", $_ } (0 .. 99)));
+$t->write_file('t2.html',
+	join('', map { sprintf "X%03dXXXXXX", $_ } (0 .. 99)));
+$t->run();
+
+###############################################################################
+
+my $t1;
+
+$t1 = http_get_range('/t1.html', 'Range: bytes=0-8');
+like($t1, qr/206/, 'range request - 206 partial reply');
+like($t1, qr/Content-Length: 9/, 'range request - correct length');
+like($t1, qr/Content-Range: bytes 0-8\/1000/, 'range request - content range');
+like($t1, qr/^X000XXXXX$/m, 'range request - correct content');
+
+$t1 = http_get_range('/t1.html', 'Range: bytes=-10');
+like($t1, qr/206/, 'final bytes - 206 partial reply');
+like($t1, qr/Content-Length: 10/, 'final bytes - content length');
+like($t1, qr/Content-Range: bytes 990-999\/1000/,
+	'final bytes - content range');
+like($t1, qr/^X099XXXXXX$/m, 'final bytes - correct content');
+
+$t1 = http_get_range('/t1.html', 'Range: bytes=990-');
+like($t1, qr/206/, 'final bytes explicit - 206 partial reply');
+like($t1, qr/Content-Length: 10/, 'final bytes explicit - content length');
+like($t1, qr/Content-Range: bytes 990-999\/1000/,
+	'final bytes explicit - content range');
+like($t1, qr/^X099XXXXXX$/m, 'final bytes explicit - correct content');
+
+$t1 = http_get_range('/t1.html', 'Range: bytes=990-1990');
+like($t1, qr/206/, 'more than length - 206 partial reply');
+like($t1, qr/Content-Length: 10/, 'more than length - content length');
+like($t1, qr/Content-Range: bytes 990-999\/1000/,
+	'more than length - content range');
+like($t1, qr/^X099XXXXXX$/m, 'more than length - correct content');
+
+$t1 = http_get_range('/t2.html', 'Range: bytes=990-1990');
+like($t1, qr/206/, 'recoded - 206 partial reply');
+like($t1, qr/Content-Length: 10/, 'recoded - content length');
+like($t1, qr/Content-Range: bytes 990-999\/1000/, 'recoded - content range');
+like($t1, qr/^Y099YYYYYY$/m, 'recoded - correct content');
+
+$t1 = http_get_range('/t1.html', 'Range: bytes=0-9, -10, 10-19');
+like($t1, qr/206/, 'multipart - 206 partial reply');
+like($t1, qr/Content-Type: multipart\/byteranges; boundary=/,
+	'multipart - content type');
+like($t1, qr/X000XXXXXX/m, 'multipart - content 0-9');
+like($t1, qr/^X099XXXXXX\x0d?$/m, 'multipart - content -10 aka 990-999');
+like($t1, qr/X001XXXXXX\x0d?$/m, 'multipart - content 10-19');
+
+###############################################################################
+
+sub http_get_range {
+	my ($url, $extra) = @_;
+	return http(<<EOF);
+GET $url HTTP/1.1
+Host: localhost
+Connection: close
+$extra
+
+EOF
+}
+
+###############################################################################

+ 93 - 0
LibOS/shim/test/apps/nginx/nginx-tests/range_flv.t

@@ -0,0 +1,93 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for range filter module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http flv/)->plan(12);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+        location / {
+            flv;
+        }
+    }
+}
+
+EOF
+
+$t->write_file('t1.flv',
+	join('', map { sprintf "X%03dXXXXXX", $_ } (0 .. 99)));
+$t->run();
+
+###############################################################################
+
+my $t1;
+
+# FLV has 13 byte header at start.
+
+$t1 = http_get_range('/t1.flv?start=100', 'Range: bytes=0-9');
+like($t1, qr/206/, 'first bytes - 206 partial reply');
+like($t1, qr/Content-Length: 10/, 'first bytes - correct length');
+like($t1, qr/Content-Range: bytes 0-9\/913/, 'first bytes - content range');
+like($t1, qr/^FLV.{7}$/m, 'first bytes - correct content');
+
+$t1 = http_get_range('/t1.flv?start=100', 'Range: bytes=-10');
+like($t1, qr/206/, 'final bytes - 206 partial reply');
+like($t1, qr/Content-Length: 10/, 'final bytes - content length');
+like($t1, qr/Content-Range: bytes 903-912\/913/,
+	'final bytes - content range');
+like($t1, qr/^X099XXXXXX$/m, 'final bytes - correct content');
+
+$t1 = http_get_range('/t1.flv?start=100', 'Range: bytes=0-99');
+like($t1, qr/206/, 'multi buffers - 206 partial reply');
+like($t1, qr/Content-Length: 100/, 'multi buffers - content length');
+like($t1, qr/Content-Range: bytes 0-99\/913/, 'multi buffers - content range');
+like($t1, qr/^FLV.{10}X010XXXXXX(X01[1-7]XXXXXX){7}X018XXX$/m,
+	'multi buffers - correct content');
+
+###############################################################################
+
+sub http_get_range {
+	my ($url, $extra) = @_;
+	return http(<<EOF);
+GET $url HTTP/1.1
+Host: localhost
+Connection: close
+$extra
+
+EOF
+}
+
+###############################################################################

+ 226 - 0
LibOS/shim/test/apps/nginx/nginx-tests/rewrite.t

@@ -0,0 +1,226 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for rewrite module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(19)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            rewrite ^ http://example.com/ redirect;
+        }
+
+        location /add {
+            rewrite ^ http://example.com/?c=d redirect;
+        }
+
+        location /no {
+            rewrite ^ http://example.com/?c=d? redirect;
+        }
+
+        location /return204 {
+            return 204;
+        }
+
+        location /return200 {
+            return 200;
+        }
+
+        location /return405 {
+            return 405;
+        }
+
+        location /error404return405 {
+            error_page 404 /return405;
+            return 404;
+        }
+
+        location /error405return204 {
+            error_page 405 /return204;
+            return 405;
+        }
+
+        location /error405return200 {
+            error_page 405 /return200;
+            return 405;
+        }
+
+        location /return200text {
+            return 200 "text";
+        }
+
+        location /return404text {
+            return 404 "text";
+        }
+
+        location /return302text {
+            return 302 "text";
+        }
+
+        location /error405return200text {
+            error_page 405 /return200text;
+            return 405;
+        }
+
+        location /error302return200text {
+            error_page 302 /return200text;
+            return 302 "text";
+        }
+
+        location /error405return302text {
+            error_page 405 /return302text;
+            return 405;
+        }
+
+        location /error405rewrite {
+            error_page 405 /;
+            return 405;
+        }
+
+        location /error405directory {
+            error_page 405 /directory;
+            return 405;
+        }
+
+        location /directory {
+        }
+    }
+}
+
+EOF
+
+mkdir($t->testdir() . '/directory');
+
+$t->run();
+
+###############################################################################
+
+like(http_get('/'), qr!^Location: http://example.com/\x0d?$!ms, 'simple');
+like(http_get('/?a=b'), qr!^Location: http://example.com/\?a=b\x0d?$!ms,
+	'simple with args');
+like(http_get('/add'), qr!^Location: http://example.com/\?c=d\x0d?$!ms,
+	'add args');
+
+like(http_get('/add?a=b'), qr!^Location: http://example.com/\?c=d&a=b\x0d?$!ms,
+	'add args with args');
+
+like(http_get('/no?a=b'), qr!^Location: http://example.com/\?c=d\x0d?$!ms,
+	'no args with args');
+
+like(http_get('/return204'), qr!204 No Content!, 'return 204');
+like(http_get('/return200'), qr!200 OK!, 'return 200');
+like(http_get('/return405'), qr!HTTP/1.1 405.*body!ms, 'return 405');
+
+like(http_get('/error404return405'), qr!HTTP/1.1 404!, 'error 404 return 405');
+
+TODO: {
+local $TODO = 'not yet';
+
+# status code should be 405, and entity body is expected (vs. normal 204
+# replies which doesn't expect to have body); use HTTP/1.1 for test
+# to make problem clear
+
+my $r = http(<<EOF);
+GET /error405return204 HTTP/1.1
+Host: localhost
+Connection: close
+
+EOF
+
+like($r, qr/HTTP\/1.1 405.*(Content-Length|\x0d\0a0\x0d\x0a)/ms,
+	'error 405 return 204');
+
+# the same test, but with return 200.  this doesn't have special
+# handling and returns builtin error page body (the same problem as
+# in /error405return200text below)
+
+like(http_get('/error405return200'), qr/HTTP\/1.1 405(?!.*body)/ms,
+	'error 405 return 200');
+
+}
+
+# tests involving return with two arguments, as introduced in
+# 0.8.42
+
+like(http_get('/return200text'), qr!text\z!, 'return 200 text');
+like(http_get('/return404text'), qr!text\z!, 'return 404 text');
+
+TODO: {
+local $TODO = 'not yet';
+
+like(http_get('/error405return200text'), qr!HTTP/1.1 405.*text\z!ms,
+	'error 405 to return 200 text');
+
+}
+
+# return 302 is somewhat special: it adds Location header instead of
+# body text.  additionally it doesn't sent reply directly (as it's done for
+# other returns since 0.8.42) but instead returns NGX_HTTP_* code
+
+like(http_get('/return302text'), qr!HTTP/1.1 302.*Location: text!ms,
+	'return 302 text');
+
+TODO: {
+local $TODO = 'not yet';
+
+like(http_get('/error302return200text'),
+	qr!HTTP/1.1 302.*Location: text.*text\z!ms,
+	'error 302 return 200 text');
+
+}
+
+TODO: {
+local $TODO = 'not yet';
+
+# in contrast to other return's this shouldn't preserve original status code
+# from error, and the same applies to "rewrite ... redirect" as an error
+# handler; both should in line with e.g. directory redirect as well
+
+like(http_get('/error405return302text'),
+	qr!HTTP/1.1 302.*Location: text!ms,
+	'error 405 return 302 text');
+
+like(http_get('/error405rewrite'),
+	qr!HTTP/1.1 302.*Location: http://example.com/!ms,
+	'error 405 rewrite redirect');
+
+}
+
+like(http_get('/error405directory'),
+	qr!HTTP/1.1 301.*Location: http://!ms,
+	'error 405 directory redirect');
+
+###############################################################################

+ 164 - 0
LibOS/shim/test/apps/nginx/nginx-tests/rewrite_unescape.t

@@ -0,0 +1,164 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for escaping/unescaping in rewrite module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(9)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location /t1 {
+            rewrite ^ $arg_r? redirect;
+        }
+
+        location /t2 {
+            rewrite ^ http://example.com$request_uri? redirect;
+        }
+
+        location /t3 {
+            rewrite ^ http://example.com$uri redirect;
+        }
+
+        location /t4 {
+            rewrite ^(.*) http://example.com$1 redirect;
+        }
+
+        location /t5 {
+            rewrite ^ http://example.com/blah%20%3Fblah redirect;
+        }
+
+        location /t6 {
+            rewrite ^ http://example.com/blah%20%2Fblah redirect;
+        }
+    }
+}
+
+EOF
+
+mkdir($t->testdir() . '/directory');
+
+$t->run();
+
+###############################################################################
+
+# Some rewrites and expected (?) behaviour
+# 
+# /t1?r=http%3A%2F%2Fexample.com%2F%3Ffrom
+# rewrite ^ $arg_r? redirect;
+# expected: http://example.com/?from
+# got:      http://example.com/?from
+#
+# /t1?r=http%3A%2F%2Fexample.com%0D%0Asplit
+# rewrite ^ $arg_r? redirect;
+# expected: http://example.com%0D%0Asplit
+# got:      http://example.com%0D%0Asplit
+#
+# /t1?r=http%3A%2F%2Fexample.com%2F%3Ffrom%3Dblah
+# rewrite ^ $arg_r? redirect;
+# expected: http://example.com/?from=blah
+# got:      http://example.com/?from%3Dblah
+#
+# /blah%3Fblah
+# rewrite ^ http://example.com$request_uri? redirect;
+# expected: http://example.com/blah%3Fblah
+# got:      http://example.com/blah?blah
+#
+# /blah%3Fblah
+# rewrite ^ http://example.com$uri redirect;
+# expected: http://example.com/blah%3Fblah
+# got:      http://example.com/blah?blah
+#
+# /blah%3Fblah
+# rewrite ^(.*) http://example.com$1 redirect;
+# expected: http://example.com/blah%3Fblah
+# got:      http://example.com/blah?blah
+#
+# /
+# rewrite ^ http://example.com/blah%3Fblah redirect;
+# expected: http://example.com/blah%3Fblah
+# got:      http://example.com/blah?blah
+#
+
+location('/t1?r=http%3A%2F%2Fexample.com%2F%3Ffrom',
+	'http://example.com/?from', 'escaped argument');
+
+location('/t1?r=http%3A%2F%2Fexample.com%0D%0Asplit',
+	'http://example.com%0D%0Asplit', 'escaped argument header splitting');
+
+TODO: {
+local $TODO = 'not yet';
+
+# Fixing this cases will require major changes to the whole aproach and
+# likely to break some currently working cases.  On the other hand, current
+# behaviour is far from acceptable.  Should be carefully thought.
+
+location('/t1?r=http%3A%2F%2Fexample.com%2F%3Ffrom%3Dblah',
+	'http://example.com/?from=blah', 'escaped argument with complex query');
+
+location('/t2/blah%20%3Fblah',
+	'http://example.com/t2/blah%20%3Fblah', 'escaped $request_uri');
+
+location('/t3/blah%20%3Fblah',
+	'http://example.com/t3/blah%20%3Fblah', 'escaped $uri');
+
+location('/t4/blah%20%3Fblah',
+	'http://example.com/t4/blah%20%3Fblah', 'escaped $1');
+
+location('/t5',
+	'http://example.com/blah%20%3Fblah', 'escaped static');
+
+location('/t5?arg=blah',
+	'http://example.com/blah%20%3Fblah?arg=blah',
+	'escaped static with argument');
+
+location('/t6',
+	'http://example.com/blah%20%2Fblah', 'escaped static slash');
+
+}
+
+###############################################################################
+
+sub location {
+	my ($url, $value, $name) = @_;
+	my $data = http_get($url);
+	if ($data !~ qr!^Location: (.*?)\x0d?$!ms) {
+		fail($name);
+		return;
+	}
+	my $location = $1;
+	is($location, $value, $name);
+}
+
+###############################################################################

+ 94 - 0
LibOS/shim/test/apps/nginx/nginx-tests/scgi.t

@@ -0,0 +1,94 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Test for scgi backend.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+eval { require SCGI; };
+plan(skip_all => 'SCGI not installed') if $@;
+
+my $t = Test::Nginx->new()->has(qw/http scgi/)->plan(4)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            scgi_pass 127.0.0.1:8081;
+            scgi_param SCGI 1;
+            scgi_param REQUEST_URI $request_uri;
+        }
+    }
+}
+
+EOF
+
+$t->run_daemon(\&scgi_daemon);
+$t->run();
+
+###############################################################################
+
+like(http_get('/'), qr/SEE-THIS/, 'scgi request');
+like(http_get('/redir'), qr/302/, 'scgi redirect');
+like(http_get('/'), qr/^3$/m, 'scgi third request');
+
+unlike(http_head('/'), qr/SEE-THIS/, 'no data in HEAD');
+
+###############################################################################
+
+sub scgi_daemon {
+	my $server = IO::Socket::INET->new(
+		Proto => 'tcp',
+		LocalHost => '127.0.0.1:8081',
+		Listen => 5,
+		Reuse => 1
+	)
+		or die "Can't create listening socket: $!\n";
+
+	my $scgi = SCGI->new($server, blocking => 1);
+	my $count = 0;
+  
+	while (my $request = $scgi->accept()) {
+		$count++;
+		$request->read_env();
+
+		$request->connection()->print(<<EOF);
+Location: http://127.0.0.1:8080/redirect
+Content-Type: text/html
+
+SEE-THIS
+$count
+EOF
+	}
+}
+
+###############################################################################

+ 147 - 0
LibOS/shim/test/apps/nginx/nginx-tests/secure_link.t

@@ -0,0 +1,147 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for nginx secure_link module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+use Digest::MD5 qw/ md5 md5_hex /;
+use MIME::Base64 qw/ encode_base64 /;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http secure_link/)->plan(8);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            # new style
+            # /test.html?hash=BASE64URL
+
+            secure_link      $arg_hash;
+            secure_link_md5  secret$uri;
+
+            # invalid hash
+            if ($secure_link = "") {
+                return 403;
+            }
+
+            # expired
+            if ($secure_link = "0") {
+                return 403;
+            }
+
+            # $secure_link = "1"
+        }
+
+        location = /expires.html {
+            # new style with expires
+            # /test.html?hash=BASE64URL&expires=12345678
+
+            secure_link      $arg_hash,$arg_expires;
+            secure_link_md5  secret$uri$arg_expires;
+
+            # invalid hash
+            if ($secure_link = "") {
+                return 403;
+            }
+
+            # expired
+            if ($secure_link = "0") {
+                return 403;
+            }
+
+            # $secure_link = "1"
+        }
+
+        location /p/ {
+            # old style
+            # /p/d8e8fca2dc0f896fd7cb4cb0031ba249/test.html
+
+            secure_link_secret secret;
+
+            if ($secure_link = "") {
+                return 403;
+            }
+
+            rewrite ^ /$secure_link break;
+        }
+    }
+}
+
+EOF
+
+$t->write_file('test.html', 'PASSED');
+$t->write_file('expires.html', 'PASSED');
+$t->run();
+
+###############################################################################
+
+# new style
+
+like(http_get('/test.html?hash=q-5vpkjBkRXXtkUMXiJVHA=='),
+	qr/PASSED/, 'request md5');
+like(http_get('/test.html?hash=q-5vpkjBkRXXtkUMXiJVHA'),
+	qr/PASSED/, 'request md5 no padding');
+like(http_get('/test.html'), qr/^HTTP.*403/, 'request no hash');
+
+# new style with expires
+
+my ($expires, $hash);
+
+$expires = time() + 86400;
+$hash = encode_base64url(md5("secret/expires.html$expires"));
+like(http_get('/expires.html?hash=' . $hash . '&expires=' . $expires),
+        qr/PASSED/, 'request md5 not expired');
+
+$expires = time() - 86400;
+$hash = encode_base64url(md5("secret/expires.html$expires"));
+like(http_get('/expires.html?hash=' . $hash . '&expires=' . $expires),
+        qr/^HTTP.*403/, 'request md5 expired');
+
+# old style
+
+like(http_get('/p/' . md5_hex('test.html' . 'secret') . '/test.html'),
+	qr/PASSED/, 'request old style');
+like(http_get('/p/' . md5_hex('fake') . '/test.html'), qr/^HTTP.*403/,
+	'request old style fake hash');
+like(http_get('/p/test.html'), qr/^HTTP.*403/, 'request old style no hash');
+
+###############################################################################
+
+sub encode_base64url {
+    my $e = encode_base64(shift, "");
+    $e =~ s/=+\z//;
+    $e =~ tr[+/][-_];
+    return $e;
+}
+
+###############################################################################

+ 129 - 0
LibOS/shim/test/apps/nginx/nginx-tests/ssi.t

@@ -0,0 +1,129 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for nginx ssi module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http ssi cache proxy rewrite/)->plan(18);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    proxy_cache_path       %%TESTDIR%%/cache levels=1:2
+                           keys_zone=NAME:10m;
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        if ($args = "found") {
+            return 204;
+        }
+
+        location / {
+            ssi on;
+        }
+        location /proxy/ {
+            ssi on;
+            proxy_pass http://127.0.0.1:8080/local/;
+        }
+        location /cache/ {
+            proxy_pass http://127.0.0.1:8080/local/;
+            proxy_cache NAME;
+            proxy_cache_valid 200 1h;
+        }
+        location /local/ {
+            ssi off;
+            alias %%TESTDIR%%/;
+        }
+    }
+}
+
+EOF
+
+$t->write_file('test1.html', 'X<!--#echo var="arg_test" -->X');
+$t->write_file('test2.html',
+	'X<!--#include virtual="/test1.html?test=test" -->X');
+$t->write_file('test3.html',
+	'X<!--#set var="blah" value="test" --><!--#echo var="blah" -->X');
+$t->write_file('test-args-rewrite.html',
+	'X<!--#include virtual="/check?found" -->X');
+$t->write_file('test-empty1.html', 'X<!--#include virtual="/empty.html" -->X');
+$t->write_file('test-empty2.html',
+	'X<!--#include virtual="/local/empty.html" -->X');
+$t->write_file('test-empty3.html',
+	'X<!--#include virtual="/cache/empty.html" -->X');
+$t->write_file('empty.html', '');
+
+$t->run();
+
+###############################################################################
+
+like(http_get('/test1.html'), qr/^X\(none\)X$/m, 'echo no argument');
+like(http_get('/test1.html?test='), qr/^XX$/m, 'empty argument');
+like(http_get('/test1.html?test=test'), qr/^XtestX$/m, 'argument');
+like(http_get('/test1.html?test=test&a=b'), qr/^XtestX$/m, 'argument 2');
+like(http_get('/test1.html?a=b&test=test'), qr/^XtestX$/m, 'argument 3');
+like(http_get('/test1.html?a=b&test=test&d=c'), qr/^XtestX$/m, 'argument 4');
+like(http_get('/test1.html?atest=a&testb=b&ctestc=c&test=test'), qr/^XtestX$/m,
+	'argument 5');
+
+like(http_get('/test2.html'), qr/^XXtestXX$/m, 'argument via include');
+
+like(http_get('/test3.html'), qr/^XtestX$/m, 'set');
+
+# args should be in subrequest even if original request has no args and that
+# was queried somehow (e.g. by server rewrites)
+
+TODO: {
+local $TODO = 'patch under review';
+
+like(http_get('/test-args-rewrite.html'), qr/^XX$/m, 'args only subrequest');
+
+}
+
+like(http_get('/test-args-rewrite.html?wasargs'), qr/^XX$/m,
+	'args was in main request');
+
+# Last-Modified and Accept-Ranges headers should be cleared
+
+unlike(http_get('/test1.html'), qr/Last-Modified|Accept-Ranges/im,
+	'cleared headers');
+unlike(http_get('/proxy/test1.html'), qr/Last-Modified|Accept-Ranges/im,
+	'cleared headers from proxy');
+
+like(http_get('/test-empty1.html'), qr/HTTP/, 'empty with ssi');
+like(http_get('/test-empty2.html'), qr/HTTP/, 'empty without ssi');
+like(http_get('/test-empty3.html'), qr/HTTP/, 'empty with proxy');
+like(http_get('/test-empty3.html'), qr/HTTP/, 'empty with proxy cached');
+
+like(`grep -F '[alert]' ${\($t->testdir())}/error.log`, qr/^$/s, 'no alerts');
+
+###############################################################################

+ 90 - 0
LibOS/shim/test/apps/nginx/nginx-tests/ssi_include_big.t

@@ -0,0 +1,90 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for nginx ssi bug with big includes.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx qw/ :DEFAULT :gzip /;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http ssi rewrite gzip proxy/)->plan(8);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    output_buffers  2 512;
+    ssi on;
+    gzip on;
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location /proxy/ {
+            proxy_pass http://127.0.0.1:8080/local/;
+        }
+        location = /local/blah {
+            return 204;
+        }
+    }
+}
+
+EOF
+
+$t->write_file('c1.html', 'X' x 1023);
+$t->write_file('c2.html', 'X' x 1024);
+$t->write_file('c3.html', 'X' x 1025);
+$t->write_file('test1.html', '<!--#include virtual="/proxy/blah" -->'
+	. '<!--#include virtual="/c1.html" -->');
+$t->write_file('test2.html', '<!--#include virtual="/proxy/blah" -->'
+	. '<!--#include virtual="/c2.html" -->');
+$t->write_file('test3.html', '<!--#include virtual="/proxy/blah" -->'
+	. '<!--#include virtual="/c3.html" -->');
+$t->write_file('test4.html', '<!--#include virtual="/proxy/blah" -->'
+	. ('X' x 1025));
+
+$t->run();
+
+###############################################################################
+
+my $t1 = http_gzip_request('/test1.html');
+ok(defined $t1, 'small included file (less than output_buffers)');
+http_gzip_like($t1, qr/^X{1023}\Z/, 'small included file content');
+
+my $t2 = http_gzip_request('/test2.html');
+ok(defined $t2, 'small included file (equal to output_buffers)');
+http_gzip_like($t2, qr/^X{1024}\Z/, 'small included file content');
+
+my $t3 = http_gzip_request('/test3.html');
+ok(defined $t3, 'big included file (more than output_buffers)');
+http_gzip_like($t3, qr/^X{1025}\Z/, 'big included file content');
+
+my $t4 = http_gzip_request('/test4.html');
+ok(defined $t4, 'big ssi main file');
+http_gzip_like($t4, qr/^X{1025}\Z/, 'big ssi main file content');
+
+###############################################################################

+ 65 - 0
LibOS/shim/test/apps/nginx/nginx-tests/ssi_waited.t

@@ -0,0 +1,65 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for nginx ssi module, waited subrequests.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http ssi/)->plan(2);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    %%TEST_GLOBALS_HTTP%%
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+        location / {
+            ssi on;
+        }
+    }
+}
+
+EOF
+
+$t->write_file('index.html', 'x<!--#include virtual="/first.html" -->' .
+	'x<!--#include virtual="/second.html" -->x');
+$t->write_file('first.html', 'FIRST');
+$t->write_file('second.html',
+	'<!--#include virtual="/waited.html" wait="yes"-->xSECOND');
+$t->write_file('waited.html', 'WAITED');
+
+$t->run();
+
+###############################################################################
+
+like(http_get('/'), qr/^xFIRSTxWAITEDxSECONDx$/m, 'waited non-active');
+
+like(`grep -F '[alert]' ${\($t->testdir())}/error.log`, qr/^$/s, 'no alerts');
+
+###############################################################################

+ 54 - 0
LibOS/shim/test/apps/nginx/nginx.manifest.template

@@ -0,0 +1,54 @@
+#!$(PAL)
+
+loader.preload = file:$(SHIMPATH)
+loader.exec = file:build/sbin/nginx
+loader.execname = nginx
+loader.env.LD_LIBRARY_PATH = /lib:/lib/x86_64-linux-gnu:/usr/lib:/usr/lib/x86_64-linux-gnu
+loader.debug_type = none
+
+fs.mount.lib1.type = chroot
+fs.mount.lib1.path = /lib
+fs.mount.lib1.uri = file:$(LIBCDIR)
+
+fs.mount.lib2.type = chroot
+fs.mount.lib2.path = /lib/x86_64-linux-gnu
+fs.mount.lib2.uri = file:/lib/x86_64-linux-gnu
+
+fs.mount.usr.type = chroot
+fs.mount.usr.path = /usr
+fs.mount.usr.uri = file:/usr
+
+fs.mount.cwd.type = chroot
+fs.mount.cwd.path = $(PWD)
+fs.mount.cwd.uri = file:
+
+fs.mount.etc.type = chroot
+fs.mount.etc.path = /etc
+fs.mount.etc.uri = file:etc
+
+# allow to bind on port for listening
+net.allow_bind.1 = $(HOST):$(PORT)
+
+sgx.enclave_size = 512M
+
+sgx.trusted_files.ld = file:$(LIBCDIR)/ld-linux-x86-64.so.2
+sgx.trusted_files.libc = file:$(LIBCDIR)/libc.so.6
+sgx.trusted_files.libdl = file:$(LIBCDIR)/libdl.so.2
+sgx.trusted_files.libm = file:$(LIBCDIR)/libm.so.6
+sgx.trusted_files.libpthread = file:$(LIBCDIR)/libpthread.so.0
+sgx.trusted_files.libcrypt = file:/lib/x86_64-linux-gnu/libcrypt.so.1
+sgx.trusted_files.libpcre = file:/lib/x86_64-linux-gnu/libpcre.so.3
+sgx.trusted_files.libcrypto = file:/lib/x86_64-linux-gnu/libcrypto.so.1.0.0
+sgx.trusted_files.libz = file:/lib/x86_64-linux-gnu/libz.so.1
+sgx.trusted_files.libnssdns = file:/lib/x86_64-linux-gnu/libnss_dns.so.2
+sgx.trusted_files.libnssfiles = file:/lib/x86_64-linux-gnu/libnss_files.so.2
+sgx.trusted_files.libnsscompact = file:/lib/x86_64-linux-gnu/libnss_compat.so.2
+sgx.trusted_files.libnssnis = file:/lib/x86_64-linux-gnu/libnss_nis.so.2
+sgx.trusted_files.libgnutls = file:/usr/lib/x86_64-linux-gnu/libgnutls.so.26
+sgx.trusted_files.libgcrypt = file:/lib/x86_64-linux-gnu/libgcrypt.so.11
+sgx.trusted_files.libnsl = file:/lib/x86_64-linux-gnu/libnsl.so.1
+
+sgx.trusted_files.conf = file:http.t
+
+sgx.allowed_files.root = file:build
+sgx.allowed_files.etc = file:etc

+ 8 - 5
LibOS/shim/test/apps/openjdk/Makefile

@@ -4,12 +4,15 @@ ifeq ($(OS),Linux)
 	NPROCS := $(shell grep -c ^processor /proc/cpuinfo)
 endif
 
-target = build-java $(patsubst %.java,%.class,$(wildcard classes/*.java))
+target = $(patsubst %.java,%.class,$(wildcard classes/*.java))
 exec_target = java.manifest
 
 clean-extra = clean-classes
 
-extra_rules = -e 's:\$$(JAVA_HOME):$(JAVA_HOME):g'
+JAVA_HOST_HOME = $(shell readlink -f /usr/bin/java | xargs dirname | xargs dirname)
+
+ABSPATH_IN_MANIFEST = yes
+extra_rules = -e 's:\$$(JAVA_HOME):$(JAVA_HOST_HOME):g'
 
 level = ../../
 include ../../Makefile
@@ -128,13 +131,13 @@ distclean: clean
 
 endif
 
-build-java: $(JAVA_HOME)/bin/java $(JAVAC)
+build-java: $(JAVA_HOME)/bin/java
 
 java_home:
 	@echo $(JAVA_HOME)
 
-%.class: %.java $(JAVAC)
-	$(JAVAC) $<
+%.class: %.java
+	javac $<
 
 clean-classes:
 	rm -f $(addsuffix .class,$(TEST_CLASS))

+ 46 - 0
LibOS/shim/test/apps/openjdk/java-local.manifest.template

@@ -0,0 +1,46 @@
+#!$(PAL)
+
+loader.preload = file:$(SHIMPATH)
+loader.exec = file:$(JAVA_HOME)/bin/java
+loader.execname = java
+loader.env.LD_LIBRARY_PATH = /jre/lib/amd64:/jre/lib/amd64/jli:/lib:/lib/x86_64-linux-gnu:/usr/lib:/usr/lib/x86_64-linux-gnu
+loader.env.PATH = /jre/bin:/bin:/usr/bin
+loader.debug_type = none
+
+fs.mount.lib1.type = chroot
+fs.mount.lib1.path = /lib
+fs.mount.lib1.uri = file:$(LIBCDIR)
+
+fs.mount.lib2.type = chroot
+fs.mount.lib2.path = /lib/x86_64-linux-gnu
+fs.mount.lib2.uri = file:/lib/x86_64-linux-gnu
+
+fs.mount.jre.type = chroot
+fs.mount.jre.path = /jre
+fs.mount.jre.uri = file:$(JAVA_HOME)
+
+fs.mount.usr.type = chroot
+fs.mount.usr.path = /usr
+fs.mount.usr.uri = file:/usr
+
+sys.stack.size = 256K
+sys.brk.size = 16M
+glibc.heap_size = 16M
+
+sgx.enclave_size = 512M
+sgx.thread_num = 16
+
+sgx.trusted_files.ld = file:$(LIBCDIR)/ld-linux-x86-64.so.2
+sgx.trusted_files.libc = file:$(LIBCDIR)/libc.so.6
+sgx.trusted_files.libdl = file:$(LIBCDIR)/libdl.so.2
+sgx.trusted_files.libm = file:$(LIBCDIR)/libm.so.6
+sgx.trusted_files.libpthread = file:$(LIBCDIR)/libpthread.so.0
+sgx.trusted_files.librt = file:/lib/x86_64-linux-gnu/librt.so.1
+sgx.trusted_files.libgcc = file:/lib/x86_64-linux-gnu/libgcc_s.so.1
+sgx.trusted_files.libcpp = file:/usr/lib/x86_64-linux-gnu/libstdc++.so.6
+sgx.trusted_files.libnss1 = file:/lib/x86_64-linux-gnu/libnss_compat.so.2
+sgx.trusted_files.libnss2 = file:/lib/x86_64-linux-gnu/libnss_files.so.2
+sgx.trusted_files.libnsl = file:/lib/x86_64-linux-gnu/libnsl.so.1
+
+sgx.allowed_files.java_home = file:$(JAVA_HOME)
+sgx.allowed_files.classes = file:classes

+ 30 - 10
LibOS/shim/test/apps/openjdk/java.manifest.template

@@ -3,12 +3,12 @@
 loader.preload = file:$(SHIMPATH)
 loader.exec = file:$(JAVA_HOME)/bin/java
 loader.execname = java
-loader.env.LD_LIBRARY_PATH = /jre/lib/amd64:/jre/lib/amd64/jli:/lib:/lib/x86_64-linux-gnu:/usr/lib:/usr/lib/x86_64-linux-gnu
+loader.env.LD_LIBRARY_PATH = $(JAVA_HOME)/lib/amd64:$(JAVA_HOME)/lib/amd64/jli:/graphene:/lib/x86_64-linux-gnu:/usr/lib:/usr/lib/x86_64-linux-gnu
 loader.env.PATH = /jre/bin:/bin:/usr/bin
 loader.debug_type = none
 
 fs.mount.lib1.type = chroot
-fs.mount.lib1.path = /lib
+fs.mount.lib1.path = /graphene
 fs.mount.lib1.uri = file:$(LIBCDIR)
 
 fs.mount.lib2.type = chroot
@@ -16,31 +16,51 @@ fs.mount.lib2.path = /lib/x86_64-linux-gnu
 fs.mount.lib2.uri = file:/lib/x86_64-linux-gnu
 
 fs.mount.jre.type = chroot
-fs.mount.jre.path = /jre
+fs.mount.jre.path = $(JAVA_HOME)
 fs.mount.jre.uri = file:$(JAVA_HOME)
 
 fs.mount.usr.type = chroot
 fs.mount.usr.path = /usr
 fs.mount.usr.uri = file:/usr
 
-sys.stack.size = 256K
+fs.mount.tmp.type = chroot
+fs.mount.tmp.path = /tmp
+fs.mount.tmp.uri = file:/tmp
+
+sys.stack.size = 1M
 sys.brk.size = 16M
-glibc.heap_size = 16M
+glibc.heap_size = 4M
 
-sgx.enclave_size = 512M
-sgx.thread_num = 16
+sgx.enclave_size = 8G
+sgx.thread_num = 40
+sgx.heap_min = 0x80000000
 
 sgx.trusted_files.ld = file:$(LIBCDIR)/ld-linux-x86-64.so.2
 sgx.trusted_files.libc = file:$(LIBCDIR)/libc.so.6
 sgx.trusted_files.libdl = file:$(LIBCDIR)/libdl.so.2
+sgx.trusted_files.librt = file:$(LIBCDIR)/librt.so.1
 sgx.trusted_files.libm = file:$(LIBCDIR)/libm.so.6
 sgx.trusted_files.libpthread = file:$(LIBCDIR)/libpthread.so.0
-sgx.trusted_files.librt = file:/lib/x86_64-linux-gnu/librt.so.1
+sgx.trusted_files.libresolv = file:$(LIBCDIR)/libresolv.so.2
+sgx.trusted_files.libnssdns = file:/lib/x86_64-linux-gnu/libnss_dns.so.2
+sgx.trusted_files.libnssfiles = file:/lib/x86_64-linux-gnu/libnss_files.so.2
+sgx.trusted_files.libnsscompact = file:/lib/x86_64-linux-gnu/libnss_compat.so.2
+sgx.trusted_files.libnssnis = file:/lib/x86_64-linux-gnu/libnss_nis.so.2
 sgx.trusted_files.libgcc = file:/lib/x86_64-linux-gnu/libgcc_s.so.1
 sgx.trusted_files.libcpp = file:/usr/lib/x86_64-linux-gnu/libstdc++.so.6
-sgx.trusted_files.libnss1 = file:/lib/x86_64-linux-gnu/libnss_compat.so.2
-sgx.trusted_files.libnss2 = file:/lib/x86_64-linux-gnu/libnss_files.so.2
 sgx.trusted_files.libnsl = file:/lib/x86_64-linux-gnu/libnsl.so.1
+sgx.trusted_files.libz = file:/lib/x86_64-linux-gnu/libz.so.1
+sgx.trusted_files.libgio = file:/usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
+sgx.trusted_files.libgobject = file:/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
+sgx.trusted_files.libglib = file:/lib/x86_64-linux-gnu/libglib-2.0.so.0
+sgx.trusted_files.libgmodule = file:/usr/lib/x86_64-linux-gnu/libgmodule-2.0.so.0
+sgx.trusted_files.libselinux = file:/lib/x86_64-linux-gnu/libselinux.so.1
+sgx.trusted_files.libffi = file:/usr/lib/x86_64-linux-gnu/libffi.so.6
+sgx.trusted_files.libpcre = file:/lib/x86_64-linux-gnu/libpcre.so.3
 
 sgx.allowed_files.java_home = file:$(JAVA_HOME)
+sgx.allowed_files.java_lib = file:/usr/lib/jvm/java-7-openjdk-amd64/lib/ct.sym
 sgx.allowed_files.classes = file:classes
+sgx.allowed_files.tmp = file:/tmp
+sgx.allowed_files.xml_out = file:xml_out
+sgx.allowed_files.SPECjvm2008 = file:SPECjvm2008

+ 9 - 5
LibOS/shim/test/apps/openjdk/run-java

@@ -1,11 +1,15 @@
 #!/bin/bash
 
-JAVA_FLAGS="-Xmx4m -Xms1m -XX:NewSize=256k -XX:MaxNewSize=1m -Xss1m -XX:+UseSerialGC -XX:-UsePerfData -XX:+DisableExplicitGC -XX:PermSize=4m -XX:MaxPermSize=16m -XX:MinHeapFreeRatio=0 -XX:MaxHeapFreeRatio=30 -XX:-UseCompiler"
+JAVA_FLAGS="-Xmx512m -Xms4m -XX:NewSize=1m -XX:MaxNewSize=4m -Xss4m -XX:+UseSerialGC -XX:-UsePerfData -XX:+DisableExplicitGC -XX:PermSize=16m -XX:MaxPermSize=64m -XX:MinHeapFreeRatio=0 -XX:MaxHeapFreeRatio=30"
 
-PAL=../pal_loader
-if [ "$1" = "-native" ]; then
+if [ "$1" = "-native" ]
+then
 	shift
-	PAL=
+	JAVA=/usr/bin/java
+else
+	JAVA=$(/usr/bin/dirname $(readlink -f ${BASH_SOURCE[0]}))/java.manifest
 fi
 
-exec $PAL `make java_home`/bin/java $JAVA_FLAGS $*
+set -x
+
+exec $JAVA $JAVA_FLAGS $*

+ 0 - 6
LibOS/shim/test/apps/python/.gitignore

@@ -1,7 +1 @@
 *.pyc
-python.manifest
-python.manifest.sgx
-python.sig
-python.token
-benchmarks/
-pal_loader

+ 1 - 1
LibOS/shim/test/apps/python/Makefile

@@ -29,7 +29,7 @@ benchmarks: benchmarks.tar.gz
 BENCHMARK = all,-rietveld,-spitfire,-tornado_http
 
 clean-tmp:
-	rm -f python.manifest.sgx python.sig python.token
+	rm -f python.manifest.sgx
 
 distclean: clean
 	rm -rf $(PYTHON_SRC) benchmarks

+ 1 - 1
LibOS/shim/test/apps/r/Makefile

@@ -1,7 +1,7 @@
 R_SRC = R-3.1.2
 R_INSTALL = $(R_SRC)/build
 
-manifests = R.manifest $(if $(wildcard R-local),R-local.manifest,)
+manifests = R.manifest Rscript.manifest sh.manifest $(if $(wildcard R-local),R-local.manifest,)
 
 target =
 exec_target = $(manifests)

Some files were not shown because too many files changed in this diff