+ /*
+ * If prevbuf was a directory name and buf is
+ * something inside that directory, then
+ * trie_before() will be called on prevbuf
+ * itself. Hence we must drop a tag before it,
+ * so that the resulting index is usable.
+ */
+ if ((!prevbuf[i] && (buf[i] == pathsep ||
+ (i > 0 && buf[i-1] == pathsep))))
+ indexbuild_tag(ib);
+
+ /*
+ * Add prevtf to the index.
+ */
+ indexbuild_add(ib, prevtf);
+
+ if (!tf) {
+ /*
+ * Drop an unconditional final tag, and
+ * get out of this loop.
+ */
+ indexbuild_tag(ib);
+ break;
+ }
+
+ /*
+ * If prevbuf was a filename inside some
+ * directory which buf is outside, then
+ * trie_before() will be called on some
+ * pathname either equal to buf or epsilon
+ * less than it. Either way, we're going to
+ * need to drop a tag after prevtf.
+ */
+ if (strchr(prevbuf+i, pathsep) || !tf)
+ indexbuild_tag(ib);
+ }
+
+ triewalk_free(tw);
+ realsize = indexbuild_realsize(ib);
+ indexbuild_free(ib);
+
+ munmap(mappedfile, totalsize);
+ if (ftruncate(fd, realsize) < 0)
+ fatal("%s: truncate: %s\n", filename, strerror(errno));
+ close(fd);
+ printf("Final index file size = %llu bytes\n",
+ (unsigned long long)realsize);
+ }
+ } else if (mode == TEXT) {
+ char *querydir = actions[action].arg;
+ size_t pathlen;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "%s: %s: open: %s\n", PNAME, filename,
+ strerror(errno));
+ return 1;
+ }
+ if (fstat(fd, &st) < 0) {
+ perror(PNAME ": fstat");
+ return 1;
+ }
+ totalsize = st.st_size;
+ mappedfile = mmap(NULL, totalsize, PROT_READ, MAP_SHARED, fd, 0);
+ if (!mappedfile) {
+ perror(PNAME ": mmap");
+ return 1;
+ }
+ if (!trie_check_magic(mappedfile)) {
+ fprintf(stderr, "%s: %s: magic numbers did not match\n"
+ "%s: check that the index was built by this version of agedu on this platform\n", PNAME, filename, PNAME);
+ return 1;
+ }
+ pathsep = trie_pathsep(mappedfile);
+
+ /*
+ * Trim trailing slash, just in case.
+ */
+ pathlen = strlen(querydir);
+ if (pathlen > 0 && querydir[pathlen-1] == pathsep)
+ querydir[--pathlen] = '\0';
+
+ if (!gotdepth)
+ depth = 1; /* default for text mode */
+ if (outfile != NULL) {
+ FILE *fp = fopen(outfile, "w");
+ if (!fp) {
+ fprintf(stderr, "%s: %s: open: %s\n", PNAME,
+ outfile, strerror(errno));
+ return 1;
+ }
+ text_query(mappedfile, querydir, textcutoff, showfiles,
+ depth, fp);
+ fclose(fp);
+ } else {
+ text_query(mappedfile, querydir, textcutoff, showfiles,
+ depth, stdout);
+ }
+
+ munmap(mappedfile, totalsize);
+ } else if (mode == HTML) {
+ char *querydir = actions[action].arg;
+ size_t pathlen, maxpathlen;
+ char *pathbuf;
+ struct html_config cfg;
+ unsigned long xi;
+ char *html;
+
+ fd = open(filename, O_RDONLY);
+ 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"
+ "<html><head>"
+ "<title>500 Internal Server Error</title>"
+ "</head><body>"
+ "<h1>500 Internal Server Error</h1>"
+ "<p><code>agedu</code> suffered an internal error."
+ "</body></html>\n");
+ return 0;
+ }
+ return 1;
+ }
+ if (fstat(fd, &st) < 0) {
+ fprintf(stderr, "%s: %s: fstat: %s\n", PNAME, filename,
+ strerror(errno));
+ if (!querydir) {
+ printf("Status: 500\nContent-type: text/html\n\n"
+ "<html><head>"
+ "<title>500 Internal Server Error</title>"
+ "</head><body>"
+ "<h1>500 Internal Server Error</h1>"
+ "<p><code>agedu</code> suffered an internal error."
+ "</body></html>\n");
+ return 0;
+ }
+ return 1;
+ }
+ totalsize = st.st_size;
+ mappedfile = mmap(NULL, totalsize, PROT_READ, MAP_SHARED, fd, 0);
+ if (!mappedfile) {
+ fprintf(stderr, "%s: %s: mmap: %s\n", PNAME, filename,
+ strerror(errno));
+ if (!querydir) {
+ printf("Status: 500\nContent-type: text/html\n\n"
+ "<html><head>"
+ "<title>500 Internal Server Error</title>"
+ "</head><body>"
+ "<h1>500 Internal Server Error</h1>"
+ "<p><code>agedu</code> suffered an internal error."
+ "</body></html>\n");
+ return 0;
+ }
+ return 1;
+ }
+ if (!trie_check_magic(mappedfile)) {
+ fprintf(stderr, "%s: %s: magic numbers did not match\n"
+ "%s: check that the index was built by this version of agedu on this platform\n", PNAME, filename, PNAME);
+ if (!querydir) {
+ printf("Status: 500\nContent-type: text/html\n\n"
+ "<html><head>"
+ "<title>500 Internal Server Error</title>"
+ "</head><body>"
+ "<h1>500 Internal Server Error</h1>"
+ "<p><code>agedu</code> suffered an internal error."
+ "</body></html>\n");
+ return 0;
+ }
+ return 1;
+ }
+ pathsep = trie_pathsep(mappedfile);
+
+ maxpathlen = trie_maxpathlen(mappedfile);
+ pathbuf = snewn(maxpathlen, char);
+
+ if (!querydir || !gotdepth) {
+ /*
+ * Single output file.
+ */
+ 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;
+ }
+ cfg.html_title = html_title;
+
+ 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"
+ "<html><head>"
+ "<title>404 Not Found</title>"
+ "</head><body>"
+ "<h1>400 Not Found</h1>"
+ "<p>Invalid <code>agedu</code> pathname."
+ "</body></html>\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"
+ "<html><head>"
+ "<title>500 Internal Server Error</title>"
+ "</head><body>"
+ "<h1>500 Internal Server Error</h1>"
+ "<p><code>agedu</code> suffered an internal "
+ "error."
+ "</body></html>\n");
+ return 0;
+ }
+ printf("Status: 301\n"
+ "Location: http://%s/%s%s\n"
+ "Content-type: text/html\n\n"
+ "<html><head>"
+ "<title>301 Moved</title>"
+ "</head><body>"
+ "<h1>301 Moved</h1>"
+ "<p>Moved."
+ "</body></html>\n",
+ servername, scriptname, canonpath);
+ return 0;
+ }