httpd_ssi.c: make it a bit less broken
No binary (build failed)
—
(log yok)
| @@ -31,13 +31,17 @@ httpd_ssi.c -o httpd_ssi | |
| */ | |
| /* Size (i386, static uclibc, approximate): | |
| - | text data bss dec hex filename |
| - | 8931 164 68552 77647 12f4f httpd_ssi |
| - | */ |
| + | * text data bss dec hex filename |
| + | * 9487 160 68552 78199 13177 httpd_ssi |
| + | * |
| + | * Note: it wouldn't be too hard to get rid of stdio and strdup, |
| + | * (especially that fgets() mangles NULs...) |
| + | */ |
| #include <sys/types.h> | |
| #include <sys/stat.h> | |
| #include <errno.h> | |
| + | #include <fcntl.h> |
| #include <stdint.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| @@ -46,40 +50,97 @@ httpd_ssi.c -o httpd_ssi | |
| #include <dirent.h> | |
| #include <time.h> | |
| + | static char* skip_whitespace(char *s) |
| + | { |
| + | while (*s == ' ' || *s == '\t') ++s; |
| + | |
| + | return s; |
| + | } |
| + | |
| static char line[64 * 1024]; | |
| - | /* |
| - | * Currently only handles directives which are alone on the line |
| - | */ |
| static void process_includes(const char *filename) | |
| { | |
| + | int curdir_fd; |
| + | char *end; |
| FILE *fp = fopen(filename, "r"); | |
| if (!fp) | |
| exit(1); | |
| - | #define INCLUDE "<!--#include file=\"" |
| + | /* Ensure that nested includes are relative: |
| + | * if we include a/1.htm and it includes b/2.htm, |
| + | * we need to include a/b/2.htm, not b/2.htm |
| + | */ |
| + | curdir_fd = -1; |
| + | end = strrchr(filename, '/'); |
| + | if (end) { |
| + | curdir_fd = open(".", O_RDONLY); |
| + | /* *end = '\0' would mishandle "/file.htm" */ |
| + | end[1] = '\0'; |
| + | chdir(filename); |
| + | } |
| + | |
| + | #define INCLUDE "<!--#include" |
| while (fgets(line, sizeof(line), fp)) { | |
| - | char *closing_dq; |
| + | unsigned preceding_len; |
| + | char *include_directive; |
| - | /* FIXME: output text leading to INCLUDE first */ |
| - | if (strncmp(line, INCLUDE, sizeof(INCLUDE)-1) != 0 |
| - | || (closing_dq = strchr(line + sizeof(INCLUDE)-1, '"')) == NULL |
| - | /* or strstr(line + sizeof(INCLUDE)-1, "\" -->")? */ |
| + | include_directive = strstr(line, INCLUDE); |
| + | if (!include_directive) { |
| + | fputs(line, stdout); |
| + | continue; |
| + | } |
| + | preceding_len = include_directive - line; |
| + | if (memchr(line, '\"', preceding_len) |
| + | || memchr(line, '\'', preceding_len) |
| ) { | |
| + | /* INCLUDE string may be inside "str" or 'str', |
| + | * ignore it */ |
| + | fputs(line, stdout); |
| + | continue; |
| + | } |
| + | /* Small bug: we accept #includefile="file" too */ |
| + | include_directive = skip_whitespace(include_directive + sizeof(INCLUDE)-1); |
| + | if (strncmp(include_directive, "file=\"", 6) != 0) { |
| + | /* "<!--#include virtual=..."? - not supported */ |
| + | fputs(line, stdout); |
| + | continue; |
| + | } |
| + | include_directive += 6; /* now it points to file name */ |
| + | end = strchr(include_directive, '\"'); |
| + | if (!end) { |
| fputs(line, stdout); | |
| continue; | |
| } | |
| - | *closing_dq = '\0'; |
| + | /* We checked that this is a valid include directive */ |
| + | |
| + | /* Print everything before directive */ |
| + | if (preceding_len) { |
| + | line[preceding_len] = '\0'; |
| + | fputs(line, stdout); |
| + | } |
| + | /* Save everything after directive */ |
| + | *end++ = '\0'; |
| + | end = strchr(end, '>'); |
| + | if (end) |
| + | end = strdup(end + 1); |
| + | |
| /* FIXME: | |
| * (1) are relative paths with /../ etc ok? | |
| - | * (2) if we include a/1.htm and it includes b/2.htm, |
| - | * do we need to include a/b/2.htm or b/2.htm? |
| - | * IOW, do we need to "cd $dirname"? |
| + | * (2) what to do with absolute paths? |
| + | * are they relative to doc root or to real root? |
| */ | |
| - | process_includes(line + sizeof(INCLUDE)-1); |
| - | /* FIXME: this should be the tail of line after --> */ |
| - | putchar('\n'); |
| + | process_includes(include_directive); |
| + | |
| + | /* Print everything after directive */ |
| + | if (end) { |
| + | fputs(end, stdout); |
| + | free(end); |
| + | } |
| } | |
| + | if (curdir_fd >= 0) |
| + | fchdir(curdir_fd); |
| + | fclose(fp); |
| } | |
| int main(int argc, char *argv[]) | |
| @@ -95,13 +156,14 @@ int main(int argc, char *argv[]) | |
| * Connection: close | |
| * Content-Type: text/html | |
| */ | |
| - | printf( |
| + | fputs( |
| /* "Date: Thu, 10 Sep 2009 18:23:28 GMT\r\n" */ | |
| /* "Server: Apache\r\n" */ | |
| /* "Accept-Ranges: bytes\r\n" - do we really accept bytes?! */ | |
| "Connection: close\r\n" | |
| "Content-Type: text/html\r\n" | |
| - | "\r\n" |
| + | "\r\n", |
| + | stdout |
| ); | |
| process_includes(argv[1]); | |
| return 0; | |