mirror of https://github.com/nealey/eris.git
start CGI (does not compile)
This commit is contained in:
parent
ae8112f5c5
commit
c5550f134b
2
Makefile
2
Makefile
|
@ -4,7 +4,7 @@ CFLAGS = -DFNORD='"eris/$(VERSION)"' -Wall -Werror
|
||||||
|
|
||||||
all: eris
|
all: eris
|
||||||
|
|
||||||
eris: eris.c strings.c mime.c time.c
|
eris: eris.c strings.c mime.c time.c cgi.c
|
||||||
$(CC) $(CFLAGS) -o $@ $<
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
test: eris
|
test: eris
|
||||||
|
|
|
@ -0,0 +1,179 @@
|
||||||
|
void
|
||||||
|
sigchld(int sig)
|
||||||
|
{
|
||||||
|
while (waitpid(0, NULL, WNOHANG) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cgi_child(const char *relpath)
|
||||||
|
{
|
||||||
|
setenv("GATEWAY_INTERFACE", "CGI/1.1", 1);
|
||||||
|
setenv("SERVER_SOFTWARE", FNORD, 1);
|
||||||
|
setenv("REQUEST_URI", path, 1);
|
||||||
|
setenv("SERVER_NAME", host, 1);
|
||||||
|
setenv("SCRIPT_NAME", relpath, 1);
|
||||||
|
setenv("REMOTE_ADDR", remote_ip, 1);
|
||||||
|
setenv("REMOTE_PORT", remote_port, 1);
|
||||||
|
setenv("REMOTE_IDENT", remote_ident, 1);
|
||||||
|
setenv("CONTENT_TYPE", content_type, 1);
|
||||||
|
{
|
||||||
|
char cl[20];
|
||||||
|
|
||||||
|
snprintf(cl, sizeof cl, "%llu", (unsigned long long) content_length);
|
||||||
|
setenv("CONTENT_LENGTH", cl, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
execl(relpath, relpath, NULL);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cgi_parent(int cin, int cout)
|
||||||
|
{
|
||||||
|
FILE *cinf = fdopen(cin, "rb");
|
||||||
|
int passthru = nph;
|
||||||
|
|
||||||
|
fcntl(child_in, F_SETFL, O_NONBLOCK);
|
||||||
|
signal(SIGCHLD, sigchld);
|
||||||
|
signal(SIGPIPE, SIG_IGN); /* NO! no signal! */
|
||||||
|
|
||||||
|
/* Eris is not this smart yet */
|
||||||
|
keepalive = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int nfds;
|
||||||
|
fd_set rfds, wfds;
|
||||||
|
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_ZERO(&wfds);
|
||||||
|
FD_SET(cin[0], &rfds);
|
||||||
|
nfds = cin[0];
|
||||||
|
|
||||||
|
if (post_len) {
|
||||||
|
/* have post data */
|
||||||
|
FD_SET(cout[1], &wfds);
|
||||||
|
if (cout[1] > nfds) {
|
||||||
|
nfds = cout[1];
|
||||||
|
}
|
||||||
|
} else if (cout[1] >= 0) {
|
||||||
|
close(cout[1]); /* no post data */
|
||||||
|
cout[1] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-1 == select(nfds+1, &rfds, &wfds, NULL, NULL)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FD_ISSET(cin[0], &rfds)) {
|
||||||
|
if (passthru) {
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
/* Re-use this big buffer */
|
||||||
|
len = fread(cgiheader, 1, sizeof cgiheader, cinf);
|
||||||
|
if (0 == len) {
|
||||||
|
/* CGI is done */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fwrite(cgiheader, 1, len, stdout);
|
||||||
|
size += len;
|
||||||
|
} else {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = read_header(cinf, cgiheader, &cgiheaderlen);
|
||||||
|
if (0 == ret) {
|
||||||
|
/* Call read_header again */
|
||||||
|
} else if (-1 == ret) {
|
||||||
|
/* EOF or error */
|
||||||
|
badrequest(500, "CGI Error",
|
||||||
|
"CGI output too weird");
|
||||||
|
} else {
|
||||||
|
/* Entire header is in memory now */
|
||||||
|
passthru = 1;
|
||||||
|
|
||||||
|
/* XXX: I think we need to look for Location:
|
||||||
|
* anywhere, but fnord got away with checking
|
||||||
|
* only the first header field, so I will too.
|
||||||
|
*/
|
||||||
|
if (memcmp(cgiheader, "Location: ", 10) == 0) {
|
||||||
|
retcode = 302;
|
||||||
|
printf
|
||||||
|
("HTTP/1.0 302 CGI-Redirect\r\nConnection: close\r\n");
|
||||||
|
fwrite(cgiheader, 1, cgiheaderlen, stdout);
|
||||||
|
dolog(0);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
retcode = 200;
|
||||||
|
printf("HTTP/1.0 200 OK\r\nServer: "
|
||||||
|
FNORD
|
||||||
|
"\r\nPragma: no-cache\r\nConnection: close\r\n");
|
||||||
|
signal(SIGCHLD, SIG_IGN);
|
||||||
|
fwrite(cgiheader, 1, cgiheaderlen, stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (FD_ISSET(cout[1], &wfds)) {
|
||||||
|
/*
|
||||||
|
* write to cgi the post data
|
||||||
|
*/
|
||||||
|
if (post_len) {
|
||||||
|
size_t len;
|
||||||
|
char buf[BUFFER_SIZE];
|
||||||
|
size_t nmemb = min(BUFFER_SIZE, post_len);
|
||||||
|
|
||||||
|
len = fread(buf, 1, nmemb, stdin);
|
||||||
|
if (len < 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
post_len -= len;
|
||||||
|
write(cout[1], buf, len);
|
||||||
|
} else {
|
||||||
|
close(cout[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
alarm(0);
|
||||||
|
|
||||||
|
fflush(stdout);
|
||||||
|
dolog(size);
|
||||||
|
cork(0);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
serve_cgi(char *relpath)
|
||||||
|
{
|
||||||
|
size_t size = 0;
|
||||||
|
int pid;
|
||||||
|
char buf[BUFFER_SIZE];
|
||||||
|
int cin[2];
|
||||||
|
int cout[2];
|
||||||
|
|
||||||
|
if (pipe(cin) || pipe(cout)) {
|
||||||
|
badrequest(500, "Internal Server Error", "Server Resource problem.");
|
||||||
|
}
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
if (-1 == pid) {
|
||||||
|
badrequest(500, "Internal Server Error", "Unable to fork.");
|
||||||
|
}
|
||||||
|
if (pid) {
|
||||||
|
close(cin[1]);
|
||||||
|
close(cout[0]);
|
||||||
|
|
||||||
|
alarm(CHILD_TIMEOUT);
|
||||||
|
cgi_parent(cin[0], cout[1]);
|
||||||
|
alarm(0);
|
||||||
|
} else {
|
||||||
|
close(cwd);
|
||||||
|
close(cout[1]);
|
||||||
|
close(cin[0]);
|
||||||
|
|
||||||
|
dup2(cout[0], 0);
|
||||||
|
dup2(cin[1], 1);
|
||||||
|
|
||||||
|
close(cout[0]);
|
||||||
|
close(cin[1]);
|
||||||
|
|
||||||
|
cgi_child(relpath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
43
eris.c
43
eris.c
|
@ -45,10 +45,6 @@
|
||||||
#define DUMP_p(v) DUMPf("%s = %p", #v, v)
|
#define DUMP_p(v) DUMPf("%s = %p", #v, v)
|
||||||
#define DUMP_buf(v, l) DUMPf("%s = %.*s", #v, (int)(l), v)
|
#define DUMP_buf(v, l) DUMPf("%s = %.*s", #v, (int)(l), v)
|
||||||
|
|
||||||
#include "strings.c"
|
|
||||||
#include "mime.c"
|
|
||||||
#include "time.c"
|
|
||||||
|
|
||||||
/* Wait this long (seconds) for a valid HTTP request */
|
/* Wait this long (seconds) for a valid HTTP request */
|
||||||
#define READTIMEOUT 2
|
#define READTIMEOUT 2
|
||||||
|
|
||||||
|
@ -77,6 +73,7 @@ int redirect = 0;
|
||||||
int portappend = 0;
|
int portappend = 0;
|
||||||
|
|
||||||
/* Variables that persist between requests */
|
/* Variables that persist between requests */
|
||||||
|
int cwd;
|
||||||
int keepalive = 0;
|
int keepalive = 0;
|
||||||
char *remote_ip = NULL;
|
char *remote_ip = NULL;
|
||||||
char *remote_port = NULL;
|
char *remote_port = NULL;
|
||||||
|
@ -93,8 +90,8 @@ char *user_agent;
|
||||||
char *refer;
|
char *refer;
|
||||||
char *path;
|
char *path;
|
||||||
int http_version;
|
int http_version;
|
||||||
|
char *content_type;
|
||||||
size_t content_length;
|
size_t content_length;
|
||||||
char *query_string = NULL;
|
|
||||||
off_t range_start, range_end;
|
off_t range_start, range_end;
|
||||||
time_t ims = 0;
|
time_t ims = 0;
|
||||||
|
|
||||||
|
@ -102,6 +99,11 @@ time_t ims = 0;
|
||||||
#define BUFFER_SIZE 8192
|
#define BUFFER_SIZE 8192
|
||||||
char stdout_buf[BUFFER_SIZE];
|
char stdout_buf[BUFFER_SIZE];
|
||||||
|
|
||||||
|
#include "strings.c"
|
||||||
|
#include "mime.c"
|
||||||
|
#include "time.c"
|
||||||
|
#include "cgi.c"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TCP_CORK is a Linux extension to work around a TCP problem.
|
* TCP_CORK is a Linux extension to work around a TCP problem.
|
||||||
* http://www.baus.net/on-tcp_cork has a good description.
|
* http://www.baus.net/on-tcp_cork has a good description.
|
||||||
|
@ -496,6 +498,7 @@ handle_request()
|
||||||
path = NULL;
|
path = NULL;
|
||||||
range_start = 0;
|
range_start = 0;
|
||||||
range_end = 0;
|
range_end = 0;
|
||||||
|
content_type = NULL;
|
||||||
content_length = 0;
|
content_length = 0;
|
||||||
|
|
||||||
alarm(READTIMEOUT);
|
alarm(READTIMEOUT);
|
||||||
|
@ -520,10 +523,16 @@ handle_request()
|
||||||
badrequest(405, "Method Not Allowed", "Unsupported HTTP method.");
|
badrequest(405, "Method Not Allowed", "Unsupported HTTP method.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (docgi) {
|
||||||
|
p[-2] = 0;
|
||||||
|
setenv("REQUEST_METHOD", p, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/* Interpret path into fspath. */
|
/* Interpret path into fspath. */
|
||||||
path = p - 1;
|
path = p - 1;
|
||||||
{
|
{
|
||||||
FILE *f = fmemopen(fspath, sizeof fspath, "w");
|
FILE *f = fmemopen(fspath, sizeof fspath, "w");
|
||||||
|
char *query_string = NULL;
|
||||||
|
|
||||||
fprintf(f, "./");
|
fprintf(f, "./");
|
||||||
for (; *p != ' '; p += 1) {
|
for (; *p != ' '; p += 1) {
|
||||||
|
@ -561,8 +570,13 @@ handle_request()
|
||||||
}
|
}
|
||||||
fputc(0, f);
|
fputc(0, f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
|
*(p++) = 0; /* NULL-terminate path */
|
||||||
|
|
||||||
|
if (docgi && query_string) {
|
||||||
|
setenv("QUERY_STRING", query_string, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*(p++) = 0; /* NULL-terminate path */
|
|
||||||
|
|
||||||
http_version = -1;
|
http_version = -1;
|
||||||
if (! strncmp(p, "HTTP/1.", 7) && p[8] && ((p[8] == '\r') || (p[8] == '\n'))) {
|
if (! strncmp(p, "HTTP/1.", 7) && p[8] && ((p[8] == '\r') || (p[8] == '\n'))) {
|
||||||
|
@ -577,6 +591,9 @@ handle_request()
|
||||||
} else {
|
} else {
|
||||||
keepalive = 0;
|
keepalive = 0;
|
||||||
}
|
}
|
||||||
|
if (docgi) {
|
||||||
|
setenv("SERVER_PROTOCOL", p, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/* Read header fields */
|
/* Read header fields */
|
||||||
{
|
{
|
||||||
|
@ -617,7 +634,9 @@ handle_request()
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set up CGI environment variables */
|
/* Set up CGI environment variables */
|
||||||
setenv(cgi_name, val, 1);
|
if (docgi) {
|
||||||
|
setenv(cgi_name, val, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/* By default, re-use buffer space */
|
/* By default, re-use buffer space */
|
||||||
base = cgi_name;
|
base = cgi_name;
|
||||||
|
@ -632,6 +651,11 @@ handle_request()
|
||||||
} else if (! strcmp(name, "REFERER")) {
|
} else if (! strcmp(name, "REFERER")) {
|
||||||
refer = val;
|
refer = val;
|
||||||
base = name + len + 1;
|
base = name + len + 1;
|
||||||
|
} else if (! strcmp(name, "CONTENT_TYPE")) {
|
||||||
|
content_type = val;
|
||||||
|
base = name + len + 1;
|
||||||
|
} else if (! strcmp(name, "CONTENT_LENGTH")) {
|
||||||
|
content_length = (size_t) strtoull(val, NULL, 10);
|
||||||
} else if (! strcmp(name, "CONNECTION")) {
|
} else if (! strcmp(name, "CONNECTION")) {
|
||||||
if (! strcasecmp(val, "keep-alive")) {
|
if (! strcasecmp(val, "keep-alive")) {
|
||||||
keepalive = 1;
|
keepalive = 1;
|
||||||
|
@ -640,8 +664,6 @@ handle_request()
|
||||||
}
|
}
|
||||||
} else if (! strcmp(name, "IF_MODIFIED_SINCE")) {
|
} else if (! strcmp(name, "IF_MODIFIED_SINCE")) {
|
||||||
ims = timerfc(val);
|
ims = timerfc(val);
|
||||||
} else if (! strcmp(name, "CONTENT_LENGTH")) {
|
|
||||||
content_length = (size_t) strtoull(val, NULL, 10);
|
|
||||||
} else if (! strcmp(name, "RANGE")) {
|
} else if (! strcmp(name, "RANGE")) {
|
||||||
/* Range: bytes=17-23 */
|
/* Range: bytes=17-23 */
|
||||||
/* Range: bytes=23- */
|
/* Range: bytes=23- */
|
||||||
|
@ -700,9 +722,10 @@ handle_request()
|
||||||
int
|
int
|
||||||
main(int argc, char *argv[], const char *const *envp)
|
main(int argc, char *argv[], const char *const *envp)
|
||||||
{
|
{
|
||||||
int cwd = open(".", O_RDONLY);
|
|
||||||
parse_options(argc, argv);
|
parse_options(argc, argv);
|
||||||
|
|
||||||
|
cwd = open(".", O_RDONLY);
|
||||||
|
|
||||||
setbuffer(stdout, stdout_buf, sizeof stdout_buf);
|
setbuffer(stdout, stdout_buf, sizeof stdout_buf);
|
||||||
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
static struct mimeentry {
|
||||||
|
const char *name,
|
||||||
|
*type;
|
||||||
|
} mimetab[] = {
|
||||||
|
{
|
||||||
|
"html", "text/html; charset=UTF-8"}, {
|
||||||
|
"htm", "text/html; charset=UTF-8"}, {
|
||||||
|
"txt", "text/plain; charset=UTF-8"}, {
|
||||||
|
"css", "text/css"}, {
|
||||||
|
"ps", "application/postscript"}, {
|
||||||
|
"pdf", "application/pdf"}, {
|
||||||
|
"js", "application/javascript"}, {
|
||||||
|
"gif", "image/gif"}, {
|
||||||
|
"png", "image/png"}, {
|
||||||
|
"jpeg", "image/jpeg"}, {
|
||||||
|
"jpg", "image/jpeg"}, {
|
||||||
|
"svg", "image/svg+xml"}, {
|
||||||
|
"mpeg", "video/mpeg"}, {
|
||||||
|
"mpg", "video/mpeg"}, {
|
||||||
|
"avi", "video/x-msvideo"}, {
|
||||||
|
"mov", "video/quicktime"}, {
|
||||||
|
"qt", "video/quicktime"}, {
|
||||||
|
"mp3", "audio/mpeg"}, {
|
||||||
|
"ogg", "audio/ogg"}, {
|
||||||
|
"wav", "audio/x-wav"}, {
|
||||||
|
"epub", "application/epub+zip"}, {
|
||||||
|
"dvi", "application/x-dvi"}, {
|
||||||
|
"pac", "application/x-ns-proxy-autoconfig"}, {
|
||||||
|
"sig", "application/pgp-signature"}, {
|
||||||
|
"swf", "application/x-shockwave-flash"}, {
|
||||||
|
"torrent", "application/x-bittorrent"}, {
|
||||||
|
"tar", "application/x-tar"}, {
|
||||||
|
"zip", "application/zip"}, {
|
||||||
|
"dtd", "text/xml"}, {
|
||||||
|
"xml", "text/xml"}, {
|
||||||
|
"xbm", "image/x-xbitmap"}, {
|
||||||
|
"xpm", "image/x-xpixmap"}, {
|
||||||
|
"xwd", "image/x-xwindowdump"}, {
|
||||||
|
"ico", "image/x-icon"}, {
|
||||||
|
0, 0}};
|
||||||
|
|
||||||
|
static const char *default_mimetype = "application/octet-stream";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine MIME type from file extension
|
||||||
|
*/
|
||||||
|
static const char *
|
||||||
|
getmimetype(char *url)
|
||||||
|
{
|
||||||
|
char *ext = strrchr(url, '.');
|
||||||
|
|
||||||
|
|
||||||
|
if (ext) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ext++;
|
||||||
|
for (i = 0; mimetab[i].name; ++i) {
|
||||||
|
if (!strcmp(mimetab[i].name, ext)) {
|
||||||
|
return mimetab[i].type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return default_mimetype;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
int
|
||||||
|
endswith(char *haystack, char *needle)
|
||||||
|
{
|
||||||
|
char *h, *n;
|
||||||
|
|
||||||
|
for (h = haystack; *h; h++);
|
||||||
|
for (n = needle; *n; n++);
|
||||||
|
|
||||||
|
if (h - haystack < n - needle) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (n >= needle) {
|
||||||
|
if (*(n--) != *(h--)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Replace whitespace with underscores for logging */
|
||||||
|
static void
|
||||||
|
sanitize(char *s)
|
||||||
|
{
|
||||||
|
if (!s) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (; *s; s += 1) {
|
||||||
|
if (isspace(*s)) {
|
||||||
|
*s = '_';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parse a header out of a line.
|
||||||
|
*
|
||||||
|
* This capitalizes the header name, and strips trailing [\r\n]
|
||||||
|
* Returns the length of the line (after stripping)
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
extract_header_field(char *buf, char **val, int cgi)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
*val = NULL;
|
||||||
|
|
||||||
|
for (len = 0; buf[len]; len += 1) {
|
||||||
|
if (! *val) {
|
||||||
|
if (buf[len] == '\n') {
|
||||||
|
/* Blank line or incorrectly-formatted header */
|
||||||
|
return 0;
|
||||||
|
} else if (buf[len] == ':') {
|
||||||
|
buf[len] = 0;
|
||||||
|
for (*val = &(buf[len+1]); **val == ' '; *val += 1);
|
||||||
|
} else if (cgi) {
|
||||||
|
switch (buf[len]) {
|
||||||
|
case 'a'...'z':
|
||||||
|
buf[len] ^= ' ';
|
||||||
|
break;
|
||||||
|
case 'A'...'Z':
|
||||||
|
case '0'...'9':
|
||||||
|
case '\r':
|
||||||
|
case '\n':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
buf[len] = '_';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; (buf[len-1] == '\n') || (buf[len-1] == '\r'); len -= 1);
|
||||||
|
buf[len] = 0;
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
fromhex(int c)
|
||||||
|
{
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
return c - '0';
|
||||||
|
else {
|
||||||
|
c |= ' ';
|
||||||
|
if (c >= 'a' && c <= 'f')
|
||||||
|
return c - 'a' + 10;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
html_esc(FILE *f, char *s)
|
||||||
|
{
|
||||||
|
for (; *s; s += 1) {
|
||||||
|
switch (*s) {
|
||||||
|
case '<':
|
||||||
|
fprintf(f, "<");
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
fprintf(f, ">");
|
||||||
|
break;
|
||||||
|
case '&':
|
||||||
|
fprintf(f, "&");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fputc(*s, f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
url_esc(FILE *f, char *s)
|
||||||
|
{
|
||||||
|
for (; *s; s += 1) {
|
||||||
|
switch (*s) {
|
||||||
|
case '%':
|
||||||
|
case 127:
|
||||||
|
case -127 ... 31:
|
||||||
|
fprintf(f, "%%%02x", *s);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fputc(*s, f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,202 @@
|
||||||
|
/*
|
||||||
|
* Code in this file is from mathopd <http://www.mathopd.org/>
|
||||||
|
*
|
||||||
|
* Copyright 1996, Michiel Boland.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or
|
||||||
|
* without modification, are permitted provided that the following
|
||||||
|
* conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above
|
||||||
|
* copyright notice, this list of conditions and the following
|
||||||
|
* disclaimer in the documentation and/or other materials
|
||||||
|
* provided with the distribution.
|
||||||
|
*
|
||||||
|
* 3. The name of the author may not be used to endorse or promote
|
||||||
|
* products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
|
||||||
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||||
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const char days[] = "SunMonTueWedThuFriSat";
|
||||||
|
static const char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
|
||||||
|
|
||||||
|
static time_t
|
||||||
|
timerfc(const char *s)
|
||||||
|
{
|
||||||
|
static const int daytab[2][12] = {
|
||||||
|
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
|
||||||
|
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
|
||||||
|
};
|
||||||
|
unsigned sec,
|
||||||
|
min,
|
||||||
|
hour,
|
||||||
|
day,
|
||||||
|
mon,
|
||||||
|
year;
|
||||||
|
char month[3];
|
||||||
|
int c;
|
||||||
|
unsigned n;
|
||||||
|
char flag;
|
||||||
|
char state;
|
||||||
|
char isctime;
|
||||||
|
enum { D_START, D_END, D_MON, D_DAY, D_YEAR, D_HOUR, D_MIN, D_SEC };
|
||||||
|
|
||||||
|
sec = 60;
|
||||||
|
min = 60;
|
||||||
|
hour = 24;
|
||||||
|
day = 32;
|
||||||
|
year = 1969;
|
||||||
|
isctime = 0;
|
||||||
|
month[0] = 0;
|
||||||
|
state = D_START;
|
||||||
|
n = 0;
|
||||||
|
flag = 1;
|
||||||
|
do {
|
||||||
|
c = *s++;
|
||||||
|
switch (state) {
|
||||||
|
case D_START:
|
||||||
|
if (c == ' ') {
|
||||||
|
state = D_MON;
|
||||||
|
isctime = 1;
|
||||||
|
} else if (c == ',')
|
||||||
|
state = D_DAY;
|
||||||
|
break;
|
||||||
|
case D_MON:
|
||||||
|
if (isalpha(c)) {
|
||||||
|
if (n < 3)
|
||||||
|
month[n++] = c;
|
||||||
|
} else {
|
||||||
|
if (n < 3)
|
||||||
|
return -1;
|
||||||
|
n = 0;
|
||||||
|
state = isctime ? D_DAY : D_YEAR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case D_DAY:
|
||||||
|
if (c == ' ' && flag);
|
||||||
|
else if (isdigit(c)) {
|
||||||
|
flag = 0;
|
||||||
|
n = 10 * n + (c - '0');
|
||||||
|
} else {
|
||||||
|
day = n;
|
||||||
|
n = 0;
|
||||||
|
state = isctime ? D_HOUR : D_MON;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case D_YEAR:
|
||||||
|
if (isdigit(c))
|
||||||
|
n = 10 * n + (c - '0');
|
||||||
|
else {
|
||||||
|
year = n;
|
||||||
|
n = 0;
|
||||||
|
state = isctime ? D_END : D_HOUR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case D_HOUR:
|
||||||
|
if (isdigit(c))
|
||||||
|
n = 10 * n + (c - '0');
|
||||||
|
else {
|
||||||
|
hour = n;
|
||||||
|
n = 0;
|
||||||
|
state = D_MIN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case D_MIN:
|
||||||
|
if (isdigit(c))
|
||||||
|
n = 10 * n + (c - '0');
|
||||||
|
else {
|
||||||
|
min = n;
|
||||||
|
n = 0;
|
||||||
|
state = D_SEC;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case D_SEC:
|
||||||
|
if (isdigit(c))
|
||||||
|
n = 10 * n + (c - '0');
|
||||||
|
else {
|
||||||
|
sec = n;
|
||||||
|
n = 0;
|
||||||
|
state = isctime ? D_YEAR : D_END;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (state != D_END && c);
|
||||||
|
switch (month[0]) {
|
||||||
|
case 'A':
|
||||||
|
mon = (month[1] == 'p') ? 4 : 8;
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
mon = 12;
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
mon = 2;
|
||||||
|
break;
|
||||||
|
case 'J':
|
||||||
|
mon = (month[1] == 'a') ? 1 : ((month[2] == 'l') ? 7 : 6);
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
mon = (month[2] == 'r') ? 3 : 5;
|
||||||
|
break;
|
||||||
|
case 'N':
|
||||||
|
mon = 11;
|
||||||
|
break;
|
||||||
|
case 'O':
|
||||||
|
mon = 10;
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
mon = 9;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (year <= 100)
|
||||||
|
year += (year < 70) ? 2000 : 1900;
|
||||||
|
--mon;
|
||||||
|
--day;
|
||||||
|
if (sec >= 60 || min >= 60 || hour >= 60 || day >= 31)
|
||||||
|
return -1;
|
||||||
|
if (year < 1970)
|
||||||
|
return 0;
|
||||||
|
return sec + 60L * (min + 60L * (hour + 24L * (day +
|
||||||
|
daytab[year % 4 == 0
|
||||||
|
&& (year % 100
|
||||||
|
|| year %
|
||||||
|
400 ==
|
||||||
|
0)][mon] +
|
||||||
|
365L * (year - 1970L) +
|
||||||
|
((year -
|
||||||
|
1969L) >> 2))));
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
rfctime(time_t t, char *buf)
|
||||||
|
{
|
||||||
|
struct tm *tp;
|
||||||
|
|
||||||
|
/* ntp: in glibc, this triggers a bunch of needless I/O. */
|
||||||
|
tp = gmtime(&t);
|
||||||
|
if (tp == 0) {
|
||||||
|
strcpy(buf, "?");
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
strftime(buf, 31, "%a, %d %b %Y %H:%M:%S GMT", tp);
|
||||||
|
return buf;
|
||||||
|
}
|
Loading…
Reference in New Issue