diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 62c8827..374487e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,9 +11,9 @@ jobs: compiler: [gcc, clang] nginx: # Mainline - - 1.21.6 + - 1.23.3 # Stable. - - 1.20.2 + - 1.22.1 # First version with loadable module support. - 1.9.15 # Oldest supported version. diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000..66cc1f9 --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1,13 @@ +-I/home/thetadev/test/ngx-fancyindex/nginx-1.25.4/src/core +-I/home/thetadev/test/ngx-fancyindex/nginx-1.25.4/src/event +-I/home/thetadev/test/ngx-fancyindex/nginx-1.25.4/src/event/quic +-I/home/thetadev/test/ngx-fancyindex/nginx-1.25.4/src/event/modules +-I/home/thetadev/test/ngx-fancyindex/nginx-1.25.4/src/http +-I/home/thetadev/test/ngx-fancyindex/nginx-1.25.4/src/http/v2 +-I/home/thetadev/test/ngx-fancyindex/nginx-1.25.4/src/http/v3 +-I/home/thetadev/test/ngx-fancyindex/nginx-1.25.4/src/http/modules +-I/home/thetadev/test/ngx-fancyindex/nginx-1.25.4/src/mail +-I/home/thetadev/test/ngx-fancyindex/nginx-1.25.4/src/misc +-I/home/thetadev/test/ngx-fancyindex/nginx-1.25.4/src/os/unix +-I/home/thetadev/test/ngx-fancyindex/nginx-1.25.4/src/stream +-I/home/thetadev/test/ngx-fancyindex/nginx-1.25.4/objs diff --git a/config b/config index 4ef3809..990f1c0 100644 --- a/config +++ b/config @@ -1,5 +1,6 @@ # vim:ft=sh: ngx_addon_name=ngx_http_fancyindex_module +ngx_module_libs="-lmarkdown" if [ "$ngx_module_link" = DYNAMIC ] ; then ngx_module_type=HTTP diff --git a/ngx_http_fancyindex_module.c b/ngx_http_fancyindex_module.c index d523656..50f625e 100644 --- a/ngx_http_fancyindex_module.c +++ b/ngx_http_fancyindex_module.c @@ -18,11 +18,13 @@ * * SPDX-License-Identifier: BSD-2-Clause */ - #include #include #include #include +#include "ngx_string.h" + +#include #include "template.h" @@ -48,6 +50,8 @@ static const char *long_month[] = { "August", "September", "October", "November", "December", }; +#define MKD_FLAGS MKD_TOC | MKD_IDANCHOR | MKD_AUTOLINK + #define DATETIME_FORMATS(F_, t) \ F_ ('a', 3, "%3s", short_weekday[((t)->ngx_tm_wday + 6) % 7]) \ @@ -168,6 +172,9 @@ typedef struct { ngx_fancyindex_headerfooter_conf_t header; ngx_fancyindex_headerfooter_conf_t footer; + + ngx_str_t title; + ngx_str_t icon_href; } ngx_http_fancyindex_loc_conf_t; #define NGX_HTTP_FANCYINDEX_SORT_CRITERION_NAME 0 @@ -330,6 +337,7 @@ typedef struct { ngx_uint_t escape; ngx_uint_t escape_html; ngx_uint_t dir; + ngx_uint_t link; time_t mtime; off_t size; } ngx_http_fancyindex_entry_t; @@ -353,6 +361,8 @@ static int ngx_libc_cdecl static int ngx_libc_cdecl ngx_http_fancyindex_cmp_entries_mtime_asc(const void *one, const void *two); +static size_t ngx_http_fancyindex_num_places (size_t n); + static ngx_int_t ngx_http_fancyindex_error(ngx_http_request_t *r, ngx_dir_t *dir, ngx_str_t *name); @@ -376,7 +386,7 @@ static uintptr_t * above). */ static ngx_inline ngx_buf_t* - make_header_buf(ngx_http_request_t *r, const ngx_str_t css_href) + make_header_buf(ngx_http_request_t *r, ngx_http_fancyindex_loc_conf_t *alcf) ngx_force_inline; @@ -487,6 +497,20 @@ static ngx_command_t ngx_http_fancyindex_commands[] = { offsetof(ngx_http_fancyindex_loc_conf_t, time_format), NULL }, + { ngx_string("fancyindex_title"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_fancyindex_loc_conf_t, title), + NULL }, + + { ngx_string("fancyindex_icon_href"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_fancyindex_loc_conf_t, icon_href), + NULL }, + ngx_null_command }; @@ -527,6 +551,18 @@ static const ngx_str_t css_href_pre = ngx_string("\n"); +static const ngx_str_t favicon_pre = + ngx_string("\n"); +static const ngx_str_t img_icon_pre = + ngx_string("\"\""); +static const ngx_str_t footer_pre = + ngx_string("
"); +static const ngx_str_t footer_post = + ngx_string("
"); #ifdef NGX_ESCAPE_URI_COMPONENT @@ -625,21 +661,29 @@ ngx_fancyindex_escape_filename(u_char *dst, u_char *src, size_t size) static ngx_inline ngx_buf_t* -make_header_buf(ngx_http_request_t *r, const ngx_str_t css_href) +make_header_buf(ngx_http_request_t *r, ngx_http_fancyindex_loc_conf_t *alcf) { ngx_buf_t *b; - size_t blen = r->uri.len - + ngx_sizeof_ssz(t01_head1) + size_t blen = ngx_sizeof_ssz(t01_head1) + ngx_sizeof_ssz(t02_head2) + + r->uri.len + ngx_sizeof_ssz(t03_head3) - + ngx_sizeof_ssz(t04_body1) - ; + + ngx_sizeof_ssz(t04_body1); + + if (alcf->title.len) { + blen += alcf->title.len + 1; + } - if (css_href.len) { + if (alcf->icon_href.len) { + blen += favicon_pre.len + + alcf->icon_href.len + + favicon_post.len; + } + + if (alcf->css_href.len) { blen += css_href_pre.len \ - + css_href.len \ - + css_href_post.len - ; + + alcf->css_href.len \ + + css_href_post.len; } if ((b = ngx_create_temp_buf(r->pool, blen)) == NULL) @@ -647,13 +691,25 @@ make_header_buf(ngx_http_request_t *r, const ngx_str_t css_href) b->last = ngx_cpymem_ssz(b->last, t01_head1); - if (css_href.len) { + if (alcf->icon_href.len) { + b->last = ngx_cpymem_str(b->last, favicon_pre); + b->last = ngx_cpymem_str(b->last, alcf->icon_href); + b->last = ngx_cpymem_str(b->last, favicon_post); + } + + if (alcf->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, alcf->css_href); b->last = ngx_cpymem_str(b->last, css_href_post); } b->last = ngx_cpymem_ssz(b->last, t02_head2); + + if (alcf->title.len) { + b->last = ngx_cpymem_str(b->last, alcf->title); + *b->last++ = ' '; + } + 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); @@ -673,7 +729,7 @@ make_content_buf( const char *sort_url_args = ""; off_t length; - size_t len, root, allocated, escape_html; + size_t len, root, allocated, n_files=0, n_dirs=0; int64_t multiplier; u_char *filename, *last; ngx_tm_t tm; @@ -683,6 +739,9 @@ make_content_buf( ngx_str_t path; ngx_dir_t dir; ngx_buf_t *b; + u_char *readme_path = NULL; + u_char readme_md = 0; + off_t readme_file_len = 0; static const char *sizes[] = { "EiB", "PiB", "TiB", "GiB", "MiB", "KiB", "B" }; static const int64_t exbibyte = 1024LL * 1024LL * 1024LL * @@ -702,6 +761,8 @@ make_content_buf( } path.data[path.len] = '\0'; + r->headers_out.last_modified_time = 0; + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http fancyindex: \"%s\"", path.data); @@ -845,12 +906,32 @@ make_content_buf( entry->name.len); entry->dir = ngx_de_is_dir(&dir); + entry->link = ngx_de_is_link(&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 (entry->dir) n_dirs += 1; + else n_files += 1; + + if (ngx_strncasecmp(entry->name.data, (u_char*) "README", 6) == 0 && (entry->name.len == 6 || entry->name.data[6] == '.')) { + if (!readme_path || ngx_strcmp(readme_path, filename) > 0) { + // readme_path = filename; + if ((readme_path = ngx_palloc(r->pool, allocated)) == NULL) + return ngx_http_fancyindex_error(r, &dir, &path); + ngx_cpystrn(readme_path, filename, allocated); + + readme_file_len = entry->size; + readme_md = entry->name.len > 6 && ngx_strncasecmp(entry->name.data + 6, (u_char*) ".md", 3) == 0; + } + } + + if (entry->mtime > r->headers_out.last_modified_time) { + r->headers_out.last_modified_time = entry->mtime; + } } if (ngx_close_dir(&dir) == NGX_ERROR) { @@ -858,33 +939,55 @@ make_content_buf( ngx_close_dir_n " \"%s\" failed", &path); } + if (readme_path) { + ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "HTTP fancyindex README: %s, MD%d, len: %d", readme_path, readme_md, readme_file_len); + } + /* * Calculate needed buffer length. */ - escape_html = ngx_escape_html(NULL, r->uri.data, r->uri.len); + len = ngx_sizeof_ssz(t06_list1) + + ngx_sizeof_ssz(t06_list2) + + ngx_sizeof_ssz(t06_list3) + + ngx_sizeof_ssz(t_parentdir_entry) + + ngx_sizeof_ssz(t07_list2) + + ngx_fancyindex_timefmt_calc_size (&alcf->time_format) * entries.nelts + + ngx_http_fancyindex_num_places(n_dirs) + + ngx_http_fancyindex_num_places(n_files); - 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 (alcf->show_path) { + len += ngx_sizeof_ssz(t05_body2) + + ngx_sizeof_ssz("

/") + + ngx_sizeof_ssz("

") + + ngx_sizeof_ssz(t05_body3); + + if (alcf->icon_href.len) { + len += img_icon_pre.len + + alcf->icon_href.len + + img_icon_post.len; + } + + ngx_uint_t last_slash = 0; + for (i = 1; i < r->uri.len; i++) { + u_char c = r->uri.data[i]; + if (c == '/') { + len += ngx_sizeof_ssz("") + + ngx_sizeof_ssz("/") + + ngx_escape_uri(NULL, r->uri.data, i, NGX_ESCAPE_URI) + + ngx_escape_html(NULL, r->uri.data + last_slash + 1, i - last_slash - 1); + last_slash = i; + } + } + } /* * 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) { + if (r->uri.len == 1 || alcf->hide_parent) { len -= ngx_sizeof_ssz(t_parentdir_entry); } @@ -899,19 +1002,34 @@ make_content_buf( * sizedate * */ - len += ngx_sizeof_ssz("") + entry[i].name.len + entry[i].utf_len + entry[i].escape_html - + ngx_sizeof_ssz("\">") - + entry[i].name.len + entry[i].utf_len + entry[i].escape_html - + ngx_sizeof_ssz("") + + ngx_sizeof_ssz("") + 20 /* File size */ - + ngx_sizeof_ssz("") /* Date prefix */ + + ngx_sizeof_ssz("") /* Date prefix */ + ngx_sizeof_ssz("\n") /* Date suffix */ + 2 /* CR LF */ ; + if (entry[i].dir) len += ngx_sizeof_ssz("folder"); + else len += ngx_sizeof_ssz("file"); + if (entry[i].link) len += ngx_sizeof_ssz("-shortcut"); + } + + char *readme_md_content; + if (readme_file_len) { + if (readme_md) { + FILE *md_file = fopen((char *)readme_path, "r"); + MMIOT *mkd = mkd_in(md_file, MKD_FLAGS); + mkd_compile(mkd, MKD_FLAGS); + readme_file_len = mkd_document(mkd, &readme_md_content); + fclose(md_file); + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "converted %s, len=%d", readme_path, readme_file_len); + } + len += footer_pre.len + readme_file_len + footer_post.len; } if ((b = ngx_create_temp_buf(r->pool, len)) == NULL) @@ -1046,13 +1164,41 @@ make_content_buf( } /* 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); + if (alcf->show_path) { b->last = ngx_cpymem_ssz(b->last, t05_body2); + + if (alcf->icon_href.len) { + b->last = ngx_cpymem_str(b->last, img_icon_pre); + b->last = ngx_cpymem_str(b->last, alcf->icon_href); + b->last = ngx_cpymem_str(b->last, img_icon_post); + } + + b->last = ngx_cpymem_ssz(b->last, "

/"); + + ngx_uint_t last_slash = 0; + for (i = 1; i < r->uri.len; i++) { + u_char c = r->uri.data[i]; + if (c == '/') { + b->last = ngx_cpymem_ssz(b->last, "last = (u_char *) ngx_escape_uri(b->last, r->uri.data, i, NGX_ESCAPE_URI); + b->last = ngx_cpymem_ssz(b->last, "\">"); + b->last = (u_char *) ngx_escape_html(b->last, r->uri.data + last_slash + 1, i - last_slash - 1); + b->last = ngx_cpymem_ssz(b->last, "/"); + + last_slash = i; + } + } + + b->last = ngx_cpymem_ssz(b->last, "

"); + b->last = ngx_cpymem_ssz(b->last, t05_body3); } /* Open the tag */ b->last = ngx_cpymem_ssz(b->last, t06_list1); + b->last = ngx_sprintf(b->last, "%d", n_dirs); + b->last = ngx_cpymem_ssz(b->last, t06_list2); + b->last = ngx_sprintf(b->last, "%d", n_files); + b->last = ngx_cpymem_ssz(b->last, t06_list3); tp = ngx_timeofday(); @@ -1060,23 +1206,23 @@ make_content_buf( if (r->uri.len > 1 && alcf->hide_parent == 0) { b->last = ngx_cpymem_ssz(b->last, "" - "" - "" - "" + "\">Parent directory" + "" + "" "" CRLF); } /* Entries for directories and files */ for (i = 0; i < entries.nelts; i++) { - b->last = ngx_cpymem_ssz(b->last, ""); @@ -1154,6 +1297,44 @@ make_content_buf( /* Output table bottom */ b->last = ngx_cpymem_ssz(b->last, t07_list2); + // Readme file + if (readme_file_len) { + b->last = ngx_cpymem_str(b->last, footer_pre); + + if (readme_md) { + b->last = ngx_cpymem(b->last, readme_md_content, readme_file_len); + } else { + ngx_file_t file; + ngx_memzero(&file, sizeof(ngx_file_t)); + file.fd = ngx_open_file(readme_path, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + if (file.fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, + "cannot open readme file \"%V\"", readme_path); + return NGX_ERROR; + } + file.log = r->connection->log; + ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "opened file %d", file); + + ssize_t n = readme_file_len; + while (n > 0) { + ssize_t rbts = ngx_read_file(&file, + b->last + file.offset, + n, + file.offset); + if (rbts == NGX_ERROR) { + ngx_close_file(file.fd); + ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, + "cannot read readme file \"%V\"", readme_path); + return NGX_ERROR; + } + b->last += rbts; + n -= rbts; + } + } + + b->last = ngx_cpymem_str(b->last, footer_post); + } + *pb = b; return NGX_OK; } @@ -1264,7 +1445,7 @@ add_builtin_header: 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); + out[0].buf = make_header_buf(r, alcf); } } @@ -1282,8 +1463,8 @@ add_builtin_header: 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].buf->pos = (u_char*) t08_foot; + out[last].buf->last = (u_char*) t08_foot + sizeof(t08_foot) - 1; } out[last-1].buf->last_in_chain = 0; @@ -1344,8 +1525,8 @@ add_builtin_header: 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->pos = (u_char*) t08_foot; + out[0].buf->last = (u_char*) t08_foot + sizeof(t08_foot) - 1; out[0].buf->last_in_chain = 1; out[0].buf->last_buf = 1; /* Directly send out the builtin footer */ @@ -1447,6 +1628,15 @@ ngx_http_fancyindex_error(ngx_http_request_t *r, ngx_dir_t *dir, ngx_str_t *name return NGX_HTTP_INTERNAL_SERVER_ERROR; } +static size_t ngx_http_fancyindex_num_places (size_t n) { + int r = 1; + while (n > 9) { + n /= 10; + r++; + } + return r; +} + static void * ngx_http_fancyindex_create_loc_conf(ngx_conf_t *cf) @@ -1508,7 +1698,7 @@ ngx_http_fancyindex_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 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_str_value(conf->time_format, prev->time_format, "%F %H:%M"); ngx_conf_merge_ptr_value(conf->ignore, prev->ignore, NULL); ngx_conf_merge_value(conf->hide_symlinks, prev->hide_symlinks, 0); diff --git a/t/README.md b/t/README.md new file mode 100644 index 0000000..d54b2fb --- /dev/null +++ b/t/README.md @@ -0,0 +1,309 @@ +# Markdown: Syntax + +* [Overview](#Overview) + * [Philosophy](#Philosophy) +* [Block Elements](#Block-Elements) + * [Paragraphs and Line Breaks](#Paragraphs-and-Line-Breaks) + * [Headers](#Headers) + * [Blockquotes](#Blockquotes) + * [Lists](#Lists) + * [Code Blocks](#Code-Blocks) + * [Horizontal Rules](#Horizontal-Rules) +* [Span Elements](#Span-Elements) + * [Links](#Links) + * [Emphasis](#Emphasis) + * [Code](#Code) + * [Table](#Table) + +---- + +## Overview + +### Philosophy + +Markdown is intended to be as easy-to-read and easy-to-write as is feasible. + +Readability, however, is emphasized above all else. A Markdown-formatted +document should be publishable as-is, as plain text, without looking +like it's been marked up with tags or formatting instructions. While +Markdown's syntax has been influenced by several existing text-to-HTML +filters -- including [Setext](http://docutils.sourceforge.net/mirror/setext.html), [atx](http://www.aaronsw.com/2002/atx/), [Textile](http://textism.com/tools/textile/), [reStructuredText](http://docutils.sourceforge.net/rst.html), +[Grutatext](http://www.triptico.com/software/grutatxt.html), and [EtText](http://ettext.taint.org/doc/) -- the single biggest source of +inspiration for Markdown's syntax is the format of plain text email. + +## Block Elements + +### Paragraphs and Line Breaks + +A paragraph is simply one or more consecutive lines of text, separated +by one or more blank lines. (A blank line is any line that looks like a +blank line -- a line containing nothing but spaces or tabs is considered +blank.) Normal paragraphs should not be indented with spaces or tabs. + +The implication of the "one or more consecutive lines of text" rule is +that Markdown supports "hard-wrapped" text paragraphs. This differs +significantly from most other text-to-HTML formatters (including Movable +Type's "Convert Line Breaks" option) which translate every line break +character in a paragraph into a `
` tag. + +When you *do* want to insert a `
` break tag using Markdown, you +end a line with two or more spaces, then type return. + +### Headers + +Markdown supports two styles of headers, [Setext] [1] and [atx] [2]. + +Optionally, you may "close" atx-style headers. This is purely +cosmetic -- you can use this if you think it looks better. The +closing hashes don't even need to match the number of hashes +used to open the header. (The number of opening hashes +determines the header level.) + + +### Blockquotes + +Markdown uses email-style `>` characters for blockquoting. If you're +familiar with quoting passages of text in an email message, then you +know how to create a blockquote in Markdown. It looks best if you hard +wrap the text and put a `>` before every line: + +> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, +> consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. +> Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. +> +> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse +> id sem consectetuer libero luctus adipiscing. + +Markdown allows you to be lazy and only put the `>` before the first +line of a hard-wrapped paragraph: + +> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, +consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. +Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. + +> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse +id sem consectetuer libero luctus adipiscing. + +Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by +adding additional levels of `>`: + +> This is the first level of quoting. +> +> > This is nested blockquote. +> +> Back to the first level. + +Blockquotes can contain other Markdown elements, including headers, lists, +and code blocks: + +> ## This is a header. +> +> 1. This is the first list item. +> 2. This is the second list item. +> +> Here's some example code: +> +> return shell_exec("echo $input | $markdown_script"); + +Any decent text editor should make email-style quoting easy. For +example, with BBEdit, you can make a selection and choose Increase +Quote Level from the Text menu. + + +### Lists + +Markdown supports ordered (numbered) and unordered (bulleted) lists. + +Unordered lists use asterisks, pluses, and hyphens -- interchangably +-- as list markers: + +* Red +* Green +* Blue + +is equivalent to: + ++ Red ++ Green ++ Blue + +and: + +- Red +- Green +- Blue + +Ordered lists use numbers followed by periods: + +1. Bird +2. McHale +3. Parish + +It's important to note that the actual numbers you use to mark the +list have no effect on the HTML output Markdown produces. The HTML +Markdown produces from the above list is: + +If you instead wrote the list in Markdown like this: + +1. Bird +1. McHale +1. Parish + +or even: + +3. Bird +1. McHale +8. Parish + +you'd get the exact same HTML output. The point is, if you want to, +you can use ordinal numbers in your ordered Markdown lists, so that +the numbers in your source match the numbers in your published HTML. +But if you want to be lazy, you don't have to. + +To make lists look nice, you can wrap items with hanging indents: + +* Lorem ipsum dolor sit amet, consectetuer adipiscing elit. + Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, + viverra nec, fringilla in, laoreet vitae, risus. +* Donec sit amet nisl. Aliquam semper ipsum sit amet velit. + Suspendisse id sem consectetuer libero luctus adipiscing. + +But if you want to be lazy, you don't have to: + +* Lorem ipsum dolor sit amet, consectetuer adipiscing elit. +Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, +viverra nec, fringilla in, laoreet vitae, risus. +* Donec sit amet nisl. Aliquam semper ipsum sit amet velit. +Suspendisse id sem consectetuer libero luctus adipiscing. + +List items may consist of multiple paragraphs. Each subsequent +paragraph in a list item must be indented by either 4 spaces +or one tab: + +1. This is a list item with two paragraphs. Lorem ipsum dolor + sit amet, consectetuer adipiscing elit. Aliquam hendrerit + mi posuere lectus. + + Vestibulum enim wisi, viverra nec, fringilla in, laoreet + vitae, risus. Donec sit amet nisl. Aliquam semper ipsum + sit amet velit. + +2. Suspendisse id sem consectetuer libero luctus adipiscing. + +It looks nice if you indent every line of the subsequent +paragraphs, but here again, Markdown will allow you to be +lazy: + +* This is a list item with two paragraphs. + + This is the second paragraph in the list item. You're +only required to indent the first line. Lorem ipsum dolor +sit amet, consectetuer adipiscing elit. + +* Another item in the same list. + +To put a blockquote within a list item, the blockquote's `>` +delimiters need to be indented: + +* A list item with a blockquote: + + > This is a blockquote + > inside a list item. + +To put a code block within a list item, the code block needs +to be indented *twice* -- 8 spaces or two tabs: + +* A list item with a code block: + + + +### Code Blocks + +Pre-formatted code blocks are used for writing about programming or +markup source code. Rather than forming normal paragraphs, the lines +of a code block are interpreted literally. Markdown wraps a code block +in both `
` and `` tags.
+
+To produce a code block in Markdown, simply indent every line of the
+block by at least 4 spaces or 1 tab.
+
+This is a normal paragraph:
+
+    This is a code block.
+
+Here is an example of AppleScript:
+
+    tell application "Foo"
+        beep
+    end tell
+
+A code block continues until it reaches a line that is not indented
+(or the end of the article).
+
+Within a code block, ampersands (`&`) and angle brackets (`<` and `>`)
+are automatically converted into HTML entities. This makes it very
+easy to include example HTML source code using Markdown -- just paste
+it and indent it, and Markdown will handle the hassle of encoding the
+ampersands and angle brackets. For example, this:
+
+    
+
+Regular Markdown syntax is not processed within code blocks. E.g.,
+asterisks are just literal asterisks within a code block. This means
+it's also easy to use Markdown to write about Markdown's own syntax.
+
+```
+tell application "Foo"
+    beep
+end tell
+```
+
+## Span Elements
+
+### Links
+
+Markdown supports two style of links: *inline* and *reference*.
+
+In both styles, the link text is delimited by [square brackets].
+
+To create an inline link, use a set of regular parentheses immediately
+after the link text's closing square bracket. Inside the parentheses,
+put the URL where you want the link to point, along with an *optional*
+title for the link, surrounded in quotes. For example:
+
+This is [an example](http://example.com/) inline link.
+
+[This link](http://example.net/) has no title attribute.
+
+### Emphasis
+
+Markdown treats asterisks (`*`) and underscores (`_`) as indicators of
+emphasis. Text wrapped with one `*` or `_` will be wrapped with an
+HTML `` tag; double `*`'s or `_`'s will be wrapped with an HTML
+`` tag. E.g., this input:
+
+*single asterisks*
+
+_single underscores_
+
+**double asterisks**
+
+__double underscores__
+
+### Code
+
+To indicate a span of code, wrap it with backtick quotes (`` ` ``).
+Unlike a pre-formatted code block, a code span indicates code within a
+normal paragraph. For example:
+
+Use the `printf()` function.
+
+### Table
+
+| Item         | Price     | # In stock |
+|--------------|-----------|------------|
+| Juicy Apples | 1.99      | *7*        |
+| Bananas      | **1.89**  | 5234       |
+| Tomatoes      | **1.89**  | 5234       |
diff --git a/t/README.txt b/t/README.txt
new file mode 100644
index 0000000..53191b8
--- /dev/null
+++ b/t/README.txt
@@ -0,0 +1,3 @@
+

Hello World

+ +Hello World, this is me ;-) diff --git a/t/ThisFilenameIsMuchLongerThan20CharsMoreWordsThisFilenameIsMuchLongerThan20CharsMoreWordsThisFilenameIsMuchLongerThan20CharsMoreWords b/t/ThisFilenameIsMuchLongerThan20CharsMoreWordsThisFilenameIsMuchLongerThan20CharsMoreWordsThisFilenameIsMuchLongerThan20CharsMoreWords new file mode 100644 index 0000000..e69de29 diff --git a/t/child-directory/c2/c3/empty-file.txt b/t/child-directory/c2/c3/empty-file.txt new file mode 100644 index 0000000..e69de29 diff --git a/template.h b/template.h index 02ba908..225cfb9 100644 --- a/template.h +++ b/template.h @@ -2,91 +2,622 @@ static const u_char t01_head1[] = "" "" "" -"" -"" -"" -"" "\n" +" @media (max-width: 600px) {" +" td:nth-child(1) {" +" width: auto;" +" }" +" th:nth-child(2)," +" td:nth-child(2) {" +" display: none;" +" }" +" h1 a {" +" margin: 0;" +" }" +" #filter {" +" max-width: 100px;" +" }" +" }" +" @media (prefers-color-scheme: dark) {" +" * {" +" --color-secondary: #082437;" +" --color-text-light: rgb(139, 157, 169);" +" }" +" body {" +" background-color: #101010;" +" color: #dddddd;" +" }" +" header {" +" background-color: #151515;" +" }" +" #list tbody tr:hover {" +" background-color: #252525;" +" }" +" header h1 a," +" #list th a {" +" color: #dddddd;" +" }" +" a {" +" color: #5796d1;" +" text-decoration: none;" +" }" +" a:hover," +" h1 a:hover {" +" color: #62b2fd;" +" }" +" #list tr {" +" border-bottom: 1px dashed rgba(255, 255, 255, 0.12);" +" }" +" #filter {" +" background-color: #151515;" +" color: #ffffff;" +" border: 1px solid #212121;" +" }" +" .meta {" +" border-bottom: 1px solid #212121;" +" }" +" footer code, footer pre {" +" background-color: rgb(8, 36, 55);" +" }" +" }" +" " ; static const u_char t02_head2[] = "" -"\n" -"Index of " +"<title>" ; 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 t05_body3[] = "" +"
" ; 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 = ngx_cpymem_ssz(b->last, "
last, @@ -1098,23 +1244,20 @@ make_content_buf( } } - *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++ = '>'; + b->last = ngx_cpymem_ssz(b->last, "\">last = ngx_cpymem_ssz(b->last, "folder"); + else b->last = ngx_cpymem_ssz(b->last, "file"); + if (entry[i].link) b->last = ngx_cpymem_ssz(b->last, "-shortcut"); + + b->last = ngx_cpymem_ssz(b->last, "\">"); len = entry[i].utf_len; b->last = (u_char *) ngx_escape_html(b->last, entry[i].name.data, entry[i].name.len); last = b->last - 3; - if (entry[i].dir) { - *b->last++ = '/'; - len++; - } - - b->last = ngx_cpymem_ssz(b->last, ""); + b->last = ngx_cpymem_ssz(b->last, ""); if (alcf->exact_size) { if (entry[i].dir) { @@ -1143,7 +1286,7 @@ make_content_buf( } ngx_gmtime(entry[i].mtime + tp->gmtoff * 60 * alcf->localtime, &tm); - b->last = ngx_cpymem_ssz(b->last, ""); + 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, "
" -"" -"" -"" -"" -"" -"" -"" +"
" +"
" +"
" +" " +; +static const u_char t06_list2[] = "" +" directories" +"" +; +static const u_char t06_list3[] = "" +" files" +" " +"
" +"
" +"
" +"
File Name  ↓ File Size  ↓ Date  ↓ 
" +" " +" " +" " +" " +" " +" " +" " "\n" -"" +" " ; static const u_char t_parentdir_entry[] = "" -"" -"" -"" -"" -"" +" " +" " +" " +" " +" " "\n" ; static const u_char t07_list2[] = "" -"" -"
" +" Size  ↓ " +" " +" Size  ↓ " +" " +" Modified  ↓ " +"
Parent directory/--
" +" " +" Parent directory" +" " +"
" +" " +" " +" " +" " +"\n" ; -static const u_char t08_foot1[] = "" -"" +static const u_char t08_foot[] = "" +" " +" " "" ; #define NFI_TEMPLATE_SIZE (0 \ @@ -95,8 +626,11 @@ static const u_char t08_foot1[] = "" + nfi_sizeof_ssz(t03_head3) \ + nfi_sizeof_ssz(t04_body1) \ + nfi_sizeof_ssz(t05_body2) \ + + nfi_sizeof_ssz(t05_body3) \ + nfi_sizeof_ssz(t06_list1) \ + + nfi_sizeof_ssz(t06_list2) \ + + nfi_sizeof_ssz(t06_list3) \ + nfi_sizeof_ssz(t_parentdir_entry) \ + nfi_sizeof_ssz(t07_list2) \ - + nfi_sizeof_ssz(t08_foot1) \ + + nfi_sizeof_ssz(t08_foot) \ ) diff --git a/template.html b/template.html index b2f521b..3436dab 100644 --- a/template.html +++ b/template.html @@ -1,101 +1,1091 @@ - - - - + + + + + + - - Index of +<title> <!-- var NONE --> - /path/to/somewhere +Index of /path/to/somewhere <!-- var t03_head3 --> - - - + + - -

Index of - - /path/to/somewhere + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -

- +
+ + +

/path/to/somewhere

+ +
- - - - - - - - +
+
+
+ + +13 + + directories + + +5 + + files + +
+
+
+
File Name  ↓ File Size  ↓ Date  ↓ 
+ + + + + + + - + - - - - - + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - -
+ Size  ↓  + + Size  ↓  + + Modified  ↓  +
--
+ + Parent directory + +
test file 1123kBdate
test file 2321MBdate
test file 3666date
+ + + test folder/ + + date
+ + + test file 1 + + date
+ + + + + test file 2 + + date
+ + + + + test file 3 + + date
- - + + + + + + +

styleguide

+ + +

Markdown style guide

+ +

Much of what makes Markdown great is the ability to write plain text, and get +great formatted output as a result. To keep the slate clean for the next author, +your Markdown should be simple and consistent with the whole corpus wherever +possible.

+ +

We seek to balance three goals:

+ +
    +
  1. Source text is readable and portable.
  2. +
  3. Markdown files are maintainable over time and across teams.
  4. +
  5. The syntax is simple and easy to remember.
  6. +
+ +

Contents:

+ +
    +
  1. Document layout
  2. +
  3. Character line limit
  4. +
  5. Trailing whitespace
  6. +
  7. Headings +
      +
    1. ATX-style headings
    2. +
    3. Add spacing to headings
    4. +
    +
  8. +
  9. Lists +
      +
    1. Use lazy numbering for long lists
    2. +
    3. Nested list spacing
    4. +
    +
  10. +
  11. Code +
      +
    1. Inline
    2. +
    3. Codeblocks
    4. +
    5. Declare the language
    6. +
    7. Escape newlines
    8. +
    9. Nest codeblocks within lists
    10. +
    +
  12. +
  13. Links +
      +
    1. Use informative Markdown link titles
    2. +
    +
  14. +
  15. Images
  16. +
  17. Prefer lists to tables
  18. +
  19. Strongly prefer Markdown to HTML
  20. +
+ +

Document layout

+ +

In general, most documents benefit from some variation of the following layout:

+ +
# Document Title
+
+Short introduction.
+
+[TOC]
+
+## Topic
+
+Content.
+
+## See also
+
+* https://link-to-more-info
+
+ +
    +
  1. +

    # Document Title: The first heading should be a level one heading, and +should ideally be the same or nearly the same as the filename. The first +level one heading is used as the page <title>.

    +
  2. +
  3. +

    author: Optional. If you’d like to claim ownership of the document or +if you are very proud of it, add yourself under the title. However, +revision history generally suffices.

    +
  4. +
  5. +

    Short introduction. 1-3 sentences providing a high-level overview of the +topic. Imagine yourself as a complete newbie, who landed on your “Extending +Foo” doc and needs to know the most basic assumptions you take for granted. +“What is Foo? Why would I extend it?”

    +
  6. +
  7. +

    [TOC]: if you use hosting that supports table of contents, such as Gitiles, +put [TOC] after the short introduction. See +[TOC] documentation.

    +
  8. +
  9. +

    ## Topic: The rest of your headings should start from level 2.

    +
  10. +
  11. +

    ## See also: Put miscellaneous links at the bottom for the user who wants +to know more or didn’t find what she needed.

    +
  12. +
+ +

Character line limit

+ +

Obey projects’ character line limit wherever possible. Long URLs and tables are +the usual suspects when breaking the rule. (Headings also can’t be wrapped, but +we encourage keeping them short). Otherwise, wrap your text:

+ +
Lorem ipsum dolor sit amet, nec eius volumus patrioque cu, nec et commodo
+hendrerit, id nobis saperet fuisset ius.
+
+*   Malorum moderatius vim eu. In vix dico persecuti. Te nam saperet percipitur
+interesset. See the [foo docs](https://gerrit.googlesource.com/gitiles/+/master/Documentation/markdown.md).
+
+ +

Often, inserting a newline before a long link preserves readability while +minimizing the overflow:

+ +
Lorem ipsum dolor sit amet. See the
+[foo docs](https://gerrit.googlesource.com/gitiles/+/master/Documentation/markdown.md)
+for details.
+
+ +

Trailing whitespace

+ +

Don’t use trailing whitespace, use a trailing backslash.

+ +

The CommonMark spec decrees +that two spaces at the end of a line should insert a <br /> tag. However, many +directories have a trailing whitespace presubmit check in place, and many IDEs +will clean it up anyway.

+ +

Best practice is to avoid the need for a <br /> altogether. Markdown creates +paragraph tags for you simply with newlines: get used to that.

+ +

Headings

+ +

ATX-style headings

+ +
## Heading 2
+
+ +

Headings with = or - underlines can be annoying to maintain and don’t fit +with the rest of the heading syntax. The user has to ask: Does --- mean H1 or +H2?

+ +
Heading - do you remember what level? DO NOT DO THIS.
+---------
+
+ +

Add spacing to headings

+ +

Prefer spacing after # and newlines before and after:

+ +
...text before.
+
+# Heading 1
+
+Text after...
+
+ +

Lack of spacing makes it a little harder to read in source:

+ +
...text before.
+
+#Heading 1
+Text after... DO NOT DO THIS.
+
+ +

Lists

+ +

Use lazy numbering for long lists

+ +

Markdown is smart enough to let the resulting HTML render your numbered lists +correctly. For longer lists that may change, especially long nested lists, use +“lazy” numbering:

+ +
1.  Foo.
+1.  Bar.
+    1.  Foofoo.
+    1.  Barbar.
+1.  Baz.
+
+ +

However, if the list is small and you don’t anticipate changing it, prefer fully +numbered lists, because it’s nicer to read in source:

+ +
1.  Foo.
+2.  Bar.
+3.  Baz.
+
+ +

Nested list spacing

+ +

When nesting lists, use a 4 space indent for both numbered and bulleted lists:

+ +
1.  2 spaces after a numbered list.
+4 space indent for wrapped text.
+2.  2 spaces again.
+
+*   3 spaces after a bullet.
+4 space indent for wrapped text.
+    1.  2 spaces after a numbered list.
+  8 space indent for the wrapped text of a nested list.
+    2.  Looks nice, don't it?
+*   3 spaces after a bullet.
+
+ +

The following works, but it’s very messy:

+ +
* One space,
+with no indent for wrapped text.
+     1. Irregular nesting... DO NOT DO THIS.
+
+ +

Even when there’s no nesting, using the 4 space indent makes layout consistent +for wrapped text:

+ +
*   Foo,
+wrapped.
+
+1.  2 spaces
+and 4 space indenting.
+2.  2 spaces again.
+
+ +

However, when lists are small, not nested, and a single line, one space can +suffice for both kinds of lists:

+ +
* Foo
+* Bar
+* Baz.
+
+1. Foo.
+2. Bar.
+
+ +

Code

+ +

Inline

+ +

`Backticks` designate inline code, and will render all wrapped content +literally. Use them for short code quotations and field names:

+ +
You'll want to run `really_cool_script.sh arg`.
+
+Pay attention to the `foo_bar_whammy` field in that table.
+
+ +

Use inline code when referring to file types in an abstract sense, rather than a +specific file:

+ +
Be sure to update your `README.md`!
+
+ +

Backticks are the most common approach for “escaping” Markdown metacharacters; +in most situations where escaping would be needed, code font just makes sense +anyway.

+ +

Codeblocks

+ +

For code quotations longer than a single line, use a codeblock:

+ +
+```python
+def Foo(self, bar):
+self.bar = bar
+```
+
+ +

Declare the language

+ +

It is best practice to explicitly declare the language, so that neither the +syntax highlighter nor the next editor must guess.

+ +

Indented codeblocks are sometimes cleaner

+ +

Four-space indenting is also interpreted as a codeblock. These can look +cleaner and be easier to read in source, but there is no way to specify the +language. We encourage their use when writing many short snippets:

+ +
You'll need to run:
+
+bazel run :thing -- --foo
+
+And then:
+
+bazel run :another_thing -- --bar
+
+And again:
+
+bazel run :yet_again -- --baz
+
+ +

Escape newlines

+ +

Because most commandline snippets are intended to be copied and pasted directly +into a terminal, it’s best practice to escape any newlines. Use a single +backslash at the end of the line:

+ +
+```shell
+bazel run :target -- --flag --foo=longlonglonglonglongvalue \
+--bar=anotherlonglonglonglonglonglonglonglonglonglongvalue
+```
+
+ +

Nest codeblocks within lists

+ +

If you need a codeblock within a list, make sure to indent it so as to not break +the list:

+ +
*   Bullet.
+
+```c++
+int foo;
+```
+
+*   Next bullet.
+
+ +

You can also create a nested code block with 4 spaces. Simply indent 4 +additional spaces from the list indentation:

+ +
*   Bullet.
+
+  int foo;
+
+*   Next bullet.
+
+ + + +

Long links make source Markdown difficult to read and break the 80 character +wrapping. Wherever possible, shorten your links.

+ + + +

Markdown link syntax allows you to set a link title, just as HTML does. Use it +wisely.

+ +

Titling your links as “link” or “here” tells the reader precisely nothing when +quickly scanning your doc and is a waste of space:

+ +
See the syntax guide for more info: [link](syntax_guide.md).
+Or, check out the style guide [here](style_guide.md).
+DO NOT DO THIS.
+
+ +

Instead, write the sentence naturally, then go back and wrap the most +appropriate phrase with the link:

+ +
See the [syntax guide](syntax_guide.md) for more info.
+Or, check out the [style guide](style_guide.md).
+
+ +

Images

+ +

Use images sparingly, and prefer simple screenshots. This guide is designed +around the idea that plain text gets users down to the business of communication +faster with less reader distraction and author procrastination. However, it’s +sometimes very helpful to show what you mean.

+ +

See image syntax.

+ +

Prefer lists to tables

+ +

Any tables in your Markdown should be small. Complex, large tables are difficult +to read in source and most importantly, a pain to modify later.

+ +
Fruit | Attribute | Notes
+--- | --- | --- | ---
+Apple | [Juicy](https://example.com/SomeReallyReallyReallyReallyReallyReallyReallyReallyLongQuery), Firm, Sweet | Apples keep doctors away.
+Banana | [Convenient](https://example.com/SomeDifferentReallyReallyReallyReallyReallyReallyReallyReallyLongQuery), Soft, Sweet | Contrary to popular belief, most apes prefer mangoes.
+
+DO NOT DO THIS
+
+ +

Lists and subheadings usually suffice to present the same information +in a slightly less compact, though much more edit-friendly way:

+ +
## Fruits
+
+### Apple
+
+* [Juicy](https://SomeReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyLongURL)
+* Firm
+* Sweet
+
+Apples keep doctors away.
+
+### Banana
+
+* [Convenient](https://example.com/SomeDifferentReallyReallyReallyReallyReallyReallyReallyReallyLongQuery)
+* Soft
+* Sweet
+
+Contrary to popular belief, most apes prefer mangoes.
+
+ +

However, there are times when a small table is called for:

+ +
Transport | Favored by | Advantages
+--- | --- | ---
+Swallow | Coconuts | Otherwise unladen
+Bicycle | Miss Gulch | Weatherproof
+X-34 landspeeder | Whiny farmboys | Cheap since the X-38 came out
+
+ +

Strongly prefer Markdown to HTML

+ +

Please prefer standard Markdown syntax wherever possible and avoid HTML hacks. +If you can’t seem to accomplish what you want, reconsider whether you really +need it. Except for big tables, Markdown meets almost +all needs already.

+ +

Every bit of HTML or Javascript hacking reduces the readability and portability. +This in turn limits the usefulness of integrations with +other tools, which may either present the source as plain text or render it. See +Philosophy.

+ +

Gitiles does not render HTML.

+ + +