Last active
July 1, 2022 09:05
-
-
Save shawnfeng0/676e5fc4b5156a32324491c0aa10b190 to your computer and use it in GitHub Desktop.
Modified the file index graphical interface to be similar to the nginx interface, Original reference: networking/httpd_indexcgi.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2007 Denys Vlasenko <[email protected]> | |
* | |
* Licensed under GPLv2, see file LICENSE in this source tree. | |
*/ | |
/* | |
* This program is a CGI application. It outputs directory index page. | |
* Put it into cgi-bin/index.cgi and chmod 0755. | |
*/ | |
/* Build a-la | |
i486-linux-uclibc-gcc \ | |
-static -static-libgcc \ | |
-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 \ | |
-Wall -Wshadow -Wwrite-strings -Wundef -Wstrict-prototypes -Werror \ | |
-Wold-style-definition -Wdeclaration-after-statement -Wno-pointer-sign \ | |
-Wmissing-prototypes -Wmissing-declarations \ | |
-Os -fno-builtin-strlen -finline-limit=0 -fomit-frame-pointer \ | |
-ffunction-sections -fdata-sections -fno-guess-branch-probability \ | |
-funsigned-char \ | |
-falign-functions=1 -falign-jumps=1 -falign-labels=1 -falign-loops=1 \ | |
-march=i386 -mpreferred-stack-boundary=2 \ | |
-Wl,-Map -Wl,link.map -Wl,--warn-common -Wl,--sort-common -Wl,--gc-sections \ | |
httpd_indexcgi.c -o index.cgi | |
*/ | |
/* We don't use printf, as it pulls in >12 kb of code from uclibc (i386). */ | |
/* Currently malloc machinery is the biggest part of libc we pull in. */ | |
/* We have only one realloc and one strdup, any idea how to do without? */ | |
/* Size (i386, static uclibc, approximate): | |
* text data bss dec hex filename | |
* 13036 44 3052 16132 3f04 index.cgi | |
* 2576 4 2048 4628 1214 index.cgi.o | |
*/ | |
#define _GNU_SOURCE 1 /* for strchrnul */ | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <errno.h> | |
#include <stdint.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <stdio.h> | |
#include <dirent.h> | |
#include <time.h> | |
/* Appearance of the table is controlled by style sheet *ONLY*, | |
* formatting code uses <TAG class=CLASS> to apply style | |
* to elements. Edit stylesheet to your liking and recompile. */ | |
typedef struct dir_list_t { | |
char *dl_name; | |
mode_t dl_mode; | |
off_t dl_size; | |
time_t dl_mtime; | |
} dir_list_t; | |
static int compare_dl(dir_list_t *a, dir_list_t *b) | |
{ | |
/* ".." is 'less than' any other dir entry */ | |
if (strcmp(a->dl_name, "..") == 0) { | |
return -1; | |
} | |
if (strcmp(b->dl_name, "..") == 0) { | |
return 1; | |
} | |
if (S_ISDIR(a->dl_mode) != S_ISDIR(b->dl_mode)) { | |
/* 1 if b is a dir (and thus a is 'after' b, a > b), | |
* else -1 (a < b) */ | |
return (S_ISDIR(b->dl_mode) != 0) ? 1 : -1; | |
} | |
return strcmp(a->dl_name, b->dl_name); | |
} | |
/* NB: formatters do not store terminating NUL! */ | |
static int fmt_fill_space(unsigned len) | |
{ | |
static const char spaces[] = " "; | |
int ret_len = len; | |
for(;len > sizeof(spaces) - 1; len -= sizeof(spaces) - 1) { | |
write(STDOUT_FILENO, spaces, sizeof(spaces) - 1); | |
} | |
write(STDOUT_FILENO, spaces, len); | |
return ret_len; | |
} | |
static int fmt_str(const char *src) | |
{ | |
unsigned len = strlen(src); | |
write(STDOUT_FILENO, src, len); | |
return len; | |
} | |
static int fmt_char(char c) | |
{ | |
write(STDOUT_FILENO, &c, 1); | |
return 1; | |
} | |
static int fmt_url(/*char *dst,*/ const char *name) | |
{ | |
int ret_len = 0; | |
while (*name) { | |
unsigned c = *name++; | |
if ((c - '0') > 9 /* not a digit */ | |
&& ((c|0x20) - 'a') > ('z' - 'a') /* not A-Z or a-z */ | |
&& !strchr("._-+@", c) | |
) { | |
ret_len += fmt_char('%'); | |
ret_len += fmt_char("0123456789ABCDEF"[c >> 4]); | |
ret_len += fmt_char("0123456789ABCDEF"[c & 0xf]); | |
} else { | |
ret_len += fmt_char(c); | |
} | |
} | |
return ret_len; | |
} | |
/* HEADROOM bytes are available after dst after this call */ | |
static int fmt_html(/*char *dst,*/ const char *name) | |
{ | |
int ret_len = 0; | |
while (*name) { | |
char c = *name++; | |
if (c == '<') | |
ret_len += fmt_str("<"); | |
else if (c == '>') | |
ret_len += fmt_str(">"); | |
else if (c == '&') { | |
ret_len += fmt_str("&"); | |
} else { | |
ret_len += fmt_char(c); | |
continue; | |
} | |
} | |
return ret_len; | |
} | |
/* HEADROOM bytes are available after dst after this call */ | |
static int fmt_ull(/*char *dst,*/ unsigned long long n) | |
{ | |
char buf[sizeof(n)*3 + 2]; | |
char *p; | |
p = buf + sizeof(buf) - 1; | |
*p = '\0'; | |
do { | |
*--p = (n % 10) + '0'; | |
n /= 10; | |
} while (n); | |
return fmt_str(p); | |
} | |
/* Does not call guarantee - eats into headroom instead */ | |
static int fmt_02u(/*char *dst,*/ unsigned n) | |
{ | |
/* n %= 100; - not needed, callers don't pass big n */ | |
fmt_char((n / 10) + '0'); | |
fmt_char((n % 10) + '0'); | |
return 2; | |
} | |
/* Does not call guarantee - eats into headroom instead */ | |
static int fmt_04u(/*char *dst,*/ unsigned n) | |
{ | |
/* n %= 10000; - not needed, callers don't pass big n */ | |
fmt_02u(n / 100); | |
fmt_02u(n % 100); | |
return 4; | |
} | |
int main(int argc, char **argv) | |
{ | |
dir_list_t *dir_list; | |
dir_list_t *cdir; | |
unsigned dir_list_count; | |
unsigned count_dirs; | |
unsigned count_files; | |
unsigned long long size_total; | |
DIR *dirp; | |
char *location; | |
location = getenv("REQUEST_URI"); | |
if (!location) | |
return 1; | |
/* drop URL arguments if any */ | |
strchrnul(location, '?')[0] = '\0'; | |
if (location[0] != '/' | |
|| strstr(location, "//") | |
|| strstr(location, "/../") | |
|| strcmp(strrchr(location, '/'), "/..") == 0 | |
) { | |
return 1; | |
} | |
if (chdir("..") | |
|| (location[1] && chdir(location + 1)) | |
) { | |
return 1; | |
} | |
dirp = opendir("."); | |
if (!dirp) | |
return 1; | |
dir_list = NULL; | |
dir_list_count = 0; | |
while (1) { | |
struct dirent *dp; | |
struct stat sb; | |
dp = readdir(dirp); | |
if (!dp) | |
break; | |
if (dp->d_name[0] == '.' && !dp->d_name[1]) | |
continue; | |
if (stat(dp->d_name, &sb) != 0) | |
continue; | |
dir_list = realloc(dir_list, (dir_list_count + 1) * sizeof(dir_list[0])); | |
dir_list[dir_list_count].dl_name = strdup(dp->d_name); | |
dir_list[dir_list_count].dl_mode = sb.st_mode; | |
dir_list[dir_list_count].dl_size = sb.st_size; | |
dir_list[dir_list_count].dl_mtime = sb.st_mtime; | |
dir_list_count++; | |
} | |
closedir(dirp); | |
qsort(dir_list, dir_list_count, sizeof(dir_list[0]), (void*)compare_dl); | |
fmt_str( | |
"" /* Additional headers (currently none) */ | |
"\r\n" /* Mandatory empty line after headers */ | |
"<html><head><title>Index of "); | |
/* Guard against directories with &, > etc */ | |
fmt_html(location); | |
fmt_str( | |
"</title>\n" | |
"</head>" "\n" | |
"<body>" "\n" | |
"<h1>Index of "); | |
fmt_html(location); | |
fmt_str( | |
"</h1>" "\n" | |
"<hr>" "\n" | |
"<pre>" "\n" | |
); | |
count_dirs = 0; | |
count_files = 0; | |
size_total = 0; | |
cdir = dir_list; | |
while (dir_list_count--) { | |
if (S_ISDIR(cdir->dl_mode)) { | |
count_dirs++; | |
} else if (S_ISREG(cdir->dl_mode)) { | |
count_files++; | |
size_total += cdir->dl_size; | |
} else | |
goto next; | |
fmt_str("<a href='"); | |
fmt_url(cdir->dl_name); /* %20 etc */ | |
if (S_ISDIR(cdir->dl_mode)) | |
fmt_char('/'); | |
fmt_str("'>"); | |
fmt_html(cdir->dl_name); /* < etc */ | |
int cur_text_position = strlen(cdir->dl_name); | |
if (S_ISDIR(cdir->dl_mode)) { | |
fmt_char('/'); | |
cur_text_position += 1; | |
} | |
fmt_str("</a>"); | |
if (strstr(cdir->dl_name, "..")) { | |
fmt_char('\n'); | |
goto next; | |
} | |
// FILENAME_LENGTH_LIMIT refers to the display length of nginx | |
const unsigned FILENAME_LENGTH_LIMIT = 51; | |
if (cur_text_position < FILENAME_LENGTH_LIMIT ) { | |
fmt_fill_space(FILENAME_LENGTH_LIMIT - cur_text_position); | |
} | |
struct tm cur_tm; | |
localtime_r(&cdir->dl_mtime, &cur_tm); | |
fmt_04u(1900 + cur_tm.tm_year); fmt_char('-'); | |
fmt_02u(cur_tm.tm_mon + 1); fmt_char('-'); | |
fmt_02u(cur_tm.tm_mday); fmt_char(' '); | |
fmt_02u(cur_tm.tm_hour); fmt_char(':'); | |
fmt_02u(cur_tm.tm_min); fmt_char(':'); | |
fmt_02u(cur_tm.tm_sec); | |
fmt_str(" "); | |
if (S_ISREG(cdir->dl_mode)) | |
fmt_ull(cdir->dl_size); | |
else | |
fmt_str("-"); | |
fmt_char('\n'); | |
next: | |
cdir++; | |
} | |
fmt_str( | |
"</pre>" "\n" | |
"<hr>" "\n" | |
); | |
fmt_str("Files: "); | |
fmt_ull(count_files); | |
/* count_dirs - 1: we don't want to count ".." */ | |
fmt_str(", directories: "); | |
fmt_ull(count_dirs - 1); | |
fmt_str(", total size: "); | |
fmt_ull(size_total); | |
/* "</body></html>" - why bother? */ | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment