/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/
/* * mod_autoindex.c: Handles the on-the-fly html index generation * * Rob McCool * 3/23/93 * * Adapted to Apache by rst. * * Version sort added by Martin Pool <mbp@humbug.org.au>.
*/
/* * This routine puts the standard HTML header at the top of the index page. * We include the DOCTYPE because we may be using features therefrom (i.e., * HEIGHT and WIDTH attributes on the icons if we're FancyIndexing).
*/ staticvoid emit_preamble(request_rec *r, int xhtml, constchar *title)
{
autoindex_config_rec *d;
d = (autoindex_config_rec *) ap_get_module_config(r->per_dir_config,
&autoindex_module);
if (xhtml) {
ap_rvputs(r, DOCTYPE_XHTML_1_0T, "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" " <head>\n <title>Index of ", title, "</title>\n", NULL);
} else {
ap_rvputs(r, DOCTYPE_HTML_3_2, "<html>\n <head>\n" " <title>Index of ", title, "</title>\n", NULL);
}
if (cl == NULL) { return"missing closing paren";
}
alt = ap_getword_nc(cmd->temp_pool, &iconbak, ',');
*cl = '\0'; /* Lose closing paren */
add_alt(cmd, d, &alt[1], to);
} if (cmd->info == BY_PATH) { if (!strcmp(to, "**DIRECTORY**")) {
to = "^^DIRECTORY^^";
}
} if (cmd->info == BY_ENCODING) { char *tmp = apr_pstrdup(cmd->temp_pool, to);
ap_str_tolower(tmp);
to = tmp;
}
push_item(((autoindex_config_rec *) d)->icon_list, cmd->info, to,
cmd->path, iconbak); return NULL;
}
/* * Add description text for a filename pattern. If the pattern has * wildcards already (or we need to add them), add leading and * trailing wildcards to it to ensure substring processing. If the * pattern contains a '/' anywhere, force wildcard matching mode, * add a slash to the prefix so that "bar/bletch" won't be matched * by "foobar/bletch", and make a note that there's a delimiter; * the matching routine simplifies to just the actual filename * whenever it can. This allows definitions in parent directories * to be made for files in subordinate ones using relative paths.
*/
/* * Absent a strcasestr() function, we have to force wildcards on * systems for which "AAA" and "aaa" mean the same file.
*/ #ifdef CASE_BLIND_FILESYSTEM #define WILDCARDS_REQUIRED 1 #else #define WILDCARDS_REQUIRED 0 #endif
staticconst command_rec autoindex_cmds[] =
{
AP_INIT_ITERATE2("AddIcon", add_icon, BY_PATH, DIR_CMD_PERMS, "an icon URL followed by one or more filenames"),
AP_INIT_ITERATE2("AddIconByType", add_icon, BY_TYPE, DIR_CMD_PERMS, "an icon URL followed by one or more MIME types"),
AP_INIT_ITERATE2("AddIconByEncoding", add_icon, BY_ENCODING, DIR_CMD_PERMS, "an icon URL followed by one or more content encodings"),
AP_INIT_ITERATE2("AddAlt", add_alt, BY_PATH, DIR_CMD_PERMS, "alternate descriptive text followed by one or more " "filenames"),
AP_INIT_ITERATE2("AddAltByType", add_alt, BY_TYPE, DIR_CMD_PERMS, "alternate descriptive text followed by one or more MIME " "types"),
AP_INIT_ITERATE2("AddAltByEncoding", add_alt, BY_ENCODING, DIR_CMD_PERMS, "alternate descriptive text followed by one or more " "content encodings"),
AP_INIT_TAKE_ARGV("IndexOptions", add_opts, NULL, DIR_CMD_PERMS, "one or more index options [+|-][]"),
AP_INIT_TAKE2("IndexOrderDefault", set_default_order, NULL, DIR_CMD_PERMS, "{Ascending,Descending} {Name,Size,Description,Date}"),
AP_INIT_ITERATE("IndexIgnore", add_ignore, NULL, DIR_CMD_PERMS, "one or more file extensions"),
AP_INIT_FLAG("IndexIgnoreReset", ap_set_flag_slot,
(void *)APR_OFFSETOF(autoindex_config_rec, ign_noinherit),
DIR_CMD_PERMS, "Reset the inherited list of IndexIgnore filenames"),
AP_INIT_ITERATE2("AddDescription", add_desc, BY_PATH, DIR_CMD_PERMS, "Descriptive text followed by one or more filenames"),
AP_INIT_TAKE1("HeaderName", ap_set_string_slot,
(void *)APR_OFFSETOF(autoindex_config_rec, header),
DIR_CMD_PERMS, "a filename"),
AP_INIT_TAKE1("ReadmeName", ap_set_string_slot,
(void *)APR_OFFSETOF(autoindex_config_rec, readme),
DIR_CMD_PERMS, "a filename"),
AP_INIT_RAW_ARGS("FancyIndexing", ap_set_deprecated, NULL, OR_ALL, "The FancyIndexing directive is no longer supported. " "Use IndexOptions FancyIndexing."),
AP_INIT_TAKE1("DefaultIcon", ap_set_string_slot,
(void *)APR_OFFSETOF(autoindex_config_rec, default_icon),
DIR_CMD_PERMS, "an icon URL"),
AP_INIT_TAKE1("IndexStyleSheet", ap_set_string_slot,
(void *)APR_OFFSETOF(autoindex_config_rec, style_sheet),
DIR_CMD_PERMS, "URL to style sheet"),
AP_INIT_TAKE1("IndexHeadInsert", ap_set_string_slot,
(void *)APR_OFFSETOF(autoindex_config_rec, head_insert),
DIR_CMD_PERMS, "String to insert in HTML HEAD section"),
{NULL}
};
new->alt_list = apr_array_append(p, add->alt_list, base->alt_list);
new->desc_list = apr_array_append(p, add->desc_list, base->desc_list);
new->icon_list = apr_array_append(p, add->icon_list, base->icon_list);
new->ign_list = add->ign_noinherit ? add->ign_list : apr_array_append(p, add->ign_list, base->ign_list); if (add->opts == NO_OPTIONS) { /* * If the current directory explicitly says 'no options' then we also * clear any incremental mods from being inheritable further down.
*/
new->opts = NO_OPTIONS;
new->incremented_opts = 0;
new->decremented_opts = 0;
} else { /* * If there were any nonincremental options selected for * this directory, they dominate and we don't inherit *anything.* * Contrariwise, we *do* inherit if the only settings here are * incremental ones.
*/ if (add->opts == OPTION_UNSET) {
new->incremented_opts = (base->incremented_opts
| add->incremented_opts)
& ~add->decremented_opts;
new->decremented_opts = (base->decremented_opts
| add->decremented_opts); /* * We may have incremental settings, so make sure we don't * inadvertently inherit an IndexOptions None from above.
*/
new->opts = (base->opts & ~NO_OPTIONS);
} else { /* * There are local nonincremental settings, which clear * all inheritance from above. They *are* the new base settings.
*/
new->opts = add->opts;
} /* * We're guaranteed that there'll be no overlap between * the add-options and the remove-options.
*/
new->opts |= new->incremented_opts;
new->opts &= ~new->decremented_opts;
} /* * Inherit the NameWidth settings if there aren't any specific to * the new location; otherwise we'll end up using the defaults set in the * config-rec creation routine.
*/ if (add->name_adjust == K_UNSET) {
new->name_width = base->name_width;
new->name_adjust = base->name_adjust;
} else {
new->name_width = add->name_width;
new->name_adjust = add->name_adjust;
}
#define find_icon(d,p,t) find_item_by_request(p,d->icon_list,t) #define find_alt(d,p,t) find_item_by_request(p,d->alt_list,t) #define find_default_icon(d,n) find_item(NULL, NULL, n, d->icon_list, 1) #define find_default_alt(d,n) find_item(NULL, NULL, n, d->alt_list, 1)
/* * Look through the list of pattern/description pairs and return the first one * if any) that matches the filename in the request. If multiple patterns * match, only the first one is used; since the order in the array is the * same as the order in which directives were processed, earlier matching * directives will dominate.
*/
/* * If the filename includes a path, extract just the name itself * for the simple matches.
*/ if ((filename_only = ap_strrchr_c(filename_full, '/')) == NULL) {
filename_only = filename_full;
} else {
filename_only++;
} for (i = 0; i < dcfg->desc_list->nelts; ++i) {
ai_desc_t *tuple = &list[i]; int found;
/* * Only use the full-path filename if the pattern contains '/'s.
*/
filename = (tuple->full_path) ? filename_full : filename_only; /* * Make the comparison using the cheapest method; only do * wildcard checking if we must.
*/ if (tuple->wildcards) {
found = (apr_fnmatch(tuple->pattern, filename, MATCH_FLAGS) == 0);
} else {
found = (ap_strstr_c(filename, tuple->pattern) != NULL);
} if (found) { return tuple->description;
}
} return NULL;
}
for (i = 0; i < list->nelts; ++i) { struct item *p = &items[i]; char *ap;
if ((ap = strrchr(p->apply_to, '/')) == NULL) {
ap = p->apply_to;
} else {
ap++;
}
#ifndef CASE_BLIND_FILESYSTEM if (!ap_strcmp_match(path, p->apply_path)
&& !ap_strcmp_match(tt, ap)) { return 1;
} #else/* !CASE_BLIND_FILESYSTEM */ /* * On some platforms, the match must be case-blind. This is really * a factor of the filesystem involved, but we can't detect that * reliably - so we have to granularise at the OS level.
*/ if (!ap_strcasecmp_match(path, p->apply_path)
&& !ap_strcasecmp_match(tt, ap)) { return 1;
} #endif/* !CASE_BLIND_FILESYSTEM */
} return 0;
}
/* * Elements of the emitted document: * Preamble * Emitted unless SUPPRESS_PREAMBLE is set AND ap_run_sub_req * succeeds for the (content_type == text/html) header file. * Header file * Emitted if found (and able). * H1 tag line * Emitted if a header file is NOT emitted. * Directory stuff * Always emitted. * HR * Emitted if FANCY_INDEXING is set. * Readme file * Emitted if found (and able). * ServerSig * Emitted if ServerSignature is not Off AND a readme file * is NOT emitted. * Postamble * Emitted unless SUPPRESS_PREAMBLE is set AND ap_run_sub_req * succeeds for the (content_type == text/html) readme file.
*/
/* * emit a plain text file
*/ staticvoid do_emit_plain(request_rec *r, apr_file_t *f)
{ char buf[AP_IOBUFSIZE + 1]; int ch;
apr_size_t i, c, n;
apr_status_t rv;
ap_rputs("<pre>\n", r); while (!apr_file_eof(f)) { do {
n = sizeof(char) * AP_IOBUFSIZE;
rv = apr_file_read(f, buf, &n);
} while (APR_STATUS_IS_EINTR(rv)); if (n == 0 || rv != APR_SUCCESS) { /* ###: better error here? */ break;
}
buf[n] = '\0';
c = 0; while (c < n) { for (i = c; i < n; i++) { if (buf[i] == '<' || buf[i] == '>' || buf[i] == '&') { break;
}
}
ch = buf[i];
buf[i] = '\0';
ap_rputs(&buf[c], r); if (ch == '<') {
ap_rputs("<", r);
} elseif (ch == '>') {
ap_rputs(">", r);
} elseif (ch == '&') {
ap_rputs("&", r);
}
c = i + 1;
}
}
ap_rputs("</pre>\n", r);
}
/* * Handle the preamble through the H1 tag line, inclusive. Locate * the file with a subrequests. Process text/html documents by actually * running the subrequest; text/xxx documents get copied verbatim, * and any other content type is ignored. This means that a non-text * document (such as HEADER.gif) might get multiviewed as the result * instead of a text document, meaning nothing will be displayed, but * oh well.
*/ staticvoid emit_head(request_rec *r, char *header_fname, int suppress_amble, int emit_xhtml, char *title)
{
autoindex_config_rec *d;
apr_table_t *hdrs = r->headers_in;
apr_file_t *f = NULL;
request_rec *rr = NULL; int emit_amble = 1; int emit_H1 = 1; constchar *r_accept; constchar *r_accept_enc;
/* * If there's a header file, send a subrequest to look for it. If it's * found and html do the subrequest, otherwise handle it
*/
r_accept = apr_table_get(hdrs, "Accept");
r_accept_enc = apr_table_get(hdrs, "Accept-Encoding");
apr_table_setn(hdrs, "Accept", "text/html, text/plain");
apr_table_unset(hdrs, "Accept-Encoding");
if ((header_fname != NULL)
&& (rr = ap_sub_req_lookup_uri(header_fname, r, r->output_filters))
&& (rr->status == HTTP_OK)
&& (rr->filename != NULL)
&& (rr->finfo.filetype == APR_REG)) { /* * Check for the two specific cases we allow: text/html and * text/anything-else. The former is allowed to be processed for * SSIs.
*/ if (rr->content_type != NULL) { if (response_is_html(rr)) {
ap_filter_t *f; /* Hope everything will work... */
emit_amble = 0;
emit_H1 = 0;
if (! suppress_amble) {
emit_preamble(r, emit_xhtml, title);
} /* This is a hack, but I can't find any better way to do this. * The problem is that we have already created the sub-request, * but we just inserted the OLD_WRITE filter, and the * sub-request needs to pass its data through the OLD_WRITE * filter, or things go horribly wrong (missing data, data in * the wrong order, etc). To fix it, if you create a * sub-request and then insert the OLD_WRITE filter before you * run the request, you need to make sure that the sub-request * data goes through the OLD_WRITE filter. Just steal this * code. The long-term solution is to remove the ap_r* * functions.
*/ for (f=rr->output_filters;
f->frec != ap_subreq_core_filter_handle; f = f->next);
f->next = r->output_filters;
/* * If there's a problem running the subrequest, display the * preamble if we didn't do it before -- the header file * didn't get displayed.
*/ if (ap_run_sub_req(rr) != OK) { /* It didn't work */
emit_amble = suppress_amble;
emit_H1 = 1;
}
} elseif (!ap_cstr_casecmpn("text/", rr->content_type, 5)) { /* * If we can open the file, prefix it with the preamble * regardless; since we'll be sending a <pre> block around * the file's contents, any HTML header it had won't end up * where it belongs.
*/ if (apr_file_open(&f, rr->filename, APR_READ,
APR_OS_DEFAULT, r->pool) == APR_SUCCESS) {
emit_preamble(r, emit_xhtml, title);
emit_amble = 0;
do_emit_plain(r, f);
apr_file_close(f);
emit_H1 = 0;
}
}
}
}
if (r_accept_enc) {
apr_table_setn(hdrs, "Accept-Encoding", r_accept_enc);
}
if (emit_amble) {
emit_preamble(r, emit_xhtml, title);
}
d = (autoindex_config_rec *) ap_get_module_config(r->per_dir_config, &autoindex_module);
if (emit_H1) { if (d->style_sheet != NULL) { /* Insert style id if stylesheet used */
ap_rvputs(r, " <h1 id=\"indextitle\">Index of ", title, "</h1>\n", NULL);
} else {
ap_rvputs(r, "<h1>Index of ", title, "</h1>\n", NULL);
}
} if (rr != NULL) {
ap_destroy_sub_req(rr);
}
}
/* * Handle the Readme file through the postamble, inclusive. Locate * the file with a subrequests. Process text/html documents by actually * running the subrequest; text/xxx documents get copied verbatim, * and any other content type is ignored. This means that a non-text * document (such as FOOTER.gif) might get multiviewed as the result * instead of a text document, meaning nothing will be displayed, but * oh well.
*/ staticvoid emit_tail(request_rec *r, char *readme_fname, int suppress_amble)
{
apr_file_t *f = NULL;
request_rec *rr = NULL; int suppress_post = 0; int suppress_sig = 0;
/* * If there's a readme file, send a subrequest to look for it. If it's * found and a text file, handle it -- otherwise fall through and * pretend there's nothing there.
*/ if ((readme_fname != NULL)
&& (rr = ap_sub_req_lookup_uri(readme_fname, r, r->output_filters))
&& (rr->status == HTTP_OK)
&& (rr->filename != NULL)
&& rr->finfo.filetype == APR_REG) { /* * Check for the two specific cases we allow: text/html and * text/anything-else. The former is allowed to be processed for * SSIs.
*/ if (rr->content_type != NULL) { if (response_is_html(rr)) {
ap_filter_t *f; for (f=rr->output_filters;
f->frec != ap_subreq_core_filter_handle; f = f->next);
f->next = r->output_filters;
if (ap_run_sub_req(rr) == OK) { /* worked... */
suppress_sig = 1;
suppress_post = suppress_amble;
}
} elseif (!ap_cstr_casecmpn("text/", rr->content_type, 5)) { /* * If we can open the file, suppress the signature.
*/ if (apr_file_open(&f, rr->filename, APR_READ,
APR_OS_DEFAULT, r->pool) == APR_SUCCESS) {
do_emit_plain(r, f);
apr_file_close(f);
suppress_sig = 1;
}
}
}
}
if (!suppress_sig) {
ap_rputs(ap_psignature("", r), r);
} if (!suppress_post) {
ap_rputs("</body></html>\n", r);
} if (rr != NULL) {
ap_destroy_sub_req(rr);
}
}
staticchar *find_title(request_rec *r)
{ char titlebuf[MAX_STRING_LEN], *find = "<title>";
apr_file_t *thefile = NULL; int x, y, p;
apr_size_t n;
if (r->status != HTTP_OK) { return NULL;
} if ((r->content_type != NULL)
&& (response_is_html(r)
|| !strcmp(r->content_type, INCLUDES_MAGIC_TYPE))
&& !r->content_encoding) { if (apr_file_open(&thefile, r->filename, APR_READ,
APR_OS_DEFAULT, r->pool) != APR_SUCCESS) { return NULL;
}
n = sizeof(char) * (MAX_STRING_LEN - 1);
apr_file_read(thefile, titlebuf, &n); if (n == 0) {
apr_file_close(thefile); return NULL;
}
titlebuf[n] = '\0'; for (x = 0, p = 0; titlebuf[x]; x++) { if (apr_tolower(titlebuf[x]) == find[p]) { if (!find[++p]) { if ((p = ap_ind(&titlebuf[++x], '<')) != -1) {
titlebuf[x + p] = '\0';
} /* Scan for line breaks for Tanmoy's secretary */ for (y = x; titlebuf[y]; y++) { if ((titlebuf[y] == CR) || (titlebuf[y] == LF)) { if (y == x) {
x++;
} else {
titlebuf[y] = ' ';
}
}
}
apr_file_close(thefile); return apr_pstrdup(r->pool, &titlebuf[x]);
}
} else {
p = 0;
}
}
apr_file_close(thefile);
} return NULL;
}
staticstruct ent *make_parent_entry(apr_int32_t autoindex_opts,
autoindex_config_rec *d,
request_rec *r, char keyid, char direction)
{ struct ent *p = (struct ent *) apr_pcalloc(r->pool, sizeof(struct ent)); char *testpath; /* * p->name is now the true parent URI. * testpath is a crafted lie, so that the syntax '/some/..' * (or simply '..')be used to describe 'up' from '/some/' * when processeing IndexIgnore, and Icon|Alt|Desc configs.
*/
/* The output has always been to the parent. Don't make ourself * our own parent (worthless cyclical reference).
*/ if (!(p->name = ap_make_full_path(r->pool, r->uri, "../"))) { return (NULL);
} if (!ap_normalize_path(p->name, AP_NORMALIZE_ALLOW_RELATIVE |
AP_NORMALIZE_NOT_ABOVE_ROOT)
|| p->name[0] == '\0') { return (NULL);
}
/* IndexIgnore has always compared "/thispath/.." */
testpath = ap_make_full_path(r->pool, r->filename, ".."); if (ignore_entry(d, testpath)) { return (NULL);
}
p->size = -1;
p->lm = -1;
p->key = apr_toupper(keyid);
p->ascending = (apr_toupper(direction) == D_ASCENDING);
p->version_sort = autoindex_opts & VERSION_SORT; if (autoindex_opts & FANCY_INDEXING) { if (!(p->icon = find_default_icon(d, testpath))) {
p->icon = find_default_icon(d, "^^DIRECTORY^^");
} if (!(p->alt = find_default_alt(d, testpath))) { if (!(p->alt = find_default_alt(d, "^^DIRECTORY^^"))) { /* Special alt text for parent dir to distinguish it from other directories
this is essential when trying to style this dir entry via AddAltClass */
p->alt = "PARENTDIR";
}
}
p->desc = find_desc(d, testpath);
} return p;
}
staticstruct ent *make_autoindex_entry(const apr_finfo_t *dirent, int autoindex_opts,
autoindex_config_rec *d,
request_rec *r, char keyid, char direction, constchar *pattern)
{
request_rec *rr; struct ent *p; int show_forbidden = 0;
/* Dot is ignored, Parent is handled by make_parent_entry() */ if ((dirent->name[0] == '.') && (!dirent->name[1]
|| ((dirent->name[1] == '.') && !dirent->name[2]))) return (NULL);
/* * On some platforms, the match must be case-blind. This is really * a factor of the filesystem involved, but we can't detect that * reliably - so we have to granularise at the OS level.
*/ if (pattern && (apr_fnmatch(pattern, dirent->name,
APR_FNM_NOESCAPE | APR_FNM_PERIOD #ifdef CASE_BLIND_FILESYSTEM
| APR_FNM_CASE_BLIND #endif
)
!= APR_SUCCESS)) { return (NULL);
}
if (ignore_entry(d, ap_make_full_path(r->pool,
r->filename, dirent->name))) { return (NULL);
}
if ((!p->desc) && (autoindex_opts & SCAN_HTML_TITLES)) {
p->desc = apr_pstrdup(r->pool, find_title(rr));
}
}
ap_destroy_sub_req(rr); /* * We don't need to take any special action for the file size key. * If we did, it would go here.
*/ if (keyid == K_LAST_MOD) { if (p->lm < 0) {
p->lm = 0;
}
} return (p);
}
staticchar *terminate_description(autoindex_config_rec *d, char *desc,
apr_int32_t autoindex_opts, int desc_width)
{ int maxsize = desc_width; int x;
/* * If there's no DescriptionWidth in effect, default to the old * behaviour of adjusting the description size depending upon * what else is being displayed. Otherwise, stick with the * setting.
*/ if (d->desc_adjust == K_UNSET) { if (autoindex_opts & SUPPRESS_ICON) {
maxsize += 6;
} if (autoindex_opts & SUPPRESS_LAST_MOD) {
maxsize += 19;
} if (autoindex_opts & SUPPRESS_SIZE) {
maxsize += 7;
}
} for (x = 0; desc[x] && ((maxsize > 0) || (desc[x] == '<')); x++) { if (desc[x] == '<') { while (desc[x] != '>') { if (!desc[x]) {
maxsize = 0; break;
}
++x;
}
} elseif (desc[x] == '&') { /* entities like ä count as one character */
--maxsize; for ( ; desc[x] != ';'; ++x) { if (desc[x] == '\0') {
maxsize = 0; break;
}
}
} else {
--maxsize;
}
} if (!maxsize && desc[x] != '\0') {
desc[x - 1] = '>'; /* Grump. */
desc[x] = '\0'; /* Double Grump! */
} return desc;
}
/* * Emit the anchor for the specified field. If a field is the key for the * current request, the link changes its meaning to reverse the order when * selected again. Non-active fields always start in ascending order.
*/ staticvoid emit_link(request_rec *r, constchar *anchor, char column, char curkey, char curdirection, constchar *colargs, int nosort)
{ if (!nosort) { char qvalue[9];
/* * Compare two file entries according to the sort criteria. The return * is essentially a signum function value.
*/
staticint dsortf(struct ent **e1, struct ent **e2)
{ struct ent *c1; struct ent *c2; int result = 0;
/* * First, see if either of the entries is for the parent directory. * If so, that *always* sorts lower than anything else.
*/ if ((*e1)->name[0] == '/') { return -1;
} if ((*e2)->name[0] == '/') { return 1;
} /* * Now see if one's a directory and one isn't, if we're set * isdir for FOLDERS_FIRST.
*/ if ((*e1)->isdir != (*e2)->isdir) { return (*e1)->isdir ? -1 : 1;
} /* * All of our comparisons will be of the c1 entry against the c2 one, * so assign them appropriately to take care of the ordering.
*/ if ((*e1)->ascending) {
c1 = *e1;
c2 = *e2;
} else {
c1 = *e2;
c2 = *e1;
}
switch (c1->key) { case K_LAST_MOD: if (c1->lm > c2->lm) { return 1;
} elseif (c1->lm < c2->lm) { return -1;
} break; case K_SIZE: if (c1->size > c2->size) { return 1;
} elseif (c1->size < c2->size) { return -1;
} break; case K_DESC: if (c1->version_sort) {
result = apr_strnatcmp(c1->desc ? c1->desc : "",
c2->desc ? c2->desc : "");
} else {
result = strcmp(c1->desc ? c1->desc : "",
c2->desc ? c2->desc : "");
} if (result) { return result;
} break;
}
/* names may identical when treated case-insensitively, * so always fall back on strcmp() flavors to put entries * in deterministic order. This means that 'ABC' and 'abc' * will always appear in the same order, rather than * variably between 'ABC abc' and 'abc ABC' order.
*/
if (c1->version_sort) { if (c1->ignore_case) {
result = apr_strnatcasecmp (c1->name, c2->name);
} if (!result) {
result = apr_strnatcmp(c1->name, c2->name);
}
}
/* The names may be identical in respects other than * filename case when strnatcmp is used above, so fall back * to strcmp on conflicts so that fn1.01.zzz and fn1.1.zzz * are also sorted in a deterministic order.
*/
if (!result && c1->ignore_case) {
result = strcasecmp (c1->name, c2->name);
}
if (!result) {
result = strcmp (c1->name, c2->name);
}
if (autoindex_opts & TRACK_MODIFIED) {
ap_update_mtime(r, r->finfo.mtime);
ap_set_last_modified(r);
ap_set_etag(r);
} if (r->header_only) {
apr_dir_close(thedir); return 0;
}
/* * If there is no specific ordering defined for this directory, * default to ascending by filename.
*/
keyid = autoindex_conf->default_keyid
? autoindex_conf->default_keyid : K_NAME;
direction = autoindex_conf->default_direction
? autoindex_conf->default_direction : D_ASCENDING;
/* * Figure out what sort of indexing (if any) we're supposed to use. * * If no QUERY_STRING was specified or client query strings have been * explicitly disabled. * If we are ignoring the client, suppress column sorting as well.
*/ if (autoindex_opts & IGNORE_CLIENT) {
qstring = NULL;
autoindex_opts |= SUPPRESS_COLSORT;
colargs = "";
} else { char fval[5], vval[5], *ppre = "", *epattern = "";
fval[0] = '\0'; vval[0] = '\0';
qstring = r->args;
/* * Since we don't know how many dir. entries there are, put them into a * linked list and then arrayificate them so qsort can use them.
*/
head = NULL;
p = make_parent_entry(autoindex_opts, autoindex_conf, r, keyid, direction); if (p != NULL) {
p->next = head;
head = p;
num_ent++;
}
fullpath = apr_palloc(r->pool, APR_PATH_MAX);
dirpathlen = strlen(name);
memcpy(fullpath, name, dirpathlen);
do {
status = apr_dir_read(&dirent, APR_FINFO_MIN | APR_FINFO_NAME, thedir); if (APR_STATUS_IS_INCOMPLETE(status)) { continue; /* ignore un-stat()able files */
} elseif (status != APR_SUCCESS) { break;
}
/* We want to explode symlinks here. */ if (dirent.filetype == APR_LNK) { constchar *savename;
apr_finfo_t fi; /* We *must* have FNAME. */
savename = dirent.name;
apr_cpystrn(fullpath + dirpathlen, dirent.name,
APR_PATH_MAX - dirpathlen);
status = apr_stat(&fi, fullpath,
dirent.valid & ~(APR_FINFO_NAME), r->pool); if (status != APR_SUCCESS) { /* Something bad happened, skip this file. */ continue;
}
memcpy(&dirent, &fi, sizeof(fi));
dirent.name = savename;
dirent.valid |= APR_FINFO_NAME;
}
p = make_autoindex_entry(&dirent, autoindex_opts, autoindex_conf, r,
keyid, direction, pstring); if (p != NULL) {
p->next = head;
head = p;
num_ent++;
}
} while (1);
if (num_ent > 0) {
ar = (struct ent **) apr_palloc(r->pool,
num_ent * sizeof(struct ent *));
p = head;
x = 0; while (p) {
ar[x++] = p;
p = p->next;
}
/* OK, nothing easy. Trot out the heavy artillery... */
if (allow_opts & OPT_INDEXES) { int errstatus;
if ((errstatus = ap_discard_request_body(r)) != OK) { return errstatus;
}
/* KLUDGE --- make the sub_req lookups happen in the right directory. * Fixing this in the sub_req_lookup functions themselves is difficult, * and would probably break virtual includes...
*/
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.