X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/agedu/blobdiff_plain/144550c6c335df2214a24d42aa49db89091e7e78..c47f39de1c2de25aa70a5f3040c531e6589420cf:/agedu.c diff --git a/agedu.c b/agedu.c index a8269a9..fd8aaea 100644 --- a/agedu.c +++ b/agedu.c @@ -309,6 +309,8 @@ static void text_query(const void *mappedfile, const char *querydir, HELPARG("directory") HELPOPT("scan only, generating a dump") \ VAL(HTML) SHORT(H) LONG(html) \ HELPARG("subdir") HELPOPT("print an HTML report on a subdirectory") \ + NOVAL(CGI) LONG(cgi) \ + HELPOPT("do the right thing when run from a CGI script") \ HELPPFX("options") \ VAL(DATAFILE) SHORT(f) LONG(file) \ HELPARG("filename") HELPOPT("[most modes] specify index file") \ @@ -339,6 +341,8 @@ static void text_query(const void *mappedfile, const char *querydir, HELPOPT("[--scan,--load] keep real atimes on directories") \ NOVAL(NODIRATIME) LONG(no_dir_atime) LONG(no_dir_atimes) \ HELPOPT("[--scan,--load] fake atimes on directories") \ + NOVAL(NOEOF) LONG(no_eof) LONG(noeof) \ + HELPOPT("[--web] do not close web server on EOF") \ NOVAL(MTIME) LONG(mtime) \ HELPOPT("[--scan] use mtime instead of atime") \ NOVAL(SHOWFILES) LONG(files) \ @@ -391,8 +395,8 @@ enum { OPTIONS(IGNORE,IGNORE,IGNORE,LONGTMP) NLONGOPTS }; static const int opthasval[NOPTIONS] = {OPTIONS(ZERO,ONE,IGNORE,IGNORE)}; static const char shortopts[] = {OPTIONS(IGNORE,IGNORE,STRINGNOCOMMA,IGNORE)}; static const char *const longopts[] = {OPTIONS(IGNORE,IGNORE,IGNORE,STRING)}; -enum { OPTIONS(SHORTNEWOPT,SHORTNEWOPT,SHORTTHISOPT,IGNORE) }; -enum { OPTIONS(LONGNEWOPT,LONGNEWOPT,IGNORE,LONGTHISOPT) }; +enum { OPTIONS(SHORTNEWOPT,SHORTNEWOPT,SHORTTHISOPT,IGNORE) UNUSEDENUMVAL1 }; +enum { OPTIONS(LONGNEWOPT,LONGNEWOPT,IGNORE,LONGTHISOPT) UNUSEDENUMVAL2 }; static const int shortvals[] = {OPTIONS(IGNORE,IGNORE,SHORTOPTVAL,IGNORE)}; static const int longvals[] = {OPTIONS(IGNORE,IGNORE,IGNORE,LONGOPTVAL)}; @@ -508,6 +512,7 @@ int main(int argc, char **argv) int depth = -1, gotdepth = 0; int fakediratimes = 1; int mtime = 0; + int closeoneof = 1; int showfiles = 0; #ifdef DEBUG_MAD_OPTION_PARSING_MACROS @@ -662,8 +667,6 @@ int main(int argc, char **argv) for (i = 0; licence[i]; i++) fputs(licence[i], stdout); - - return 0; } return 0; case OPT_SCAN: @@ -712,12 +715,14 @@ int main(int argc, char **argv) nactions++; break; case OPT_HTML: + case OPT_CGI: if (nactions >= actionsize) { actionsize = nactions * 3 / 2 + 16; actions = sresize(actions, actionsize, struct action); } actions[nactions].mode = HTML; - actions[nactions].arg = optval; + actions[nactions].arg = (optid == OPT_HTML ? optval : + NULL); nactions++; break; case OPT_HTTPD: @@ -765,6 +770,9 @@ int main(int argc, char **argv) case OPT_MTIME: mtime = 1; break; + case OPT_NOEOF: + closeoneof = 0; + break; case OPT_DATAFILE: filename = optval; break; @@ -1129,6 +1137,7 @@ int main(int argc, char **argv) prevbuf[0] = '\0'; tf = triewalk_next(tw, buf); assert(tf); + prevtf = NULL; /* placate lint */ while (1) { int i; @@ -1296,16 +1305,48 @@ int main(int argc, char **argv) if (fd < 0) { fprintf(stderr, "%s: %s: open: %s\n", PNAME, filename, strerror(errno)); + if (!querydir) { + printf("Status: 500\nContent-type: text/html\n\n" + "" + "500 Internal Server Error" + "" + "

500 Internal Server Error

" + "

agedu suffered an internal error." + "\n"); + return 0; + } return 1; } if (fstat(fd, &st) < 0) { - perror(PNAME ": fstat"); + fprintf(stderr, "%s: %s: fstat: %s\n", PNAME, filename, + strerror(errno)); + if (!querydir) { + printf("Status: 500\nContent-type: text/html\n\n" + "" + "500 Internal Server Error" + "" + "

500 Internal Server Error

" + "

agedu suffered an internal error." + "\n"); + return 0; + } return 1; } totalsize = st.st_size; mappedfile = mmap(NULL, totalsize, PROT_READ, MAP_SHARED, fd, 0); if (!mappedfile) { - perror(PNAME ": mmap"); + fprintf(stderr, "%s: %s: mmap: %s\n", PNAME, filename, + strerror(errno)); + if (!querydir) { + printf("Status: 500\nContent-type: text/html\n\n" + "" + "500 Internal Server Error" + "" + "

500 Internal Server Error

" + "

agedu suffered an internal error." + "\n"); + return 0; + } return 1; } pathsep = trie_pathsep(mappedfile); @@ -1313,38 +1354,129 @@ int main(int argc, char **argv) maxpathlen = trie_maxpathlen(mappedfile); pathbuf = snewn(maxpathlen, char); - /* - * Trim trailing slash, just in case. - */ - pathlen = strlen(querydir); - if (pathlen > 0 && querydir[pathlen-1] == pathsep) - querydir[--pathlen] = '\0'; - - xi = trie_before(mappedfile, querydir); - if (xi >= trie_count(mappedfile) || - (trie_getpath(mappedfile, xi, pathbuf), - strcmp(pathbuf, querydir))) { - fprintf(stderr, "%s: pathname '%s' does not exist in index\n" - "%*s(check it is spelled exactly as it is in the " - "index, including\n%*sany leading './')\n", - PNAME, querydir, - (int)(1+sizeof(PNAME)), "", - (int)(1+sizeof(PNAME)), ""); - } else if (!index_has_root(mappedfile, xi)) { - fprintf(stderr, "%s: pathname '%s' is" - " a file, not a directory\n", PNAME, querydir); - } else if (!gotdepth) { + if (!querydir || !gotdepth) { /* * Single output file. */ - cfg.format = NULL; - cfg.rootpage = NULL; + if (!querydir) { + cfg.uriformat = "/%|/%p/%|%|/%p"; + } else { + cfg.uriformat = NULL; + } + cfg.autoage = htmlautoagerange; + cfg.oldest = htmloldest; + cfg.newest = htmlnewest; + cfg.showfiles = showfiles; + } else { + cfg.uriformat = "/index.html%|/%/p.html"; + cfg.fileformat = "/index.html%|/%/p.html"; cfg.autoage = htmlautoagerange; cfg.oldest = htmloldest; cfg.newest = htmlnewest; cfg.showfiles = showfiles; - html = html_query(mappedfile, xi, &cfg, 0); - if (outfile != NULL) { + } + + if (!querydir) { + /* + * If we're run in --cgi mode, read PATH_INFO to get + * a numeric pathname index. + */ + char *path_info = getenv("PATH_INFO"); + + if (!path_info) + path_info = ""; + + /* + * Parse the path. + */ + if (!html_parse_path(mappedfile, path_info, &cfg, &xi)) { + printf("Status: 404\nContent-type: text/html\n\n" + "" + "404 Not Found" + "" + "

400 Not Found

" + "

Invalid agedu pathname." + "\n"); + return 0; + } + + /* + * If the path was parseable but not canonically + * expressed, return a redirect to the canonical + * version. + */ + char *canonpath = html_format_path(mappedfile, &cfg, xi); + if (strcmp(canonpath, path_info)) { + char *servername = getenv("SERVER_NAME"); + char *scriptname = getenv("SCRIPT_NAME"); + if (!servername || !scriptname) { + if (servername) + fprintf(stderr, "%s: SCRIPT_NAME unset\n", PNAME); + else if (scriptname) + fprintf(stderr, "%s: SCRIPT_NAME unset\n", PNAME); + else + fprintf(stderr, "%s: SERVER_NAME and " + "SCRIPT_NAME both unset\n", PNAME); + printf("Status: 500\nContent-type: text/html\n\n" + "" + "500 Internal Server Error" + "" + "

500 Internal Server Error

" + "

agedu suffered an internal " + "error." + "\n"); + return 0; + } + printf("Status: 301\n" + "Location: http://%s/%s%s\n" + "Content-type: text/html\n\n" + "" + "301 Moved" + "" + "

301 Moved

" + "

Moved." + "\n", + servername, scriptname, canonpath); + return 0; + } + + } else { + /* + * In ordinary --html mode, process a query + * directory passed in on the command line. + */ + + /* + * Trim trailing slash, just in case. + */ + pathlen = strlen(querydir); + if (pathlen > 0 && querydir[pathlen-1] == pathsep) + querydir[--pathlen] = '\0'; + + xi = trie_before(mappedfile, querydir); + if (xi >= trie_count(mappedfile) || + (trie_getpath(mappedfile, xi, pathbuf), + strcmp(pathbuf, querydir))) { + fprintf(stderr, "%s: pathname '%s' does not exist in index\n" + "%*s(check it is spelled exactly as it is in the " + "index, including\n%*sany leading './')\n", + PNAME, querydir, + (int)(1+sizeof(PNAME)), "", + (int)(1+sizeof(PNAME)), ""); + return 1; + } else if (!index_has_root(mappedfile, xi)) { + fprintf(stderr, "%s: pathname '%s' is" + " a file, not a directory\n", PNAME, querydir); + return 1; + } + } + + if (!querydir || !gotdepth) { + /* + * Single output file. + */ + html = html_query(mappedfile, xi, &cfg, 1); + if (querydir && outfile != NULL) { FILE *fp = fopen(outfile, "w"); if (!fp) { fprintf(stderr, "%s: %s: open: %s\n", PNAME, @@ -1361,6 +1493,9 @@ int main(int argc, char **argv) return 1; } } else { + if (!querydir) { + printf("Content-type: text/html\n\n"); + } fputs(html, stdout); } } else { @@ -1380,15 +1515,15 @@ int main(int argc, char **argv) snprintf(prefix, dirlen, "./"); unsigned long xi2; + /* + * pathbuf is only set up in the plain-HTML case and + * not in the CGI case; but that's OK, because the + * CGI case can't come to this branch of the if + * anyway. + */ make_successor(pathbuf); xi2 = trie_before(mappedfile, pathbuf); - cfg.format = "%lu.html"; - cfg.rootpage = "index.html"; - cfg.autoage = htmlautoagerange; - cfg.oldest = htmloldest; - cfg.newest = htmlnewest; - cfg.showfiles = showfiles; if (html_dump(mappedfile, xi, xi2, depth, &cfg, prefix)) return 1; } @@ -1451,9 +1586,9 @@ int main(int argc, char **argv) dcfg.address = httpserveraddr; dcfg.port = httpserverport; + dcfg.closeoneof = closeoneof; dcfg.basicauthdata = httpauthdata; - pcfg.format = NULL; - pcfg.rootpage = NULL; + pcfg.uriformat = "/%|/%p/%|%|/%p"; pcfg.autoage = htmlautoagerange; pcfg.oldest = htmloldest; pcfg.newest = htmlnewest;