+ }
+ if (!key)
+ key = "";
+ hash = hash_str(key) % size;
+ strbuf_addstr(&filename, path);
+ strbuf_ensure_end(&filename, '/');
+ for (i = 0; i < 8; i++) {
+ strbuf_addf(&filename, "%x", (unsigned char)(hash & 0xf));
+ hash >>= 4;
+ }
+ strbuf_addbuf(&lockname, &filename);
+ strbuf_addstr(&lockname, ".lock");
+ slot.fn = fn;
+ slot.ttl = ttl;
+ slot.stdout_fd = -1;
+ slot.cache_name = filename.buf;
+ slot.lock_name = lockname.buf;
+ slot.key = key;
+ slot.keylen = strlen(key);
+ result = process_slot(&slot);
+
+ strbuf_release(&filename);
+ strbuf_release(&lockname);
+ return result;
+}
+
+/* Return a strftime formatted date/time
+ * NB: the result from this function is to shared memory
+ */
+static char *sprintftime(const char *format, time_t time)
+{
+ static char buf[64];
+ struct tm *tm;
+
+ if (!time)
+ return NULL;
+ tm = gmtime(&time);
+ strftime(buf, sizeof(buf)-1, format, tm);
+ return buf;
+}
+
+int cache_ls(const char *path)
+{
+ DIR *dir;
+ struct dirent *ent;
+ int err = 0;
+ struct cache_slot slot = { NULL };
+ struct strbuf fullname = STRBUF_INIT;
+ size_t prefixlen;
+
+ if (!path) {
+ cache_log("[cgit] cache path not specified\n");
+ return -1;
+ }
+ dir = opendir(path);
+ if (!dir) {
+ err = errno;
+ cache_log("[cgit] unable to open path %s: %s (%d)\n",
+ path, strerror(err), err);
+ return err;
+ }
+ strbuf_addstr(&fullname, path);
+ strbuf_ensure_end(&fullname, '/');
+ prefixlen = fullname.len;
+ while ((ent = readdir(dir)) != NULL) {
+ if (strlen(ent->d_name) != 8)
+ continue;
+ strbuf_setlen(&fullname, prefixlen);
+ strbuf_addstr(&fullname, ent->d_name);
+ slot.cache_name = fullname.buf;
+ if ((err = open_slot(&slot)) != 0) {
+ cache_log("[cgit] unable to open path %s: %s (%d)\n",
+ fullname.buf, strerror(err), err);
+ continue;
+ }
+ htmlf("%s %s %10"PRIuMAX" %s\n",
+ fullname.buf,
+ sprintftime("%Y-%m-%d %H:%M:%S",
+ slot.cache_st.st_mtime),
+ (uintmax_t)slot.cache_st.st_size,
+ slot.buf);
+ close_slot(&slot);
+ }
+ closedir(dir);
+ strbuf_release(&fullname);
+ return 0;