pax_global_header00006660000000000000000000000064141365747070014530gustar00rootroot0000000000000052 comment=6cdffdb0af493b7c21eedba2f09d8486579f3922 ngx-fancyindex-0.5.2/000077500000000000000000000000001413657470700144565ustar00rootroot00000000000000ngx-fancyindex-0.5.2/.gitattributes000066400000000000000000000001361413657470700173510ustar00rootroot00000000000000/.gitignore export-ignore /.travis.yml export-ignore /make-dist export-ignore t/* text eol=lf ngx-fancyindex-0.5.2/.github/000077500000000000000000000000001413657470700160165ustar00rootroot00000000000000ngx-fancyindex-0.5.2/.github/workflows/000077500000000000000000000000001413657470700200535ustar00rootroot00000000000000ngx-fancyindex-0.5.2/.github/workflows/ci.yml000066400000000000000000000016751413657470700212020ustar00rootroot00000000000000--- name: Build on: [pull_request] jobs: build: strategy: fail-fast: false matrix: compiler: [gcc, clang] dynamic: [0, 1] nginx: # Mainline - 1.21.3 # Stable. - 1.20.1 # First version with loadable module support. - 1.9.15 # Oldest supported version. - 0.8.55 exclude: - nginx: 0.8.55 dynamic: 1 runs-on: ubuntu-18.04 env: CFLAGS: "-Wno-error" steps: - name: Checkout uses: actions/checkout@v2 - name: Install Packages run: | sudo apt update sudo apt install -y libpcre3-dev libssl-dev t/get-pup || echo 'Tests needing pup will be skipped' - name: Test env: CC: ${{ matrix.compiler }} run: | CC=${{ matrix.compiler }} t/build-and-run ${{ matrix.nginx }} ${{ matrix.dynamic }} ngx-fancyindex-0.5.2/CHANGELOG.md000066400000000000000000000172611413657470700162760ustar00rootroot00000000000000# Change Log All notable changes to this project will be documented in this file. ## [Unreleased] ## [0.5.2] - 2021-10-28 ### Fixed - Properly escape file names to ensure that file names are never renreded as HTML. (Patch by Anthony Ryan <>, [#128](https://github.com/aperezdc/ngx-fancyindex/pull/128).) ## [0.5.1] - 2020-10-26 ### Fixed - Properly handle optional second argument to `fancyindex_header` and `fancyindex_footer` ([#117](https://github.com/aperezdc/ngx-fancyindex/issues/117)). ## [0.5.0] - 2020-10-24 ### Added - New option `fancyindex_show_dotfiles`. (Path by Joshua Shaffer <>.) - The `fancyindex_header` and `fancyindex_footer` options now support local files properly, by means of a `local` flag. (Patches by JoungKyun Kim <> and Adrián Pérez <>.) ### Changed - Improved performance of directory entry sorting, which should be quite noticeable for directories with thousands of files. (Patch by [Yuxiang Zhang](https://github.com/z4yx).) - The minimum Nginx version supported by the module is now 0.8.x. ### Fixed - Properly escape square brackets in directory entry names when the module is built with older versions of Nginx. (Patch by Adrián Pérez <>.) - Fix directory entry listing not being shown when using the [nginx-auth-ldap](https://github.com/kvspb/nginx-auth-ldap) module. (Patch by JoungKyun Kim <>.) ## [0.4.4] - 2020-02-19 ### Added - New option `fancyindex_hide_parent_dir`, which disables generating links to parent directories in listings. (Patch by Kawai Ryota <>.) ### Changed - Each table row is now separated by a new line (as a matter of fact, a `CRLF` sequence), which makes it easier to parse output using simple text tools. (Patch by Anders Trier <>.) - Some corrections and additions to the README file. (Patches by Nicolas Carpi <> and David Beitey <>.) ### Fixed - Use correct character references for `&` characters in table sorter URLs within the template (Patch by David Beitey <>.) - Properly encode filenames when used as URI components. ## [0.4.3] - 2018-07-03 ### Added - Table cells now have class names, which allows for better CSS styling. (Patch by qjqqyy <>.) - The test suite now can parse and check elements from the HTML returned by the module, thanks to the [pup](https://github.com/EricChiang/pup) tool. ### Fixed - Sorting by file size now works correctly. (Patch by qjqqyy <>.) ## [0.4.2] - 2017-08-19 ### Changed - Generated HTML from the default template is now proper HTML5, and it should pass validation (#52). - File sizes now have decimal positions when using `fancyindex_exact_size off`. (Patch by Anders Trier <>.) - Multiple updates to `README.rst` (Patches by Danila Vershinin <>, Iulian Onofrei, Lilian Besson, and Nick Geoghegan <>.) ### Fixed - Sorting by file size now also works correctly for directories which contain files of sizes bigger than `INT_MAX`. (#74, fix suggestion by Chris Young.) - Custom headers which fail to declare an UTF-8 encoding no longer cause table header arrows to be rendered incorrectly by browsers (#50). - Fix segmentation fault when opening directories with empty files (#61, patch by Catgirl <>.) ## [0.4.1] - 2016-08-18 ### Added - New `fancyindex_directories_first` configuration directive (enabled by default), which allows setting whether directories are sorted before other files. (Patch by Luke Zapart <>.) ### Fixed - Fix index files not working when the fancyindex module is in use (#46). ## [0.4.0] - 2016-06-08 ### Added - The module can now be built as a [dynamic module](https://www.nginx.com/resources/wiki/extending/converting/). (Patch by Róbert Nagy <>.) - New configuration directive `fancyindex_show_path`, which allows hiding the `

` header which contains the current path. (Patch by Thomas P. <>.) ### Changed - Directory and file links in listings now have a title="..." attribute. (Patch by `@janglapuk` <>.) ### Fixed - Fix for hung requests when the module is used along with `ngx_pagespeed`. (Patch by Otto van der Schaaf <>.) ## [0.3.6] - 2016-01-26 ### Added - New feature: Allow filtering out symbolic links using the `fancyindex_hide_symlinks` configuration directive. (Idea and prototype patch by Thomas Wemm.) - New feature: Allow specifying the format of timestamps using the `fancyindex_time_format` configuration directive. (Idea suggested by Xiao Meng <>). ### Changed - Listings in top-level directories will not generate a "Parent Directory" link as first element of the listing. (Patch by Thomas P.) ### Fixed - Fix propagation and overriding of the `fancyindex_css_href` setting inside nested locations. - Minor changes in the code to allow building cleanly under Windows with Visual Studio 2013. (Patch by Y. Yuan <>). ## [0.3.5] - 2015-02-19 ### Added - New feature: Allow setting the default sort criterion using the `fancyindex_default_sort` configuration directive. (Patch by Алексей Урбанский). - New feature: Allow changing the maximum length of file names, using the `fancyindex_name_length` configuration directive. (Patch by Martin Herkt). ### Changed - Renames `NEWS.rst` to `CHANGELOG.md`, which follows the recommendations from [Keep a Change Log](http://keepachangelog.com/). - Configuring Nginx without the `http_addition_module` will generate a warning during configuration, as it is needed for the `fancyindex_footer` and `fancyindex_header` directives. ## [0.3.4] - 2014-09-03 ### Added - Viewport is now defined in the generated HTML, which works better for mobile devices. ### Changed - Even-odd row styling moved to the CSS using :nth-child(). This makes the HTML served to clients smaller. ## [0.3.3] - 2013-10-25 ### Added - New feature: table headers in the default template are now clickable to set the sorting criteria and direction of the index entries. (https://github.com/aperezdc/ngx-fancyindex/issues/7) ## [0.3.2] - 2013-06-05 ### Fixed - Solved a bug that would leave certain clients stalled forever. - Improved handling of subrequests for non-builtin headers/footers. ## [0.3.1] - 2011-04-04 ### Added - `NEWS.rst` file, to act as change log. [Unreleased]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.5.2...HEAD [0.5.2]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.5.1...v0.5.2 [0.5.1]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.5.0...v0.5.1 [0.5.0]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.4.4...v0.5.0 [0.4.4]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.4.3...v0.4.4 [0.4.3]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.4.2...v0.4.3 [0.4.2]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.4.1...v0.4.2 [0.4.1]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.4.0...v0.4.1 [0.4.0]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.3.6...v0.4.0 [0.3.6]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.3.5...v0.3.6 [0.3.5]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.3.4...v0.3.5 [0.3.4]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.3.3...v0.3.4 [0.3.3]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.3.2...v0.3.3 [0.3.2]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.3.1...v0.3.2 [0.3.1]: https://github.com/aperezdc/ngx-fancyindex/compare/v0.3...v0.3.1 ngx-fancyindex-0.5.2/HACKING.md000066400000000000000000000022511413657470700160440ustar00rootroot00000000000000# Fancy Index module Hacking HOW-TO ## How to modify the template The template is in the `template.html` file. Note that comment markers are used to control how the `template.awk` Awk script generates the C header which gets ultimately included in the compiled object code. Comment markers have the `` format. Here `identifier` must be a valid C identifier. All the text following the marker until the next marker will be flattened into a C string. If the identifier is `NONE` (capitalized) the text from that marker up to the next marker will be discarded. ## Regenerating the C header You will need Awk. I hope any decent implementation will do, but the GNU one is known to work flawlessly. Just do: $ awk -f template.awk template.html > template.h If your copy of `awk` is not the GNU implementation, you will need to install it and use `gawk` instead in the command line above. This includes macOS where the current built-in `awk` (currently version 20070501 at time of testing on 10.13.6) doesn't apply correctly and causes characters to be omitted from the output. `gawk` can be installed with a package manager such as [Homebrew](https://brew.sh). ngx-fancyindex-0.5.2/LICENSE000066400000000000000000000023011413657470700154570ustar00rootroot00000000000000Redistribution 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. ngx-fancyindex-0.5.2/README.rst000066400000000000000000000264411413657470700161540ustar00rootroot00000000000000======================== Nginx Fancy Index module ======================== .. image:: https://travis-ci.com/aperezdc/ngx-fancyindex.svg?branch=master :target: https://travis-ci.com/aperezdc/ngx-fancyindex :alt: Build Status .. contents:: The Fancy Index module makes possible the generation of file listings, like the built-in `autoindex `__ module does, but adding a touch of style. This is possible because the module allows a certain degree of customization of the generated content: * Custom headers. Either local or stored remotely. * Custom footers. Either local or stored remotely. * Add you own CSS style rules. * Allow choosing to sort elements by name (default), modification time, or size; both ascending (default), or descending. This module is designed to work with Nginx_, a high performance open source web server written by `Igor Sysoev `__. Requirements ============ CentOS 7 ~~~~~~~~ For users of the `official stable `__ Nginx repository, `extra packages repository with dynamic modules `__ is available and fancyindex is included. Install directly:: yum install https://extras.getpagespeed.com/redhat/7/x86_64/RPMS/nginx-module-fancyindex-1.12.0.0.4.1-1.el7.gps.x86_64.rpm Alternatively, add extras repository first (for future updates) and install the module:: yum install nginx-module-fancyindex Then load the module in `/etc/nginx/nginx.conf` using:: load_module "modules/ngx_http_fancyindex_module.so"; Other platforms ~~~~~~~~~~~~~~~ In most other cases you will need the sources for Nginx_. Any version starting from the 0.8 series should work. In order to use the ``fancyindex_header_`` and ``fancyindex_footer_`` directives you will also need the `ngx_http_addition_module `_ built into Nginx. Building ======== 1. Unpack the Nginx_ sources:: $ gunzip -c nginx-?.?.?.tar.gz | tar -xvf - 2. Unpack the sources for the fancy indexing module:: $ gunzip -c nginx-fancyindex-?.?.?.tar.gz | tar -xvf - 3. Change to the directory which contains the Nginx_ sources, run the configuration script with the desired options and be sure to put an ``--add-module`` flag pointing to the directory which contains the source of the fancy indexing module:: $ cd nginx-?.?.? $ ./configure --add-module=../nginx-fancyindex-?.?.? \ [--with-http_addition_module] [extra desired options] Since version 0.4.0, the module can also be built as a `dynamic module `_, using ``--add-dynamic-module=…`` instead and ``load_module "modules/ngx_http_fancyindex_module.so";`` in the configuration file 4. Build and install the software:: $ make And then, as ``root``:: # make install 5. Configure Nginx_ by using the modules' configuration directives_. Example ======= You can test the default built-in style by adding the following lines into a ``server`` section in your Nginx_ configuration file:: location / { fancyindex on; # Enable fancy indexes. fancyindex_exact_size off; # Output human-readable file sizes. } Themes ~~~~~~ The following themes demonstrate the level of customization which can be achieved using the module: * `Theme `__ by `@TheInsomniac `__. Uses custom header and footer. * `Theme `__ by `@Naereen `__. Uses custom header and footer, the header includes search field to filter by filename using JavaScript. * `Theme `__ by `@fraoustin `__. Responsive theme using Material Design elements. * `Theme `__ by `@alehaa `__. Simple, flat theme based on Bootstrap 4 and FontAwesome. Directives ========== fancyindex ~~~~~~~~~~ :Syntax: *fancyindex* [*on* | *off*] :Default: fancyindex off :Context: http, server, location :Description: Enables or disables fancy directory indexes. fancyindex_default_sort ~~~~~~~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_default_sort* [*name* | *size* | *date* | *name_desc* | *size_desc* | *date_desc*] :Default: fancyindex_default_sort name :Context: http, server, location :Description: Defines sorting criterion by default. fancyindex_directories_first ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_directories_first* [*on* | *off*] :Default: fancyindex_directories_first on :Context: http, server, location :Description: If enabled (default setting), groups directories together and sorts them before all regular files. If disabled, directories are sorted together with files. fancyindex_css_href ~~~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_css_href uri* :Default: fancyindex_css_href "" :Context: http, server, location :Description: Allows inserting a link to a CSS style sheet in generated listings. The provided *uri* parameter will be inserted as-is in a ```` HTML tag. The link is inserted after the built-in CSS rules, so you can override the default styles. fancyindex_exact_size ~~~~~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_exact_size* [*on* | *off*] :Default: fancyindex_exact_size on :Context: http, server, location :Description: Defines how to represent file sizes in the directory listing; either accurately, or rounding off to the kilobyte, the megabyte and the gigabyte. fancyindex_name_length ~~~~~~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_name_length length* :Default: fancyindex_name_length 50 :Context: http, server, location :Description: Defines the maximum file name length limit in bytes. fancyindex_footer ~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_footer path* [*subrequest* | *local*] :Default: fancyindex_footer "" :Context: http, server, location :Description: Specifies which file should be inserted at the foot of directory listings. If set to an empty string, the default footer supplied by the module will be sent. The optional parameter indicates whether the *path* is to be treated as an URI to load using a *subrequest* (the default), or whether it refers to a *local* file. .. note:: Using this directive needs the ngx_http_addition_module_ built into Nginx. .. warning:: When inserting custom header/footer a subrequest will be issued so potentially any URL can be used as source for them. Although it will work with external URLs, only using internal ones is supported. External URLs are totally untested and using them will make Nginx_ block while waiting for the subrequest to complete. If you feel like external header/footer is a must-have for you, please `let me know `__. fancyindex_header ~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_header path* [*subrequest* | *local*] :Default: fancyindex_header "" :Context: http, server, location :Description: Specifies which file should be inserted at the head of directory listings. If set to an empty string, the default header supplied by the module will be sent. The optional parameter indicates whether the *path* is to be treated as an URI to load using a *subrequest* (the default), or whether it refers to a *local* file. .. note:: Using this directive needs the ngx_http_addition_module_ built into Nginx. fancyindex_show_path ~~~~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_show_path* [*on* | *off*] :Default: fancyindex_show_path on :Context: http, server, location :Description: Whether to output or not the path and the closing

tag after the header. This is useful when you want to handle the path displaying with a PHP script for example. .. warning:: This directive can be turned off only if a custom header is provided using fancyindex_header. fancyindex_show_dotfiles ~~~~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_show_dotfiles* [*on* | *off*] :Default: fancyindex_show_dotfiles off :Context: http, server, location :Description: Whether to list files that are proceeded with a dot. Normal convention is to hide these. fancyindex_ignore ~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_ignore string1 [string2 [... stringN]]* :Default: No default. :Context: http, server, location :Description: Specifies a list of file names which will be not be shown in generated listings. If Nginx was built with PCRE support strings are interpreted as regular expressions. fancyindex_hide_symlinks ~~~~~~~~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_hide_symlinks* [*on* | *off*] :Default: fancyindex_hide_symlinks off :Context: http, server, location :Description: When enabled, generated listings will not contain symbolic links. fancyindex_hide_parent_dir ~~~~~~~~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_hide_parent_dir* [*on* | *off*] :Default: fancyindex_hide_parent_dir off :Context: http, server, location :Description: When enabled, it will not show parent directory. fancyindex_localtime ~~~~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_localtime* [*on* | *off*] :Default: fancyindex_localtime off :Context: http, server, location :Description: Enables showing file times as local time. Default is “off” (GMT time). fancyindex_time_format ~~~~~~~~~~~~~~~~~~~~~~ :Syntax: *fancyindex_time_format* string :Default: fancyindex_time_format "%Y-%b-%d %H:%M" :Context: http, server, location :Description: Format string used for timestamps. The format specifiers are a subset of those supported by the `strftime `_ function, and the behavior is locale-independent (for example, day and month names are always in English). The supported formats are: * ``%a``: Abbreviated name of the day of the week. * ``%A``: Full name of the day of the week. * ``%b``: Abbreviated month name. * ``%B``: Full month name. * ``%d``: Day of the month as a decimal number (range 01 to 31). * ``%e``: Like ``%d``, the day of the month as a decimal number, but a leading zero is replaced by a space. * ``%F``: Equivalent to ``%Y-%m-%d`` (the ISO 8601 date format). * ``%H``: Hour as a decimal number using a 24-hour clock (range 00 to 23). * ``%I``: Hour as a decimal number using a 12-hour clock (range 01 to 12). * ``%k``: Hour (24-hour clock) as a decimal number (range 0 to 23); single digits are preceded by a blank. * ``%l``: Hour (12-hour clock) as a decimal number (range 1 to 12); single digits are preceded by a blank. * ``%m``: Month as a decimal number (range 01 to 12). * ``%M``: Minute as a decimal number (range 00 to 59). * ``%p``: Either "AM" or "PM" according to the given time value. * ``%P``: Like ``%p`` but in lowercase: "am" or "pm". * ``%r``: Time in a.m. or p.m. notation. Equivalent to ``%I:%M:%S %p``. * ``%R``: Time in 24-hour notation (``%H:%M``). * ``%S``: Second as a decimal number (range 00 to 60). * ``%T``: Time in 24-hour notation (``%H:%M:%S``). * ``%u``: Day of the week as a decimal, range 1 to 7, Monday being 1. * ``%w``: Day of the week as a decimal, range 0 to 6, Monday being 0. * ``%y``: Year as a decimal number without a century (range 00 to 99). * ``%Y``: Year as a decimal number including the century. .. _nginx: https://nginx.org .. vim:ft=rst:spell:spelllang=en: ngx-fancyindex-0.5.2/config000066400000000000000000000014411413657470700156460ustar00rootroot00000000000000# vim:ft=sh: ngx_addon_name=ngx_http_fancyindex_module if [ "$ngx_module_link" = DYNAMIC ] ; then ngx_module_type=HTTP ngx_module_name=ngx_http_fancyindex_module ngx_module_srcs="$ngx_addon_dir/ngx_http_fancyindex_module.c" ngx_module_deps="$ngx_addon_dir/template.h" ngx_module_order="$ngx_module_name ngx_http_autoindex_module" . auto/module else # XXX: Insert fancyindex module *after* index module! # HTTP_MODULES=`echo "${HTTP_MODULES}" | sed -e \ 's/ngx_http_index_module/ngx_http_fancyindex_module ngx_http_index_module/'` NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_fancyindex_module.c" if [ $HTTP_ADDITION != YES ] ; then echo " - The 'addition' filter is needed for fancyindex_{header,footer}, but it was disabled" fi fi ngx-fancyindex-0.5.2/ngx_http_fancyindex_module.c000066400000000000000000001537071413657470700222470ustar00rootroot00000000000000/* * ngx_http_fancyindex_module.c * Copyright © 2007-2016 Adrian Perez * * Module used for fancy indexing of directories. Features and differences * with the stock nginx autoindex module: * * - Output is a table instead of a
 element with embedded  links.
 *  - Header and footer may be added to every generated directory listing.
 *  - Default header and/or footer are generated if custom ones are not
 *    configured. Files used for header and footer can only be local path
 *    names (i.e. you cannot insert the result of a subrequest.)
 *  - Proper HTML is generated: it should validate both as XHTML 1.0 Strict
 *    and HTML 4.01.
 *
 * Base functionality heavy based upon the stock nginx autoindex module,
 * which in turn was made by Igor Sysoev, like the majority of nginx.
 *
 * Distributed under terms of the BSD license.
 */

#include 
#include 
#include 
#include 

#include "template.h"

#if defined(__GNUC__) && (__GNUC__ >= 3)
# define ngx_force_inline __attribute__((__always_inline__))
#else /* !__GNUC__ */
# define ngx_force_inline
#endif /* __GNUC__ */


static const char *short_weekday[] = {
    "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun",
};
static const char *long_weekday[] = {
    "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Sunday",
};
static const char *short_month[] = {
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
};
static const char *long_month[] = {
    "January", "February", "March", "April", "May", "June", "July",
    "August", "September", "October", "November", "December",
};


#define DATETIME_FORMATS(F_, t) \
    F_ ('a',  3, "%3s",  short_weekday[((t)->ngx_tm_wday + 6) % 7]) \
    F_ ('A',  9, "%s",   long_weekday [((t)->ngx_tm_wday + 6) % 7]) \
    F_ ('b',  3, "%3s",  short_month[(t)->ngx_tm_mon - 1]         ) \
    F_ ('B',  9, "%s",   long_month [(t)->ngx_tm_mon - 1]         ) \
    F_ ('d',  2, "%02d", (t)->ngx_tm_mday                         ) \
    F_ ('e',  2, "%2d",  (t)->ngx_tm_mday                         ) \
    F_ ('F', 10, "%d-%02d-%02d",                                    \
                  (t)->ngx_tm_year,                                 \
                  (t)->ngx_tm_mon,                                  \
                  (t)->ngx_tm_mday                                ) \
    F_ ('H',  2, "%02d", (t)->ngx_tm_hour                         ) \
    F_ ('I',  2, "%02d", ((t)->ngx_tm_hour % 12) + 1              ) \
    F_ ('k',  2, "%2d",  (t)->ngx_tm_hour                         ) \
    F_ ('l',  2, "%2d",  ((t)->ngx_tm_hour % 12) + 1              ) \
    F_ ('m',  2, "%02d", (t)->ngx_tm_mon                          ) \
    F_ ('M',  2, "%02d", (t)->ngx_tm_min                          ) \
    F_ ('p',  2, "%2s",  (((t)->ngx_tm_hour < 12) ? "AM" : "PM")  ) \
    F_ ('P',  2, "%2s",  (((t)->ngx_tm_hour < 12) ? "am" : "pm")  ) \
    F_ ('r', 11, "%02d:%02d:%02d %2s",                              \
                 ((t)->ngx_tm_hour % 12) + 1,                       \
                 (t)->ngx_tm_min,                                   \
                 (t)->ngx_tm_sec,                                   \
                 (((t)->ngx_tm_hour < 12) ? "AM" : "PM")          ) \
    F_ ('R',  5, "%02d:%02d", (t)->ngx_tm_hour, (t)->ngx_tm_min   ) \
    F_ ('S',  2, "%02d", (t)->ngx_tm_sec                          ) \
    F_ ('T',  8, "%02d:%02d:%02d",                                  \
                 (t)->ngx_tm_hour,                                  \
                 (t)->ngx_tm_min,                                   \
                 (t)->ngx_tm_sec                                  ) \
    F_ ('u',  1, "%1d", (((t)->ngx_tm_wday + 6) % 7) + 1          ) \
    F_ ('w',  1, "%1d", ((t)->ngx_tm_wday + 6) % 7                ) \
    F_ ('y',  2, "%02d", (t)->ngx_tm_year % 100                   ) \
    F_ ('Y',  4, "%04d", (t)->ngx_tm_year                         )


static size_t
ngx_fancyindex_timefmt_calc_size (const ngx_str_t *fmt)
{
#define DATETIME_CASE(letter, fmtlen, fmt, ...) \
        case letter: result += (fmtlen); break;

    size_t i, result = 0;
    for (i = 0; i < fmt->len; i++) {
        if (fmt->data[i] == '%') {
            if (++i >= fmt->len) {
                result++;
                break;
            }
            switch (fmt->data[i]) {
                DATETIME_FORMATS(DATETIME_CASE,)
                default:
                    result++;
            }
        } else {
            result++;
        }
    }
    return result;

#undef DATETIME_CASE
}


static u_char*
ngx_fancyindex_timefmt (u_char *buffer, const ngx_str_t *fmt, const ngx_tm_t *tm)
{
#define DATETIME_CASE(letter, fmtlen, fmt, ...) \
        case letter: buffer = ngx_snprintf(buffer, fmtlen, fmt, ##__VA_ARGS__); break;

    size_t i;
    for (i = 0; i < fmt->len; i++) {
        if (fmt->data[i] == '%') {
            if (++i >= fmt->len) {
                *buffer++ = '%';
                break;
            }
            switch (fmt->data[i]) {
                DATETIME_FORMATS(DATETIME_CASE, tm)
                default:
                    *buffer++ = fmt->data[i];
            }
        } else {
            *buffer++ = fmt->data[i];
        }
    }
    return buffer;

#undef DATETIME_CASE
}

typedef struct {
    ngx_str_t path;
    ngx_str_t local;
} ngx_fancyindex_headerfooter_conf_t;

/**
 * Configuration structure for the fancyindex module. The configuration
 * commands defined in the module do fill in the members of this structure.
 */
typedef struct {
    ngx_flag_t enable;         /**< Module is enabled. */
    ngx_uint_t default_sort;   /**< Default sort criterion. */
    ngx_flag_t dirs_first;     /**< Group directories together first when sorting */
    ngx_flag_t localtime;      /**< File mtime dates are sent in local time. */
    ngx_flag_t exact_size;     /**< Sizes are sent always in bytes. */
    ngx_uint_t name_length;    /**< Maximum length of file names in bytes. */
    ngx_flag_t hide_symlinks;  /**< Hide symbolic links in listings. */
    ngx_flag_t show_path;      /**< Whether to display or not the path + '' after the header */
    ngx_flag_t hide_parent;    /**< Hide parent directory. */
    ngx_flag_t show_dot_files; /**< Show files that start with a dot.*/

    ngx_str_t  css_href;       /**< Link to a CSS stylesheet, or empty if none. */
    ngx_str_t  time_format;    /**< Format used for file timestamps. */

    ngx_array_t *ignore;       /**< List of files to ignore in listings. */

    ngx_fancyindex_headerfooter_conf_t header;
    ngx_fancyindex_headerfooter_conf_t footer;
} ngx_http_fancyindex_loc_conf_t;

#define NGX_HTTP_FANCYINDEX_SORT_CRITERION_NAME       0
#define NGX_HTTP_FANCYINDEX_SORT_CRITERION_SIZE       1
#define NGX_HTTP_FANCYINDEX_SORT_CRITERION_DATE       2
#define NGX_HTTP_FANCYINDEX_SORT_CRITERION_NAME_DESC  3
#define NGX_HTTP_FANCYINDEX_SORT_CRITERION_SIZE_DESC  4
#define NGX_HTTP_FANCYINDEX_SORT_CRITERION_DATE_DESC  5

static ngx_conf_enum_t ngx_http_fancyindex_sort_criteria[] = {
    { ngx_string("name"), NGX_HTTP_FANCYINDEX_SORT_CRITERION_NAME },
    { ngx_string("size"), NGX_HTTP_FANCYINDEX_SORT_CRITERION_SIZE },
    { ngx_string("date"), NGX_HTTP_FANCYINDEX_SORT_CRITERION_DATE },
    { ngx_string("name_desc"), NGX_HTTP_FANCYINDEX_SORT_CRITERION_NAME_DESC },
    { ngx_string("size_desc"), NGX_HTTP_FANCYINDEX_SORT_CRITERION_SIZE_DESC },
    { ngx_string("date_desc"), NGX_HTTP_FANCYINDEX_SORT_CRITERION_DATE_DESC },
    { ngx_null_string, 0 }
};

enum {
    NGX_HTTP_FANCYINDEX_HEADERFOOTER_SUBREQUEST,
    NGX_HTTP_FANCYINDEX_HEADERFOOTER_LOCAL,
};

static ngx_uint_t
headerfooter_kind(const ngx_str_t *value)
{
    static const struct {
        ngx_str_t name;
        ngx_uint_t value;
    } values[] = {
        { ngx_string("subrequest"), NGX_HTTP_FANCYINDEX_HEADERFOOTER_SUBREQUEST },
        { ngx_string("local"), NGX_HTTP_FANCYINDEX_HEADERFOOTER_LOCAL },
    };

    unsigned i;

    for (i = 0; i < sizeof(values) / sizeof(values[0]); i++) {
        if (value->len == values[i].name.len &&
            ngx_strcasecmp(value->data, values[i].name.data) == 0)
        {
            return values[i].value;
        }
    }

    return NGX_CONF_UNSET_UINT;
}

static char*
ngx_fancyindex_conf_set_headerfooter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_fancyindex_headerfooter_conf_t *item =
        (void*) (((char*) conf) + cmd->offset);
    ngx_str_t *values = cf->args->elts;

    if (item->path.data)
        return "is duplicate";

    item->path = values[1];

    /* Kind of path. Default is "subrequest". */
    ngx_uint_t kind = NGX_HTTP_FANCYINDEX_HEADERFOOTER_SUBREQUEST;
    if (cf->args->nelts == 3) {
        kind = headerfooter_kind(&values[2]);
        if (kind == NGX_CONF_UNSET_UINT) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "unknown header/footer kind \"%V\"", &values[2]);
            return NGX_CONF_ERROR;
        }
    }

    if (kind == NGX_HTTP_FANCYINDEX_HEADERFOOTER_LOCAL) {
        ngx_file_t file;
        ngx_file_info_t fi;
        ssize_t n;

        ngx_memzero(&file, sizeof(ngx_file_t));
        file.log = cf->log;
        file.fd = ngx_open_file(item->path.data, NGX_FILE_RDONLY, 0, 0);
        if (file.fd == NGX_INVALID_FILE) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
                               "cannot open file \"%V\"", &values[1]);
            return NGX_CONF_ERROR;
        }

        if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
            ngx_close_file(file.fd);
            ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
                               "cannot get info for file \"%V\"", &values[1]);
            return NGX_CONF_ERROR;
        }

        item->local.len = ngx_file_size(&fi);
        item->local.data = ngx_pcalloc(cf->pool, item->local.len + 1);
        if (item->local.data == NULL) {
            ngx_close_file(file.fd);
            return NGX_CONF_ERROR;
        }

        n = item->local.len;
        while (n > 0) {
            ssize_t r = ngx_read_file(&file,
                                      item->local.data + file.offset,
                                      n,
                                      file.offset);
            if (r == NGX_ERROR) {
                ngx_close_file(file.fd);
                ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
                                   "cannot read file \"%V\"", &values[1]);
                return NGX_CONF_ERROR;
            }

            n -= r;
        }
        item->local.data[item->local.len] = '\0';
    }

    return NGX_CONF_OK;
}

#define NGX_HTTP_FANCYINDEX_PREALLOCATE  50


/**
 * Calculates the length of a NULL-terminated string. It is ugly having to
 * remember to substract 1 from the sizeof result.
 */
#define ngx_sizeof_ssz(_s)  (sizeof(_s) - 1)

/**
 * Compute the length of a statically allocated array
 */
#define DIM(x) (sizeof(x)/sizeof(*(x)))

/**
 * Copy a static zero-terminated string. Useful to output template
 * string pieces into a temporary buffer.
 */
#define ngx_cpymem_ssz(_p, _t) \
	(ngx_cpymem((_p), (_t), sizeof(_t) - 1))

/**
 * Copy a ngx_str_t.
 */
#define ngx_cpymem_str(_p, _s) \
	(ngx_cpymem((_p), (_s).data, (_s).len))

/**
 * Check whether a particular bit is set in a particular value.
 */
#define ngx_has_flag(_where, _what) \
	(((_where) & (_what)) == (_what))




typedef struct {
    ngx_str_t      name;
    size_t         utf_len;
    ngx_uint_t     escape;
    ngx_uint_t     escape_html;
    ngx_uint_t     dir;
    time_t         mtime;
    off_t          size;
} ngx_http_fancyindex_entry_t;



static int ngx_libc_cdecl
    ngx_http_fancyindex_cmp_entries_name_desc(const void *one, const void *two);
static int ngx_libc_cdecl
    ngx_http_fancyindex_cmp_entries_size_desc(const void *one, const void *two);
static int ngx_libc_cdecl
    ngx_http_fancyindex_cmp_entries_mtime_desc(const void *one, const void *two);
static int ngx_libc_cdecl
    ngx_http_fancyindex_cmp_entries_name_asc(const void *one, const void *two);
static int ngx_libc_cdecl
    ngx_http_fancyindex_cmp_entries_size_asc(const void *one, const void *two);
static int ngx_libc_cdecl
    ngx_http_fancyindex_cmp_entries_mtime_asc(const void *one, const void *two);

static ngx_int_t ngx_http_fancyindex_error(ngx_http_request_t *r,
    ngx_dir_t *dir, ngx_str_t *name);

static ngx_int_t ngx_http_fancyindex_init(ngx_conf_t *cf);

static void *ngx_http_fancyindex_create_loc_conf(ngx_conf_t *cf);

static char *ngx_http_fancyindex_merge_loc_conf(ngx_conf_t *cf,
    void *parent, void *child);

static char *ngx_http_fancyindex_ignore(ngx_conf_t    *cf,
                                        ngx_command_t *cmd,
                                        void          *conf);

static uintptr_t
    ngx_fancyindex_escape_filename(u_char *dst, u_char*src, size_t size);

/*
 * These are used only once per handler invocation. We can tell GCC to
 * inline them always, if possible (see how ngx_force_inline is defined
 * above).
 */
static ngx_inline ngx_buf_t*
    make_header_buf(ngx_http_request_t *r, const ngx_str_t css_href)
    ngx_force_inline;


static ngx_command_t  ngx_http_fancyindex_commands[] = {

    { ngx_string("fancyindex"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, enable),
      NULL },

    { ngx_string("fancyindex_default_sort"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_enum_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, default_sort),
      &ngx_http_fancyindex_sort_criteria },

    { ngx_string("fancyindex_directories_first"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, dirs_first),
      NULL },

    { ngx_string("fancyindex_localtime"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, localtime),
      NULL },

    { ngx_string("fancyindex_exact_size"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, exact_size),
      NULL },

    { ngx_string("fancyindex_name_length"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_num_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, name_length),
      NULL },

    { ngx_string("fancyindex_header"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
      ngx_fancyindex_conf_set_headerfooter,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, header),
      NULL },

    { ngx_string("fancyindex_footer"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
      ngx_fancyindex_conf_set_headerfooter,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, footer),
      NULL },

    { ngx_string("fancyindex_css_href"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_str_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, css_href),
      NULL },

    { ngx_string("fancyindex_ignore"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
      ngx_http_fancyindex_ignore,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
      NULL },

    { ngx_string("fancyindex_hide_symlinks"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, hide_symlinks),
      NULL },

    { ngx_string("fancyindex_show_path"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, show_path),
      NULL },

    { ngx_string("fancyindex_show_dotfiles"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, show_dot_files),
      NULL },

    { ngx_string("fancyindex_hide_parent_dir"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, hide_parent),
      NULL },

    { ngx_string("fancyindex_time_format"),
      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
      ngx_conf_set_str_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_fancyindex_loc_conf_t, time_format),
      NULL },

    ngx_null_command
};


static ngx_http_module_t  ngx_http_fancyindex_module_ctx = {
    NULL,                                  /* preconfiguration */
    ngx_http_fancyindex_init,              /* postconfiguration */

    NULL,                                  /* create main configuration */
    NULL,                                  /* init main configuration */

    NULL,                                  /* create server configuration */
    NULL,                                  /* merge server configuration */

    ngx_http_fancyindex_create_loc_conf,   /* create location configuration */
    ngx_http_fancyindex_merge_loc_conf     /* merge location configuration */
};


ngx_module_t  ngx_http_fancyindex_module = {
    NGX_MODULE_V1,
    &ngx_http_fancyindex_module_ctx,       /* module context */
    ngx_http_fancyindex_commands,          /* module directives */
    NGX_HTTP_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};



static const ngx_str_t css_href_pre =
    ngx_string("\n");


#ifdef NGX_ESCAPE_URI_COMPONENT
static inline uintptr_t
ngx_fancyindex_escape_filename(u_char *dst, u_char *src, size_t size)
{
    return ngx_escape_uri(dst, src, size, NGX_ESCAPE_URI_COMPONENT);
}
#else /* !NGX_ESCAPE_URI_COMPONENT */
static uintptr_t
ngx_fancyindex_escape_filename(u_char *dst, u_char *src, size_t size)
{
    /*
     * The ngx_escape_uri() function will not escape colons or the
     * ? character, which signals the beginning of the query string.
     * So we handle those characters ourselves.
     *
     * TODO: Get rid of this once ngx_escape_uri() works as expected!
     */

    u_int escapes = 0;
    u_char *psrc = src;
    size_t psize = size;

    while (psize--) {
        switch (*psrc++) {
            case ':':
            case '?':
            case '[':
            case ']':
                escapes++;
                break;
        }
    }

    if (dst == NULL) {
        return escapes + ngx_escape_uri(NULL, src, size, NGX_ESCAPE_HTML);
    }
    else if (escapes == 0) {
        /* No need to do extra escaping, avoid the temporary buffer */
        return ngx_escape_uri(dst, src, size, NGX_ESCAPE_HTML);
    }
    else {
        uintptr_t uescapes = ngx_escape_uri(NULL, src, size, NGX_ESCAPE_HTML);
        size_t bufsz = size + 2 * uescapes;

        /*
         * GCC and CLANG both support stack-allocated variable length
         * arrays. Take advantage of that to avoid a malloc-free cycle.
         */
#if defined(__GNUC__) || defined(__clang__)
        u_char cbuf[bufsz];
        u_char *buf = cbuf;
#else  /* __GNUC__ || __clang__ */
        u_char *buf = (u_char*) malloc(sizeof(u_char) * bufsz);
#endif /* __GNUC__ || __clang__ */

        ngx_escape_uri(buf, src, size, NGX_ESCAPE_HTML);

        while (bufsz--) {
            switch (*buf) {
                case ':':
                    *dst++ = '%';
                    *dst++ = '3';
                    *dst++ = 'A';
                    break;
                case '?':
                    *dst++ = '%';
                    *dst++ = '3';
                    *dst++ = 'F';
                    break;
                case '[':
                    *dst++ = '%';
                    *dst++ = '5';
                    *dst++ = 'B';
                    break;
                case ']':
                    *dst++ = '%';
                    *dst++ = '5';
                    *dst++ = 'D';
                    break;
                default:
                    *dst++ = *buf;
            }
            buf++;
        }

#if !defined(__GNUC__) && !defined(__clang__)
        free(buf);
#endif /* !__GNUC__ && !__clang__ */

        return escapes + uescapes;
    }
}
#endif /* NGX_ESCAPE_URI_COMPONENT */


static ngx_inline ngx_buf_t*
make_header_buf(ngx_http_request_t *r, const ngx_str_t css_href)
{
    ngx_buf_t *b;
    size_t blen = r->uri.len
        + ngx_sizeof_ssz(t01_head1)
        + ngx_sizeof_ssz(t02_head2)
        + ngx_sizeof_ssz(t03_head3)
        + ngx_sizeof_ssz(t04_body1)
        ;

    if (css_href.len) {
        blen += css_href_pre.len \
              + css_href.len \
              + css_href_post.len
              ;
    }

    if ((b = ngx_create_temp_buf(r->pool, blen)) == NULL)
        return NULL;

    b->last = ngx_cpymem_ssz(b->last, t01_head1);

    if (css_href.len) {
        b->last = ngx_cpymem_str(b->last, css_href_pre);
        b->last = ngx_cpymem_str(b->last, css_href);
        b->last = ngx_cpymem_str(b->last, css_href_post);
    }

    b->last = ngx_cpymem_ssz(b->last, t02_head2);
    b->last = ngx_cpymem_str(b->last, r->uri);
    b->last = ngx_cpymem_ssz(b->last, t03_head3);
    b->last = ngx_cpymem_ssz(b->last, t04_body1);

    return b;
}


static ngx_inline ngx_int_t
make_content_buf(
        ngx_http_request_t *r, ngx_buf_t **pb,
        ngx_http_fancyindex_loc_conf_t *alcf)
{
    ngx_http_fancyindex_entry_t *entry;

    int (*sort_cmp_func)(const void *, const void *);
    const char  *sort_url_args = "";

    off_t        length;
    size_t       len, root, copy, allocated, escape_html;
    int64_t      multiplier;
    u_char      *filename, *last;
    ngx_tm_t     tm;
    ngx_array_t  entries;
    ngx_time_t  *tp;
    ngx_uint_t   i, j;
    ngx_str_t    path;
    ngx_dir_t    dir;
    ngx_buf_t   *b;

    static const char    *sizes[]  = { "EiB", "PiB", "TiB", "GiB", "MiB", "KiB", "B" };
    static const int64_t  exbibyte = 1024LL * 1024LL * 1024LL *
                                     1024LL * 1024LL * 1024LL;

    /*
     * NGX_DIR_MASK_LEN is lesser than NGX_HTTP_FANCYINDEX_PREALLOCATE
     */
    if ((last = ngx_http_map_uri_to_path(r, &path, &root,
                    NGX_HTTP_FANCYINDEX_PREALLOCATE)) == NULL)
        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    allocated = path.len;
    path.len  = last - path.data - 1;
    path.data[path.len] = '\0';

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http fancyindex: \"%s\"", path.data);

    if (ngx_open_dir(&path, &dir) == NGX_ERROR) {
        ngx_int_t rc, err = ngx_errno;
        ngx_uint_t level;

        if (err == NGX_ENOENT || err == NGX_ENOTDIR || err == NGX_ENAMETOOLONG) {
            level = NGX_LOG_ERR;
            rc = NGX_HTTP_NOT_FOUND;
        } else if (err == NGX_EACCES) {
            level = NGX_LOG_ERR;
            rc = NGX_HTTP_FORBIDDEN;
        } else {
            level = NGX_LOG_CRIT;
            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
        }

        ngx_log_error(level, r->connection->log, err,
                ngx_open_dir_n " \"%s\" failed", path.data);

        return rc;
    }

#if (NGX_SUPPRESS_WARN)
    /* MSVC thinks 'entries' may be used without having been initialized */
    ngx_memzero(&entries, sizeof(ngx_array_t));
#endif /* NGX_SUPPRESS_WARN */


    if (ngx_array_init(&entries, r->pool, 40,
                sizeof(ngx_http_fancyindex_entry_t)) != NGX_OK)
        return ngx_http_fancyindex_error(r, &dir, &path);

    filename = path.data;
    filename[path.len] = '/';

    /* Read directory entries and their associated information. */
    for (;;) {
        ngx_set_errno(0);

        if (ngx_read_dir(&dir) == NGX_ERROR) {
            ngx_int_t err = ngx_errno;

            if (err != NGX_ENOMOREFILES) {
                ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
                        ngx_read_dir_n " \"%V\" failed", &path);
                return ngx_http_fancyindex_error(r, &dir, &path);
            }
            break;
        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "http fancyindex file: \"%s\"", ngx_de_name(&dir));

        len = ngx_de_namelen(&dir);

        if (!alcf->show_dot_files && ngx_de_name(&dir)[0] == '.')
            continue;

        if (alcf->hide_symlinks && ngx_de_is_link (&dir))
            continue;

#if NGX_PCRE
        {
            ngx_str_t str;
            str.len = len;
            str.data = ngx_de_name(&dir);

            if (alcf->ignore && ngx_regex_exec_array(alcf->ignore, &str,
                                                     r->connection->log)
                != NGX_DECLINED)
            {
                continue;
            }
        }
#else /* !NGX_PCRE */
        if (alcf->ignore) {
            u_int match_found = 0;
            ngx_str_t *s = alcf->ignore->elts;

            for (i = 0; i < alcf->ignore->nelts; i++, s++) {
                if (ngx_strcmp(ngx_de_name(&dir), s->data) == 0) {
                    match_found = 1;
                    break;
                }
            }

            if (match_found) {
                continue;
            }
        }
#endif /* NGX_PCRE */

        if (!dir.valid_info) {
            /* 1 byte for '/' and 1 byte for terminating '\0' */
            if (path.len + 1 + len + 1 > allocated) {
                allocated = path.len + 1 + len + 1
                          + NGX_HTTP_FANCYINDEX_PREALLOCATE;

                if ((filename = ngx_palloc(r->pool, allocated)) == NULL)
                    return ngx_http_fancyindex_error(r, &dir, &path);

                last = ngx_cpystrn(filename, path.data, path.len + 1);
                *last++ = '/';
            }

            ngx_cpystrn(last, ngx_de_name(&dir), len + 1);

            if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) {
                ngx_int_t err = ngx_errno;

                if (err != NGX_ENOENT) {
                    ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
                            ngx_de_info_n " \"%s\" failed", filename);
                    continue;
                }

                if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) {
                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
                            ngx_de_link_info_n " \"%s\" failed", filename);
                    return ngx_http_fancyindex_error(r, &dir, &path);
                }
            }
        }

        if ((entry = ngx_array_push(&entries)) == NULL)
            return ngx_http_fancyindex_error(r, &dir, &path);

        entry->name.len  = len;
        entry->name.data = ngx_palloc(r->pool, len + 1);
        if (entry->name.data == NULL)
            return ngx_http_fancyindex_error(r, &dir, &path);

        ngx_cpystrn(entry->name.data, ngx_de_name(&dir), len + 1);
        entry->escape = 2 * ngx_fancyindex_escape_filename(NULL,
                                                           ngx_de_name(&dir),
                                                           len);
        entry->escape_html = ngx_escape_html(NULL,
                                             entry->name.data,
                                             entry->name.len);

        entry->dir     = ngx_de_is_dir(&dir);
        entry->mtime   = ngx_de_mtime(&dir);
        entry->size    = ngx_de_size(&dir);
        entry->utf_len = (r->headers_out.charset.len == 5 &&
                ngx_strncasecmp(r->headers_out.charset.data, (u_char*) "utf-8", 5) == 0)
            ?  ngx_utf8_length(entry->name.data, entry->name.len)
            : len;
    }

    if (ngx_close_dir(&dir) == NGX_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
                ngx_close_dir_n " \"%s\" failed", &path);
    }

    /*
     * Calculate needed buffer length.
     */

    escape_html = ngx_escape_html(NULL, r->uri.data, r->uri.len);

    if (alcf->show_path)
        len = r->uri.len + escape_html
          + ngx_sizeof_ssz(t05_body2)
          + ngx_sizeof_ssz(t06_list1)
          + ngx_sizeof_ssz(t_parentdir_entry)
          + ngx_sizeof_ssz(t07_list2)
          + ngx_fancyindex_timefmt_calc_size (&alcf->time_format) * entries.nelts
          ;
   else
        len = r->uri.len + escape_html
          + ngx_sizeof_ssz(t06_list1)
          + ngx_sizeof_ssz(t_parentdir_entry)
          + ngx_sizeof_ssz(t07_list2)
          + ngx_fancyindex_timefmt_calc_size (&alcf->time_format) * entries.nelts
          ;

    /*
     * If we are a the root of the webserver (URI =  "/" --> length of 1),
     * do not display the "Parent Directory" link.
     */
    if (r->uri.len == 1) {
        len -= ngx_sizeof_ssz(t_parentdir_entry);
    }

    entry = entries.elts;
    for (i = 0; i < entries.nelts; i++) {
        /*
         * Genearated table rows are as follows, unneeded whitespace
         * is stripped out:
         *
         *   
         *     fname
         *     sizedate
         *   
         */
        len += ngx_sizeof_ssz("")
            + entry[i].name.len + entry[i].utf_len + entry[i].escape_html
            + alcf->name_length + ngx_sizeof_ssz(">")
            + ngx_sizeof_ssz("")
            + 20 /* File size */
            + ngx_sizeof_ssz("")    /* Date prefix */
            + ngx_sizeof_ssz("\n") /* Date suffix */
            + 2 /* CR LF */
            ;
    }

    if ((b = ngx_create_temp_buf(r->pool, len)) == NULL)
        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    /*
     * Determine the sorting criteria. URL arguments look like:
     *
     *    C=x[&O=y]
     *
     * Where x={M,S,N} and y={A,D}
     */
    if ((r->args.len == 3 || (r->args.len == 7 && r->args.data[3] == '&')) &&
        r->args.data[0] == 'C' && r->args.data[1] == '=')
    {
        /* Determine whether the direction of the sorting */
        ngx_int_t sort_descending = r->args.len == 7
                                 && r->args.data[4] == 'O'
                                 && r->args.data[5] == '='
                                 && r->args.data[6] == 'D';

        /* Pick the sorting criteria */
        switch (r->args.data[2]) {
            case 'M': /* Sort by mtime */
                if (sort_descending) {
                    sort_cmp_func = ngx_http_fancyindex_cmp_entries_mtime_desc;
                    if (alcf->default_sort != NGX_HTTP_FANCYINDEX_SORT_CRITERION_DATE_DESC)
                        sort_url_args = "?C=M&O=D";
                }
                else {
                    sort_cmp_func = ngx_http_fancyindex_cmp_entries_mtime_asc;
                    if (alcf->default_sort != NGX_HTTP_FANCYINDEX_SORT_CRITERION_DATE)
                        sort_url_args = "?C=M&O=A";
                }
                break;
            case 'S': /* Sort by size */
                if (sort_descending) {
                    sort_cmp_func = ngx_http_fancyindex_cmp_entries_size_desc;
                    if (alcf->default_sort != NGX_HTTP_FANCYINDEX_SORT_CRITERION_SIZE_DESC)
                        sort_url_args = "?C=S&O=D";
                }
                else {
                    sort_cmp_func = ngx_http_fancyindex_cmp_entries_size_asc;
                        if (alcf->default_sort != NGX_HTTP_FANCYINDEX_SORT_CRITERION_SIZE)
                    sort_url_args = "?C=S&O=A";
                }
                break;
            case 'N': /* Sort by name */
            default:
                if (sort_descending) {
                    sort_cmp_func = ngx_http_fancyindex_cmp_entries_name_desc;
                    if (alcf->default_sort != NGX_HTTP_FANCYINDEX_SORT_CRITERION_NAME_DESC)
                        sort_url_args = "?C=N&O=D";
                }
                else {
                    sort_cmp_func = ngx_http_fancyindex_cmp_entries_name_asc;
                    if (alcf->default_sort != NGX_HTTP_FANCYINDEX_SORT_CRITERION_NAME)
                        sort_url_args = "?C=N&O=A";
                }
                break;
        }
    }
    else {
        switch (alcf->default_sort) {
            case NGX_HTTP_FANCYINDEX_SORT_CRITERION_DATE_DESC:
                sort_cmp_func = ngx_http_fancyindex_cmp_entries_mtime_desc;
                break;
            case NGX_HTTP_FANCYINDEX_SORT_CRITERION_DATE:
                sort_cmp_func = ngx_http_fancyindex_cmp_entries_mtime_asc;
                break;
            case NGX_HTTP_FANCYINDEX_SORT_CRITERION_SIZE_DESC:
                sort_cmp_func = ngx_http_fancyindex_cmp_entries_size_desc;
                break;
            case NGX_HTTP_FANCYINDEX_SORT_CRITERION_SIZE:
                sort_cmp_func = ngx_http_fancyindex_cmp_entries_size_asc;
                break;
            case NGX_HTTP_FANCYINDEX_SORT_CRITERION_NAME_DESC:
                sort_cmp_func = ngx_http_fancyindex_cmp_entries_name_desc;
                break;
            case NGX_HTTP_FANCYINDEX_SORT_CRITERION_NAME:
            default:
                sort_cmp_func = ngx_http_fancyindex_cmp_entries_name_asc;
                break;
        }
    }

    /* Sort entries, if needed */
    if (entries.nelts > 1) {
        if (alcf->dirs_first)
        {
            ngx_http_fancyindex_entry_t *l, *r;

            l = entry;
            r = entry + entries.nelts - 1;
            while (l < r)
            {
                while (l < r && l->dir)
                    l++;
                while (l < r && !r->dir)
                    r--;
                if (l < r) {
                    /* Now l points a file while r points a directory */
                    ngx_http_fancyindex_entry_t tmp;
                    tmp = *l;
                    *l = *r;
                    *r = tmp;
                }
            }
            if (r->dir)
                r++;

            if (r > entry)
                /* Sort directories */
                ngx_qsort(entry, (size_t)(r - entry),
                        sizeof(ngx_http_fancyindex_entry_t), sort_cmp_func);
            if (r < entry + entries.nelts)
                /* Sort files */
                ngx_qsort(r, (size_t)(entry + entries.nelts - r),
                        sizeof(ngx_http_fancyindex_entry_t), sort_cmp_func);
        } else {
            ngx_qsort(entry, (size_t)entries.nelts,
                    sizeof(ngx_http_fancyindex_entry_t), sort_cmp_func);
        }
    }

    /* Display the path, if needed */
    if (alcf->show_path){
        b->last = last = (u_char *) ngx_escape_html(b->last, r->uri.data, r->uri.len);
        b->last = ngx_cpymem_ssz(b->last, t05_body2);
    }

    /* Open the  tag */
    b->last = ngx_cpymem_ssz(b->last, t06_list1);

    tp = ngx_timeofday();

    /* "Parent dir" entry, always first if displayed */
    if (r->uri.len > 1 && alcf->hide_parent == 0) {
        b->last = ngx_cpymem_ssz(b->last,
                                 ""
                                 ""
                                 ""
                                 ""
                                 ""
                                 CRLF);
    }

    /* Entries for directories and files */
    for (i = 0; i < entries.nelts; i++) {
        b->last = ngx_cpymem_ssz(b->last, "");

        *b->last++ = CR;
        *b->last++ = LF;
    }

    /* Output table bottom */
    b->last = ngx_cpymem_ssz(b->last, t07_list2);

    *pb = b;
    return NGX_OK;
}



static ngx_int_t
ngx_http_fancyindex_handler(ngx_http_request_t *r)
{
    ngx_http_request_t             *sr;
    ngx_str_t                      *sr_uri;
    ngx_str_t                       rel_uri;
    ngx_int_t                       rc;
    ngx_http_fancyindex_loc_conf_t *alcf;
    ngx_chain_t                     out[3] = {
        { NULL, NULL }, { NULL, NULL}, { NULL, NULL }};


    if (r->uri.data[r->uri.len - 1] != '/') {
        return NGX_DECLINED;
    }

    /* TODO: Win32 */
#if defined(nginx_version) \
    && ((nginx_version < 7066) \
        || ((nginx_version > 8000) && (nginx_version < 8038)))
    if (r->zero_in_uri) {
        return NGX_DECLINED;
    }
#endif

    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
        return NGX_DECLINED;
    }

    alcf = ngx_http_get_module_loc_conf(r, ngx_http_fancyindex_module);

    if (!alcf->enable) {
        return NGX_DECLINED;
    }

    if ((rc = make_content_buf(r, &out[0].buf, alcf)) != NGX_OK)
        return rc;

    out[0].buf->last_in_chain = 1;

    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_type_len  = ngx_sizeof_ssz("text/html");
    r->headers_out.content_type.len  = ngx_sizeof_ssz("text/html");
    r->headers_out.content_type.data = (u_char *) "text/html";

    rc = ngx_http_send_header(r);
    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only)
        return rc;

    if (alcf->header.path.len > 0 && alcf->header.local.len == 0) {
        /* URI is configured, make Nginx take care of with a subrequest. */
        sr_uri = &alcf->header.path;

        if (*sr_uri->data != '/') {
            /* Relative path */
            rel_uri.len  = r->uri.len + alcf->header.path.len;
            rel_uri.data = ngx_palloc(r->pool, rel_uri.len);
            if (rel_uri.data == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }
            ngx_memcpy(ngx_cpymem(rel_uri.data, r->uri.data, r->uri.len),
                    alcf->header.path.data, alcf->header.path.len);
            sr_uri = &rel_uri;
        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "http fancyindex: header subrequest \"%V\"", sr_uri);

        rc = ngx_http_subrequest(r, sr_uri, NULL, &sr, NULL, 0);
        if (rc == NGX_ERROR || rc == NGX_DONE) {
            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http fancyindex: header subrequest for \"%V\" failed", sr_uri);
            return rc;
        }

        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "http fancyindex: header subrequest status = %i",
                sr->headers_out.status);
        /* ngx_http_subrequest returns NGX_OK(0), not NGX_HTTP_OK(200) */
        if (sr->headers_out.status != NGX_OK) {
            /*
             * XXX: Should we write a message to the error log just in case
             * we get something different from a 404?
             */
            goto add_builtin_header;
        }
    }
    else {
add_builtin_header:
        /* Make space before */
        out[1].next = out[0].next;
        out[1].buf  = out[0].buf;
        /* Chain header buffer */
        out[0].next = &out[1];
        if (alcf->header.local.len > 0) {
            /* Header buffer is local, make a buffer pointing to the data. */
            out[0].buf = ngx_calloc_buf(r->pool);
            if (out[0].buf == NULL)
                return NGX_ERROR;
            out[0].buf->memory = 1;
            out[0].buf->pos = alcf->header.local.data;
            out[0].buf->last = alcf->header.local.data + alcf->header.local.len;
        } else {
            /* Prepare a buffer with the contents of the builtin header. */
            out[0].buf = make_header_buf(r, alcf->css_href);
        }
    }

    /* If footer is disabled, chain up footer buffer. */
    if (alcf->footer.path.len == 0 || alcf->footer.local.len > 0) {
        ngx_uint_t last = (alcf->header.path.len == 0) ? 2 : 1;

        out[last-1].next = &out[last];
        out[last].buf = ngx_calloc_buf(r->pool);
        if (out[last].buf == NULL)
            return NGX_ERROR;

        out[last].buf->memory = 1;
        if (alcf->footer.local.len > 0) {
            out[last].buf->pos = alcf->footer.local.data;
            out[last].buf->last = alcf->footer.local.data + alcf->footer.local.len;
        } else {
            out[last].buf->pos = (u_char*) t08_foot1;
            out[last].buf->last = (u_char*) t08_foot1 + sizeof(t08_foot1) - 1;
        }

        out[last-1].buf->last_in_chain = 0;
        out[last].buf->last_in_chain   = 1;
        out[last].buf->last_buf        = 1;
        /* Send everything with a single call :D */
        return ngx_http_output_filter(r, &out[0]);
    }

    /*
     * If we reach here, we were asked to send a custom footer. We need to:
     * partially send whatever is referenced from out[0] and then send the
     * footer as a subrequest. If the subrequest fails, we should send the
     * standard footer as well.
     */
    rc = ngx_http_output_filter(r, &out[0]);

    if (rc != NGX_OK && rc != NGX_AGAIN)
        return NGX_HTTP_INTERNAL_SERVER_ERROR;

    /* URI is configured, make Nginx take care of with a subrequest. */
    sr_uri = &alcf->footer.path;

    if (*sr_uri->data != '/') {
        /* Relative path */
        rel_uri.len  = r->uri.len + alcf->footer.path.len;
        rel_uri.data = ngx_palloc(r->pool, rel_uri.len);
        if (rel_uri.data == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
        ngx_memcpy(ngx_cpymem(rel_uri.data, r->uri.data, r->uri.len),
                alcf->footer.path.data, alcf->footer.path.len);
        sr_uri = &rel_uri;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
            "http fancyindex: footer subrequest \"%V\"", sr_uri);

    rc = ngx_http_subrequest(r, sr_uri, NULL, &sr, NULL, 0);
    if (rc == NGX_ERROR || rc == NGX_DONE) {
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                "http fancyindex: footer subrequest for \"%V\" failed", sr_uri);
        return rc;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
            "http fancyindex: header subrequest status = %i",
            sr->headers_out.status);

    /* see above: ngx_http_subrequest resturns NGX_OK (0) not NGX_HTTP_OK (200) */
    if (sr->headers_out.status != NGX_OK) {
        /*
         * XXX: Should we write a message to the error log just in case
         * we get something different from a 404?
         */
        out[0].next = NULL;
        out[0].buf = ngx_calloc_buf(r->pool);
        if (out[0].buf == NULL)
            return NGX_ERROR;
        out[0].buf->memory = 1;
        out[0].buf->pos = (u_char*) t08_foot1;
        out[0].buf->last = (u_char*) t08_foot1 + sizeof(t08_foot1) - 1;
        out[0].buf->last_in_chain = 1;
        out[0].buf->last_buf = 1;
        /* Directly send out the builtin footer */
        return ngx_http_output_filter(r, &out[0]);
    }

    return (r != r->main) ? rc : ngx_http_send_special(r, NGX_HTTP_LAST);
}


static int ngx_libc_cdecl
ngx_http_fancyindex_cmp_entries_name_desc(const void *one, const void *two)
{
    ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one;
    ngx_http_fancyindex_entry_t *second = (ngx_http_fancyindex_entry_t *) two;

    return (int) ngx_strcmp(second->name.data, first->name.data);
}


static int ngx_libc_cdecl
ngx_http_fancyindex_cmp_entries_size_desc(const void *one, const void *two)
{
    ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one;
    ngx_http_fancyindex_entry_t *second = (ngx_http_fancyindex_entry_t *) two;

    return (first->size < second->size) - (first->size > second->size);
}


static int ngx_libc_cdecl
ngx_http_fancyindex_cmp_entries_mtime_desc(const void *one, const void *two)
{
    ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one;
    ngx_http_fancyindex_entry_t *second = (ngx_http_fancyindex_entry_t *) two;

    return (int) (second->mtime - first->mtime);
}


static int ngx_libc_cdecl
ngx_http_fancyindex_cmp_entries_name_asc(const void *one, const void *two)
{
    ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one;
    ngx_http_fancyindex_entry_t *second = (ngx_http_fancyindex_entry_t *) two;

    return (int) ngx_strcmp(first->name.data, second->name.data);
}


static int ngx_libc_cdecl
ngx_http_fancyindex_cmp_entries_size_asc(const void *one, const void *two)
{
    ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one;
    ngx_http_fancyindex_entry_t *second = (ngx_http_fancyindex_entry_t *) two;

    return (first->size > second->size) - (first->size < second->size);
}


static int ngx_libc_cdecl
ngx_http_fancyindex_cmp_entries_mtime_asc(const void *one, const void *two)
{
    ngx_http_fancyindex_entry_t *first = (ngx_http_fancyindex_entry_t *) one;
    ngx_http_fancyindex_entry_t *second = (ngx_http_fancyindex_entry_t *) two;

    return (int) (first->mtime - second->mtime);
}


static ngx_int_t
ngx_http_fancyindex_error(ngx_http_request_t *r, ngx_dir_t *dir, ngx_str_t *name)
{
    if (ngx_close_dir(dir) == NGX_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
                      ngx_close_dir_n " \"%V\" failed", name);
    }

    return NGX_HTTP_INTERNAL_SERVER_ERROR;
}


static void *
ngx_http_fancyindex_create_loc_conf(ngx_conf_t *cf)
{
    ngx_http_fancyindex_loc_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fancyindex_loc_conf_t));
    if (conf == NULL) {
        return NGX_CONF_ERROR;
    }

    /*
     * Set by ngx_pcalloc:
     *    conf->header.*.len     = 0
     *    conf->header.*.data    = NULL
     *    conf->footer.*.len     = 0
     *    conf->footer.*.data    = NULL
     *    conf->css_href.len     = 0
     *    conf->css_href.data    = NULL
     *    conf->time_format.len  = 0
     *    conf->time_format.data = NULL
     */
    conf->enable         = NGX_CONF_UNSET;
    conf->default_sort   = NGX_CONF_UNSET_UINT;
    conf->dirs_first     = NGX_CONF_UNSET;
    conf->localtime      = NGX_CONF_UNSET;
    conf->name_length    = NGX_CONF_UNSET_UINT;
    conf->exact_size     = NGX_CONF_UNSET;
    conf->ignore         = NGX_CONF_UNSET_PTR;
    conf->hide_symlinks  = NGX_CONF_UNSET;
    conf->show_path      = NGX_CONF_UNSET;
    conf->hide_parent    = NGX_CONF_UNSET;
    conf->show_dot_files = NGX_CONF_UNSET;

    return conf;
}


static char *
ngx_http_fancyindex_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
    ngx_http_fancyindex_loc_conf_t *prev = parent;
    ngx_http_fancyindex_loc_conf_t *conf = child;

    (void) cf; /* unused */

    ngx_conf_merge_value(conf->enable, prev->enable, 0);
    ngx_conf_merge_uint_value(conf->default_sort, prev->default_sort, NGX_HTTP_FANCYINDEX_SORT_CRITERION_NAME);
    ngx_conf_merge_value(conf->dirs_first, prev->dirs_first, 1);
    ngx_conf_merge_value(conf->localtime, prev->localtime, 0);
    ngx_conf_merge_value(conf->exact_size, prev->exact_size, 1);
    ngx_conf_merge_value(conf->show_path, prev->show_path, 1);
    ngx_conf_merge_value(conf->show_dot_files, prev->show_dot_files, 0);
    ngx_conf_merge_uint_value(conf->name_length, prev->name_length, 50);

    ngx_conf_merge_str_value(conf->header.path, prev->header.path, "");
    ngx_conf_merge_str_value(conf->header.path, prev->header.local, "");
    ngx_conf_merge_str_value(conf->footer.path, prev->footer.path, "");
    ngx_conf_merge_str_value(conf->footer.path, prev->footer.local, "");

    ngx_conf_merge_str_value(conf->css_href, prev->css_href, "");
    ngx_conf_merge_str_value(conf->time_format, prev->time_format, "%Y-%b-%d %H:%M");

    ngx_conf_merge_ptr_value(conf->ignore, prev->ignore, NULL);
    ngx_conf_merge_value(conf->hide_symlinks, prev->hide_symlinks, 0);
    ngx_conf_merge_value(conf->hide_parent, prev->hide_parent, 0);

    /* Just make sure we haven't disabled the show_path directive without providing a custom header */
    if (conf->show_path == 0 && conf->header.path.len == 0)
    {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "FancyIndex : cannot set show_path to off without providing a custom header !");
        return NGX_CONF_ERROR;
    }

    return NGX_CONF_OK;
}


static char*
ngx_http_fancyindex_ignore(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_fancyindex_loc_conf_t *alcf = conf;
    ngx_str_t *value;

    (void) cmd; /* unused */

#if (NGX_PCRE)
    ngx_uint_t          i;
    ngx_regex_elt_t    *re;
    ngx_regex_compile_t rc;
    u_char              errstr[NGX_MAX_CONF_ERRSTR];

    if (alcf->ignore == NGX_CONF_UNSET_PTR) {
        alcf->ignore = ngx_array_create(cf->pool, 2, sizeof(ngx_regex_elt_t));
        if (alcf->ignore == NULL) {
            return NGX_CONF_ERROR;
        }
    }

    value = cf->args->elts;

    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));

    rc.err.data = errstr;
    rc.err.len  = NGX_MAX_CONF_ERRSTR;
    rc.pool     = cf->pool;

    for (i = 1; i < cf->args->nelts; i++) {
        re = ngx_array_push(alcf->ignore);
        if (re == NULL) {
            return NGX_CONF_ERROR;
        }

        rc.pattern = value[i];
        rc.options = NGX_REGEX_CASELESS;

        if (ngx_regex_compile(&rc) != NGX_OK) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
            return NGX_CONF_ERROR;
        }

        re->name  = value[i].data;
        re->regex = rc.regex;
    }

    return NGX_CONF_OK;
#else /* !NGX_PCRE */
    ngx_uint_t i;
    ngx_str_t *str;

    if (alcf->ignore == NGX_CONF_UNSET_PTR) {
        alcf->ignore = ngx_array_create(cf->pool, 2, sizeof(ngx_str_t));
        if (alcf->ignore == NULL) {
            return NGX_CONF_ERROR;
        }
    }

    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {
        str = ngx_array_push(alcf->ignore);
        if (str == NULL) {
            return NGX_CONF_ERROR;
        }

        str->data = value[i].data;
        str->len  = value[i].len;
    }

    return NGX_CONF_OK;
#endif /* NGX_PCRE */

}


static ngx_int_t
ngx_http_fancyindex_init(ngx_conf_t *cf)
{
    ngx_http_handler_pt        *h;
    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);

    h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
    if (h == NULL) {
        return NGX_ERROR;
    }

    *h = ngx_http_fancyindex_handler;

    return NGX_OK;
}

/* vim:et:sw=4:ts=4:
 */
ngx-fancyindex-0.5.2/t/000077500000000000000000000000001413657470700147215ustar00rootroot00000000000000ngx-fancyindex-0.5.2/t/00-build-artifacts.test000066400000000000000000000011031413657470700211070ustar00rootroot00000000000000#! /bin/bash
cat <<---
This test checks that the built Nginx either has the dynamic fancyindex
module available, or that it's not there (for static builds).
--

readonly nginx_path="${PREFIX}/sbin/nginx"
readonly so_path="${PREFIX}/modules/ngx_http_fancyindex_module.so"

if [[ ! -x ${nginx_path} ]] ; then
	fail "executable binary not found at '%s'\n" "${nginx_path}"
fi

if ${DYNAMIC} ; then
	if [[ ! -r ${so_path} ]] ; then
		fail "module not found at '%s'\n" "${so_path}"
	fi
else
	if [[ -r ${so_path} ]] ; then
		fail "module should not exist at '%s'\n" "${so_path}"
	fi
fi
ngx-fancyindex-0.5.2/t/01-smoke-hasindex.test000066400000000000000000000003401413657470700207540ustar00rootroot00000000000000#! /bin/bash
cat <<---
This test fetches the root directory served by Nginx, which has no index file,
and checks that the output contains something that resembles a directory index.
--
nginx_start
grep 'Index of' <( fetch )
ngx-fancyindex-0.5.2/t/02-smoke-indexisfancy.test000066400000000000000000000006461413657470700216470ustar00rootroot00000000000000#! /bin/bash
cat <<---
This test fetches the root directory served by Nginx, which has no index file,
and checks that the output contains something that resembles the output from
the fancyindex module.
--
nginx_start
content=$(fetch --with-headers)
grep 'Index of /' <<< "${content}"  # It is an index
grep '\'  <<< "${content}"  # It contains a table
grep '^  Content-Type:[[:space:]]*text/html' <<< "${content}"
ngx-fancyindex-0.5.2/t/03-exact_size_off.test000066400000000000000000000003561413657470700210360ustar00rootroot00000000000000#! /bin/bash
cat <<---
We test if the output from using "fancyindex_exact_size off" looks sane
--
nginx_start 'fancyindex_exact_size off;'
content=$(fetch)
grep -e '[1-9]\.[0-9] KiB'  <<< "${content}"
grep -E '[0-9]+ B'  <<< "${content}"
ngx-fancyindex-0.5.2/t/04-hasindex-html.test000066400000000000000000000011771413657470700206160ustar00rootroot00000000000000#! /bin/bash
cat <<---
This test fetches the root directory served by Nginx, which has no index
file, and checks the output contains a few HTML elements know to exist in
a directory index.
--
use pup
nginx_start

content=$( fetch )

# Check page title
[[ $(pup -p title text{} <<< "${content}") = 'Index of /' ]]

# Check table headers
[[ $(pup -n body table thead th a:first-child <<< "${content}") -eq 3 ]]
{
	read -r name_label
	read -r size_label
	read -r date_label
} < <(  pup -p body table thead th a:first-child text{} <<< "${content}" )
[[ ${name_label} = File\ Name ]]
[[ ${size_label} = File\ Size ]]
[[ ${date_label} = Date ]]
ngx-fancyindex-0.5.2/t/05-sort-by-size.test000066400000000000000000000014641413657470700204200ustar00rootroot00000000000000#! /bin/bash
cat <<---
This test validates that the sorting by file size works.
--
use pup
nginx_start

# Ascending sort.
previous=''
while read -r size ; do
	if [[ ${size} = - ]] ; then
		continue
	fi
	if [[ -z ${previous} ]] ; then
		previous=${size}
		continue
	fi
	[[ ${previous} -le ${size} ]] || fail \
		'Size %d should be smaller than %d\n' "${previous}" "${size}"
done < <( fetch '/?C=S&O=A' \
	    | pup -p body table tbody 'td:nth-child(2)' text{} )

# Descending sort.
previous=''
while read -r size ; do
	if [[ ${size} = - ]] ; then
		continue
	fi
	if [[ -z ${previous} ]] ; then
		previous=${size}
		continue
	fi
	[[ ${previous} -ge ${size} ]] || fail \
		'Size %d should be greater than %d\n' "${previous}" "${size}"
done < <( fetch '/?C=S&O=D' \
	    | pup -p body table tbody 'td:nth-child(2)' text{} )
ngx-fancyindex-0.5.2/t/06-hide_parent.test000066400000000000000000000012031413657470700203230ustar00rootroot00000000000000#! /bin/bash
cat <<---
This test check the output using "fancyindex_hide_parent_dir on"
--
use pup
nginx_start 'fancyindex_hide_parent_dir on;'

content=$( fetch /child-directory/ )

# Check page title
[[ $(pup -p title text{} <<< "${content}") = "Index of /child-directory/" ]]

# Check table headers
[[ $(pup -n body table tbody tr:first-child td <<< "${content}") -eq 3 ]]
{
	read -r name_label
	read -r size_label
	read -r date_label
} < <(  pup -p body table tbody tr:first-child td text{} <<< "${content}" )
[[ ${name_label} != Parent\ Directory/ ]]
[[ ${name_label} = empty-file.txt ]]
[[ ${size_label} != - ]]
[[ ${date_label} != - ]]
ngx-fancyindex-0.5.2/t/07-directory-first.test000066400000000000000000000022001413657470700211710ustar00rootroot00000000000000#! /bin/bash
cat <<---
This test check the output using "fancyindex_directories_first on"
--
use pup

for d in "008d" "000d" "004d" ; do
	mkdir -p "${TESTDIR}/dir_first/${d}"
done
for f in "005f" "001f" "003f"; do
	touch "${TESTDIR}/dir_first/${f}"
done
for d in "006d" "002d" ; do
	mkdir -p "${TESTDIR}/dir_first/${d}"
done

nginx_start 'fancyindex_directories_first on;'
previous=''
cur_type=''
while read -r name ; do
	case "$name" in
	*Parent*)
		;;
	*d*)
		echo "dir $name"
		[[ "$cur_type" = f ]] && fail 'Directories should come before Files'
		cur_type=d
		if [[ -z ${previous} ]] ; then
			previous=${name}
		else
			[[ ${previous} < ${name} ]] || fail \
				'Name %s should come before %s\n' "${previous}" "${name}"
		fi
		;;
	*f*)
		echo "file $name"
		[[ -z "$cur_type" ]] && fail 'Directories should come before Files'
		if [[ "$cur_type" = d ]] ; then
			cur_type=f
			previous=${name}
		else
			[[ ${previous} < ${name} ]] || fail \
				'Name %s should come before %s\n' "${previous}" "${name}"
		fi
		;;
	esac
done < <( fetch '/dir_first/' \
		| pup -p body table tbody 'td:nth-child(1)' text{} )

nginx_is_running || fail "Nginx died"
ngx-fancyindex-0.5.2/t/07-show_dotfiles.test000066400000000000000000000006051413657470700207200ustar00rootroot00000000000000#! /bin/bash
cat <<---
Test the option to show dotfiles.
--
# Turn it on.
nginx_start 'fancyindex_show_dotfiles on;'
on_content=$(fetch /show_dotfiles/)
nginx_stop
if [ $(grep '.okay'  <<< "${on_content}") -ne 0 ] ; then
    exit 1
fi

# Turn it off.
nginx_start
off_content=$(fetch /show_dotfiles/)
nginx_stop
if [ $(grep '.okay'  <<< "${on_content}") -eq 0] ; then
    exit 1
fi

exit 0
ngx-fancyindex-0.5.2/t/08-local-footer.test000066400000000000000000000006141413657470700204360ustar00rootroot00000000000000#! /bin/bash
cat <<---
This test checks that a local footer can be included with
"fancyindex_header ... local"
--
use pup

cat > "${TESTDIR}/footer" <yes
EOF

nginx_start "fancyindex_footer \"${TESTDIR}/footer\" local;"

T=$(fetch / | pup -p body 'div#customfooter' text{})
[[ $T == yes ]] ||  fail 'Custom header missing'

nginx_is_running || fail 'Nginx died'
ngx-fancyindex-0.5.2/t/09-local-header.test000066400000000000000000000006141413657470700203710ustar00rootroot00000000000000#! /bin/bash
cat <<---
This test checks that a local header can be included with
"fancyindex_header ... local"
--
use pup

cat > "${TESTDIR}/header" <yes
EOF

nginx_start "fancyindex_header \"${TESTDIR}/header\" local;"

T=$(fetch / | pup -p body 'div#customheader' text{})
[[ $T == yes ]] ||  fail 'Custom header missing'

nginx_is_running || fail 'Nginx died'
ngx-fancyindex-0.5.2/t/10-local-headerfooter.test000066400000000000000000000012301413657470700215730ustar00rootroot00000000000000#! /bin/bash
cat <<---
This test checks that both a local header and footer can be included with
"fancyindex_{header,footer} ... local"
--
use pup

cat > "${TESTDIR}/header" <yes
EOF
cat > "${TESTDIR}/footer" <yes
EOF

nginx_start "fancyindex_header \"${TESTDIR}/header\" local;
             fancyindex_footer \"${TESTDIR}/footer\" local;"

P=$(fetch /)

H=$(pup -p body 'div#customheader' text{} <<< "$P")
[[ $H == yes ]] ||  fail 'Custom header missing'

F=$(pup -p body 'div#customfooter' text{} <<< "$P")
[[ $F == yes ]] || fail 'Custom footer missing'

nginx_is_running || fail 'Nginx died'
ngx-fancyindex-0.5.2/t/11-local-footer-nested.test000066400000000000000000000017641413657470700217170ustar00rootroot00000000000000#! /bin/bash
cat <<---
This test checks that local footers are correctly included in presence of
directives in nested locations:

	fancyindex_footer  local;
	location /sub {
		fancyindex_footer  local;
	}

--
use pup

echo '
yes
' > "${TESTDIR}/top-footer" echo '
yes
' > "${TESTDIR}/sub-footer" nginx_start "fancyindex_footer \"${TESTDIR}/top-footer\" local; location /child-directory { fancyindex_footer \"${TESTDIR}/sub-footer\" local; }" T=$(fetch /) echo "$T" > "$TESTDIR/top.html" [[ $(pup -p body 'div#topfooter' text{} <<< "$T") = yes ]] || fail 'Custom header missing at /' [[ -z $(pup -p body 'div#subfooter' text{} <<< "$T") ]] || fail 'Wrong header at /' T=$(fetch /child-directory/) [[ $(pup -p body 'div#subfooter' text{} <<< "$T") = yes ]] || fail 'Custom header missing at /sub/' [[ -z $(pup -p body 'div#topfooter' text{} <<< "$T") ]] || fail 'Wrong header at /sub/' nginx_is_running || fail 'Nginx died' ngx-fancyindex-0.5.2/t/12-local-footer-nested.test000066400000000000000000000005021413657470700217050ustar00rootroot00000000000000#! /bin/bash cat <<--- This test checks that the configuration file is properly parsed if there is only one parameter passed to the fancyndex_header and fancyindex_footer configuration directives. -- nginx_start 'fancyindex_header "/header"; fancyindex_footer "/footer";' nginx_is_running || fail 'Nginx died' ngx-fancyindex-0.5.2/t/bug61-empty-file-segfault.test000066400000000000000000000006671413657470700224400ustar00rootroot00000000000000#! /bin/bash cat <<--- Bug #61: Listing a directory with an empty file crashes Nginx https://github.com/aperezdc/ngx-fancyindex/issues/61 -- # Prepare an empty directory with an empty file mkdir -p "${TESTDIR}/bug61" touch "${TESTDIR}/bug61/bug61.txt" nginx_start 'fancyindex_exact_size off;' content=$(fetch /bug61/) test -n "${content}" || fail "Empty response" echo "Response:" echo "${content}" nginx_is_running || fail "Nginx died" ngx-fancyindex-0.5.2/t/bug95-square-brackets.test000066400000000000000000000011421413657470700216450ustar00rootroot00000000000000#! /bin/bash cat <<--- Bug #95: FancyIndex does not encode square brackets https://github.com/aperezdc/ngx-fancyindex/issues/95 -- use pup # Prepare a directory with a file that contains square brackets in the name. mkdir -p "${TESTDIR}/bug95" touch "${TESTDIR}"/bug95/'bug[95].txt' nginx_start content=$(fetch /bug95/) test -n "${content}" || fail 'Empty response' expected_href='bug%5B95%5D.txt' obtained_href=$(pup -p body tbody 'tr:nth-child(2)' a 'attr{href}' <<< "${content}") test "${expected_href}" = "${obtained_href}" || \ fail 'Expected: %s - Obtained: %s' "${expected_href}" "${obtained_href}" ngx-fancyindex-0.5.2/t/build-and-run000077500000000000000000000007311413657470700173110ustar00rootroot00000000000000#! /bin/bash set -e if [[ $# -lt 1 || $# -gt 2 ]] ; then echo "Usage: $0 [1]" 1>&2 exit 1 fi readonly NGINX=$1 if [[ $2 -eq 1 ]] ; then readonly DYNAMIC=$2 fi cd "$(dirname "$0")/.." wget -O - http://nginx.org/download/nginx-${NGINX}.tar.gz | tar -xzf - rm -rf prefix/ cd nginx-${NGINX} ./configure \ --add-${DYNAMIC:+dynamic-}module=.. \ --with-http_addition_module \ --prefix="$(pwd)/../prefix" make install cd .. exec ./t/run prefix ${DYNAMIC} ngx-fancyindex-0.5.2/t/child-directory/000077500000000000000000000000001413657470700200065ustar00rootroot00000000000000ngx-fancyindex-0.5.2/t/child-directory/empty-file.txt000066400000000000000000000000001413657470700226100ustar00rootroot00000000000000ngx-fancyindex-0.5.2/t/get-pup000077500000000000000000000072761413657470700162440ustar00rootroot00000000000000#! /bin/bash set -e declare -r VERSION='0.4.0' declare -r SHASUMS='\ 75c27caa0008a9cc639beb7506077ad9f32facbffcc4e815e999eaf9588a527e pup_v0.4.0_darwin_386.zip c539a697efee2f8e56614a54cb3b215338e00de1f6a7c2fa93144ab6e1db8ebe pup_v0.4.0_darwin_amd64.zip 259eee82c7d7d766f1b8f93a382be21dcfefebc855a9ce8124fd78717f9df439 pup_v0.4.0_dragonfly_amd64.zip ba0fe5e87a24cab818e5d2efdd7540714ddfb1b7246600135915c666fdf1a601 pup_v0.4.0_freebsd_386.zip 1838ef84ec1f961e8009d19a4d1e6a23b926ee315da3d60c08878f3d69af5692 pup_v0.4.0_freebsd_amd64.zip 6886a9c60a912a810d012610bc3f784f0417999ff7d7df833a0695b9af60395b pup_v0.4.0_freebsd_arm.zip e486b32ca07552cd3aa713cbf2f9d1b6e210ddb51d34b3090c7643f465828057 pup_v0.4.0_linux_386.zip ec3d29e9fb375b87ac492c8b546ad6be84b0c0b49dab7ff4c6b582eac71ba01c pup_v0.4.0_linux_amd64.zip c09b669fa8240f4f869dee7d34ee3c7ea620a0280cee1ea7d559593bcdd062c9 pup_v0.4.0_linux_arm64.zip ebf70b3c76c02e0202c94af7ef06dcb3ecc866d1b9b84453d43fe01fa5dd5870 pup_v0.4.0_linux_arm.zip a98a4d1f3c3a103e8ebe1a7aba9cb9d3cb045003208ca6f5f3d54889a225f267 pup_v0.4.0_linux_mips64le.zip 8e471cf6cfa118b2497bb3f42a7a48c52d0096107f748f37216855c8ab94f8e5 pup_v0.4.0_linux_mips64.zip cfda9375eba65f710e052b1b59893c228c3fc92b0510756bb3f02c25938eee30 pup_v0.4.0_linux_ppc64le.zip 91a1e07ffb2c373d6053252e4de732f5db78c8eace49c6e1a0ef52402ecdf56c pup_v0.4.0_linux_ppc64.zip fdc9b28a3daac5ad096023e1647292a7eccea6d9b1686f871307dae9f3bd064f pup_v0.4.0_nacl_386.zip c8d3c9b56783bd5a55446f4580e1835606b2b945da2d1417ed509c5927a5f8bc pup_v0.4.0_nacl_amd64p32.zip 48c068c4353672528c8c3447a536208b0719f1e6d0f8fab8416b38b63ad0c1d9 pup_v0.4.0_nacl_arm.zip 7a27497b2f0be95c51bb2cbc25da12efba682c4f766bc5abc5742e9fc8d1eeb0 pup_v0.4.0_netbsd_386.zip 71a1808eb1b6442aa45d1de9e1c4fca543b2754c1aff5ba3d62b3456f9519691 pup_v0.4.0_netbsd_amd64.zip 928e6691b11c68ae3f28826848a13dc5c1c9673848fe7cf7f80dd76c9fb6e8a6 pup_v0.4.0_netbsd_arm.zip 5aca20a9b3264d2fde5a8d32f213c434edf9570ee6fae18953b8fff09d2976e2 pup_v0.4.0_openbsd_386.zip e965c6f04b897240d84c60e2c18226deb231a657c5583680f58a61051ff5a100 pup_v0.4.0_openbsd_amd64.zip 30bc88a1e06606f4f3449af9fbf586f97c2e958677460a72bb1a168f67c4911c pup_v0.4.0_openbsd_arm.zip 9d50decf4572292f187cfec84660648d648336bc6109e1f032b1699ba1d28549 pup_v0.4.0_plan9_386.zip 1b2a6bd2388ddd691ca429497d88b2b047ec8dfb7bce9436925cb2f30632bf8e pup_v0.4.0_plan9_amd64.zip 0835de9c10a9e2b3b958b82d148da49eaafc695fe4a018cbaf7bb861b455583f pup_v0.4.0_solaris_amd64.zip 01acae220b69fb1ba8477d0e7f4d7669ef5de147966dc819cf75a845af74c5f3 pup_v0.4.0_windows_386.zip 6755cbd43e94eaf173689e93e914c7056a2249c2977e5b90024fb397f9b45ba4 pup_v0.4.0_windows_amd64.zip ' declare -r BASEURL="https://github.com/ericchiang/pup/releases/download/v${VERSION}" declare -r TDIR=$(dirname "$0") ARCH='' OS='' case $(uname -m) in x86_64 | amd64 ) ARCH=amd64 ;; i[3456]86 ) ARCH=386 ;; esac OS=$(uname -s | tr 'A-Z' 'a-z') case ${OS} in linux | freebsd | openbsd | netbsd | darwin ) ;; * ) OS='' esac if [[ -z ${ARCH} || -z ${OS} ]] ; then echo "pup ${VERSION} is not available for $(uname -s) on $(uname -m)" 1>&2 exit 1 fi declare -r ZIPFILE="pup_v${VERSION}_${OS}_${ARCH}.zip" EXPECT_SHA='' while read sum fname ; do if [[ ${fname} = ${ZIPFILE} ]] ; then EXPECT_SHA=${sum} break fi done <<< "${SHASUMS}" wget -cO "${TDIR}/${ZIPFILE}" "${BASEURL}/${ZIPFILE}" read -r GOT_SHA _ < <( sha256sum "${TDIR}/${ZIPFILE}" ) if [[ ${EXPECT_SHA} = ${GOT_SHA} ]] ; then echo "Checksum for ${ZIPFILE} verified :-)" else rm -f "${TDIR}/${ZIPFILE}" "${TDIR}/pup" echo "Checksum for ${ZIPFILE} does not match :-(" echo " Expected: ${EXPECT_SHA}" echo " Got: ${GOT_SHA}" exit 2 fi 1>&2 rm -f "${TDIR}/pup" unzip "${TDIR}/${ZIPFILE}" pup -d "${TDIR}" ngx-fancyindex-0.5.2/t/has-index.test000066400000000000000000000003671413657470700175100ustar00rootroot00000000000000#! /bin/bash cat <<--- This test ensures that the "index.html" is returned instead of a directory listing when fetching a directory which contains an index file. -- nginx_start diff -u "${TESTDIR}/has-index/index.html" <( fetch /has-index/ ) 1>&2 ngx-fancyindex-0.5.2/t/has-index/000077500000000000000000000000001413657470700166015ustar00rootroot00000000000000ngx-fancyindex-0.5.2/t/has-index/index.html000066400000000000000000000002461413657470700206000ustar00rootroot00000000000000 Index file test This is index.html. ngx-fancyindex-0.5.2/t/nginx.conf000066400000000000000000000007471413657470700167230ustar00rootroot00000000000000worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 80; server_name localhost; location / { root html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } } ngx-fancyindex-0.5.2/t/preamble000066400000000000000000000041641413657470700164400ustar00rootroot00000000000000#! /bin/bash # # preamble # Copyright (C) 2016 Adrian Perez # # Distributed under terms of the MIT license. # function nginx_conf_generate () { if ${DYNAMIC} ; then echo 'load_module modules/ngx_http_fancyindex_module.so;' fi cat <<-EOF worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { server_name localhost; listen 127.0.0.1:${NGINX_PORT}; root ${TESTDIR}; error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } location / { index index.html; fancyindex on; $* } } } EOF } readonly NGINX_CONF="${PREFIX}/conf/nginx.conf" readonly NGINX_PID="${PREFIX}/logs/nginx.pid" NGINX_PORT=$(ss -4Htnl | awk '{ sub("[^:]+:", "", $4) ; seen[$4]=1 } END { p=1025 ; while (seen[p]) p++; print p}') readonly NGINX_PORT rm -f "${NGINX_CONF}" "${NGINX_PID}" mkdir -p "${PREFIX}/logs" function pup () { if [[ -x ${TESTDIR}/pup ]] ; then "${TESTDIR}/pup" "$@" else skip 'Test uses "pup", which is not available' fi } function use () { case $1 in pup ) [[ -x ${TESTDIR}/pup ]] \ || skip 'Test uses "pup", which is unavailable\n' ;; * ) warn "Invalid 'use' flag: '%s'\n'" "$1" ;; esac } function nginx () { env - PATH="${PATH}" "${PREFIX}/sbin/nginx" "$@" } function nginx_conf () { nginx_conf_generate "$@" > "${NGINX_CONF}" } function nginx_is_running () { [[ -r ${NGINX_PID} ]] && kill -0 $(< "${NGINX_PID}") } function nginx_stop () { if nginx_is_running ; then nginx -s stop ; fi rm -f "${NGINX_PID}" } trap nginx_stop EXIT function nginx_start () { if [[ $# -gt 0 || ! -r ${NGINX_CONF} ]] ; then nginx_conf "$@" ; fi nginx_stop # Ensure that it is not running. nginx } function fetch () { local -a opts=( -q ) if [[ $1 = --with-headers ]] ; then opts+=( -S ) shift fi wget "${opts[@]}" -O- "http://localhost:${NGINX_PORT}${1:-/}" 2>&1 } function skip () { printf '(--) ' printf "$@" exit 111 } 1>&2 function fail () { printf '(FF) ' printf "$@" exit 1 } 1>&2 function warn () { printf '(WW) ' printf "$@" } 1>&2 ngx-fancyindex-0.5.2/t/run000077500000000000000000000034701413657470700154570ustar00rootroot00000000000000#!/bin/bash set -e if [[ $# -lt 1 || $# -gt 2 ]] ; then echo "Usage: $0 [1]" 1>&2 exit 1 fi # Obtain the absolute path to the tests directory pushd "$(dirname "$0")" &> /dev/null readonly T=$(pwd) popd &> /dev/null export T # Same for the nginx prefix directory pushd "$1" &> /dev/null readonly prefix=$(pwd) popd &> /dev/null dynamic=false if [[ $# -gt 1 && $2 -eq 1 ]] ; then dynamic=true fi readonly dynamic declare -a t_pass=( ) declare -a t_fail=( ) declare -a t_skip=( ) for t in `ls "$T"/*.test | sort -R` ; do name="t/${t##*/}" name=${name%.test} printf "${name} ... " errfile="${name}.err" outfile="${name}.out" shfile="${name}.sh" cat > "${shfile}" <<-EOF readonly DYNAMIC=${dynamic} readonly TESTDIR='$T' readonly PREFIX='${prefix}' $(< "$T/preamble") $(< "$t") EOF if bash -e "${shfile}" > "${outfile}" 2> "${errfile}" ; then t_pass+=( "${name}" ) printf 'passed\n' elif [[ $? -eq 111 ]] ; then t_skip+=( "${name}" ) printf 'skipped\n' else t_fail+=( "${name}" ) printf 'failed\n' fi done for name in "${t_fail[@]}" ; do echo printf '=== %s.out\n' "${name}" cat "${name}.out" echo printf '=== %s.err\n' "${name}" cat "${name}.err" echo done if [[ ${#t_skip[@]} -gt 0 ]] ; then echo printf 'Skipped tests:\n' for name in "${t_skip[@]}" ; do reason=$(grep '^(\-\-) ' "${name}.err" | head -1) if [[ -z ${reason} ]] ; then reason='No reason given' else reason=${reason:5} fi printf ' - %s: %s\n' "${name}" "${reason:-No reason given}" done echo fi printf '=== passed/skipped/failed/total: %d/%d/%d/%d\n' \ ${#t_pass[@]} ${#t_skip[@]} ${#t_fail[@]} $(( ${#t_pass[@]} + ${#t_fail[@]} )) if [[ ${#t_fail[@]} -gt 0 ]] ; then exit 1 fi ngx-fancyindex-0.5.2/t/show_dotfiles/000077500000000000000000000000001413657470700175725ustar00rootroot00000000000000ngx-fancyindex-0.5.2/t/show_dotfiles/.okay000066400000000000000000000000001413657470700205240ustar00rootroot00000000000000ngx-fancyindex-0.5.2/template.awk000077500000000000000000000016541413657470700170060ustar00rootroot00000000000000#! /usr/bin/awk -f # # Copyright © Adrian Perez # # Converts an HTML template into a C header suitable for inclusion. # Take a look at the HACKING.rst file to know how to use it :-) # # This code is placed in the public domain. BEGIN { varname = 0; print "/* Automagically generated, do not edit! */" vars_count = 0; } /^$/ { if (varname) print ";"; if ($3 == "NONE") { varname = 0; next; } varname = $3; vars[vars_count++] = varname; print "static const u_char " varname "[] = \"\""; next; } /^$/ { if (!varname) next; print "\"\\n\""; next; } { if (!varname) next; # Order matters gsub(/[\t\v\n\r\f]+/, ""); gsub(/\\/, "\\\\"); gsub(/"/, "\\\""); print "\"" $0 "\"" } END { if (varname) print ";"; print "#define NFI_TEMPLATE_SIZE (0 \\"; for (var in vars) { print "\t+ nfi_sizeof_ssz(" vars[var] ") \\"; } print "\t)" } ngx-fancyindex-0.5.2/template.h000066400000000000000000000040701413657470700164430ustar00rootroot00000000000000/* Automagically generated, do not edit! */ static const u_char t01_head1[] = "" "" "" "" "" "" "" "\n" ; static const u_char t02_head2[] = "" "\n" "Index of " ; static const u_char t03_head3[] = "" "" "\n" "" ; static const u_char t04_body1[] = "" "" "

Index of " ; static const u_char t05_body2[] = "" "

" "\n" ; static const u_char t06_list1[] = "" "
last = ngx_cpymem(b->last, sort_url_args, ngx_sizeof_ssz("?C=N&O=A")); } b->last = ngx_cpymem_ssz(b->last, "\">Parent directory/--
last, entry[i].name.data, entry[i].name.len); b->last += entry[i].name.len + entry[i].escape; } else { b->last = ngx_cpymem_str(b->last, entry[i].name); } if (entry[i].dir) { *b->last++ = '/'; if (*sort_url_args) { b->last = ngx_cpymem(b->last, sort_url_args, ngx_sizeof_ssz("?C=x&O=y")); } } *b->last++ = '"'; b->last = ngx_cpymem_ssz(b->last, " title=\""); b->last = (u_char *) ngx_escape_html(b->last, entry[i].name.data, entry[i].name.len); *b->last++ = '"'; *b->last++ = '>'; len = entry[i].utf_len; if (entry[i].name.len != len) { if (len > alcf->name_length) { copy = alcf->name_length - 3 + 1; } else { copy = alcf->name_length + 1; } last = b->last; b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data, copy, entry[i].name.len); b->last = (u_char *) ngx_escape_html(last, entry[i].name.data, b->last - last); last = b->last; } else { if (len > alcf->name_length) { b->last = (u_char *) ngx_escape_html(b->last, entry[i].name.data, alcf->name_length + 1); } else { b->last = (u_char *) ngx_escape_html(b->last, entry[i].name.data, entry[i].name.len); } last = b->last - 3; } if (len > alcf->name_length) { b->last = ngx_cpymem_ssz(last, "..>"); } else { if (entry[i].dir && alcf->name_length - len > 0) { *b->last++ = '/'; len++; } b->last = ngx_cpymem_ssz(b->last, ""); } if (alcf->exact_size) { if (entry[i].dir) { *b->last++ = '-'; } else { b->last = ngx_sprintf(b->last, "%19O", entry[i].size); } } else { if (entry[i].dir) { *b->last++ = '-'; } else { length = entry[i].size; multiplier = exbibyte; for (j = 0; j < DIM(sizes) - 1 && length < multiplier; j++) multiplier /= 1024; /* If we are showing the filesize in bytes, do not show a decimal */ if (j == DIM(sizes) - 1) b->last = ngx_sprintf(b->last, "%O %s", length, sizes[j]); else b->last = ngx_sprintf(b->last, "%.1f %s", (float) length / multiplier, sizes[j]); } } ngx_gmtime(entry[i].mtime + tp->gmtoff * 60 * alcf->localtime, &tm); b->last = ngx_cpymem_ssz(b->last, ""); b->last = ngx_fancyindex_timefmt(b->last, &alcf->time_format, &tm); b->last = ngx_cpymem_ssz(b->last, "
" "" "" "" "" "" "" "" "\n" "" ; static const u_char t_parentdir_entry[] = "" "" "" "" "" "" "\n" ; static const u_char t07_list2[] = "" "" "
File Name  ↓ File Size  ↓ Date  ↓ 
Parent directory/--
" ; static const u_char t08_foot1[] = "" "" "" ; #define NFI_TEMPLATE_SIZE (0 \ + nfi_sizeof_ssz(t01_head1) \ + nfi_sizeof_ssz(t02_head2) \ + nfi_sizeof_ssz(t03_head3) \ + nfi_sizeof_ssz(t04_body1) \ + nfi_sizeof_ssz(t05_body2) \ + nfi_sizeof_ssz(t06_list1) \ + nfi_sizeof_ssz(t_parentdir_entry) \ + nfi_sizeof_ssz(t07_list2) \ + nfi_sizeof_ssz(t08_foot1) \ ) ngx-fancyindex-0.5.2/template.html000066400000000000000000000036201413657470700171600ustar00rootroot00000000000000 Index of <!-- var NONE --> /path/to/somewhere <!-- var t03_head3 -->

Index of /path/to/somewhere

File Name  ↓  File Size  ↓  Date  ↓ 
- -
test file 1 123kB date
test file 2 321MB date
test file 3 666 date