private import std.conv;
private import std.stream;
class URI
{
    // I don't know why it's wrong on arm-linux...
    //private static int [char] h2i;
    //private static int [char] noEsc;
    private static int [256] h2i;
    private static int [256] noEsc;
    static this()
    {
        for(char c='0'; c<='9'; ++c)         h2i[c] = c - '0';
        for(char c='A'; c<='F'; ++c)         h2i[c] = c - 'A' + 10;
        for(char c='a'; c<='f'; ++c)         h2i[c] = c - 'a' + 10;
        for(char c=0x20; c<=0x7E; ++c)       noEsc[c] = true;
        foreach(char c ; ";/?:&=+,-_.!~'()") noEsc[c] = false;
    }

    static char[] encode( char[] s )
    {
        char[] r;
        foreach( char c ; s )
            if( noEsc[c] ) r ~= (c==' ' ? '+' : c);
            else           r ~= int_format("%%%02X", c);
        version (IgnoreCase) {
            r = tolower(r);
        }
        return r;
    }

    static char[] decode( char[] s )
    {
        char[] r;
        for( int i=0; i!=s.length; ++i )
            switch( s[i] )
            {
            default:  r ~= s[i]; break;
            case '+': r ~= ' ';  break;
            case '%':
                if( i+1>=s.length )         break;
                if( s[++i]=='%' ) { r~='%'; break; }
                if( i+1>=s.length )         break;
                r~= cast(char)( 16*h2i[s[i]] + h2i[s[i+1]] );
                ++i;
            }
        version (IgnoreCase) {
            r = tolower(r);
        }
        return r;
    }
    static char[] int_format( char[] fmt, int v )
    {
        MemoryStream m = new MemoryStream();
        m.printf( fmt, v );
        return cast(char[]) m.data();
    }
}

extern(C) char* getenv( char* envvarname );
class Env
{
    static char[] opIndex( char[] varname )
    {
        char* result = getenv( toStringz(varname) );
        return result is null ? `` : std.string.toString(result);
    }
    class asInt {
        static int opIndex( char[] v )
        {
            return toInt( Env[v] );
        }
    }
}

class CGI
{
    char[]         self_name;
    char[][char[]] reqs;

    this()
    {
        char[] query;
        switch( Env[`REQUEST_METHOD`] )
        {
        case `GET` : query = Env[`QUERY_STRING`];                      break;
        case `POST`: query = readStdIn( Env.asInt[`CONTENT_LENGTH`] ); break;
        }
        foreach( char[] pair ; split(query,`&`) )
        {
            char[][] kv = split( pair, `=` );
            switch( kv.length )
            {
            case 1: reqs[``]               = URI.decode(kv[0]); break;
            case 2: reqs[URI.decode(kv[0])] = URI.decode(kv[1]); break;
            }
        }

        self_name = Env[`SCRIPT_NAME`];
        self_name = self_name[ rfind(self_name,'/')+1 .. self_name.length ];
        int end = find(self_name,'?');
        if( end >= 0 ) self_name = self_name[0 .. end];
    }

    private char[] readStdIn( uint size )
    {
        ubyte[] buf; buf.length = size;
        ubyte* ptr = buf;

        int x=0, y=1;
        while( x<size && y!=0 )
        {
            y = stdin.readBlock( ptr+x, size-x );
            x += y;
        }

        buf.length = x;
        return cast(char[]) buf;
    }
}
char[] escapeHTML(char[] str)
{
    str = replace(str,"&","&amp;");
    str = replace(str,"<","&lt;");
    str = replace(str,">","&gt;");
    str = replace(str,"\"","&quot;");
    str = replace(str,"'","&#39;");
    return str;
}
private import std.date;
extern(C) int	 sprintf(char *,char *,...);
char[] getExpires()
{
	d_time time = getUTCtime();
    d_time t;
    char sign;
    int hr;
    int mn;
    int len;
    d_time offset;
    d_time dst;

    char[] buffer = new char[30 + 7 + 1+5];

    if (time == d_time_nan)
	return "Invalid Date";

    dst = DaylightSavingTA(time);
    offset = LocalTZA + dst;
    t = time + offset;
    sign = '+';
    if (offset < 0)
    {	sign = '-';
	offset = -(LocalTZA + dst);
    }

    mn = cast(int)(offset / msPerMinute);
    hr = mn / 60;
    mn %= 60;

	len = sprintf(buffer, "%.3s, %02d %.3s %d %02d:%02d:%02d GMT%c%02d%02d",
	&daystr[WeekDay(t) * 3],
	DateFromTime(t),
	&monstr[MonthFromTime(t) * 3],
	cast(int)YearFromTime(t),
	cast(int)HourFromTime(t), cast(int)MinFromTime(t), cast(int)SecFromTime(t),
	sign, hr, mn
	);
    assert(len < buffer.length);

    return buffer[0 .. len];
}
private import std.file;
private import std.string;
private import std.regexp;

extern(C) int system(char *string);

char[][char[]] getenv()
{
	system(std.string.toStringz("set>____envdata_____.txt"));
	char[] set = cast(char[])read("____envdata_____.txt");
	remove("____envdata_____.txt");
	set = set.replace("\r","");
	char[][] setdt=set.split("\n");
	char[][char[]] env;
	for(int i=0;i<setdt.length;i++){
//		printf("[%.*s]\n",setdt[i]);
	    RegExp reg = new RegExp("=","g");
		int pos = reg.search(setdt[i]);
		if(pos>0){
			env[setdt[i][0..pos]]=setdt[i][pos+1..setdt[i].length];
		}
	}
	return env;
}
version(CGI_TEST){
version(GNU){
extern(C) int fork();
extern(C) int wait(int *status);
extern(C) int exit(int status);
}
void main()
{
version(Windows){
	char[] r = Env["REQUEST_URL"];
	if(r==""){
write("cgigo.bat",cast(void[])`
@echo off
set REQUEST_URL=/cgi.cgi/index.html?abc
set PATH_INFO=/index.html
set REQUEST_METHOD=GET
cgi`
);
		system("cgigo.bat");
		return;
	}
}

    CGI cgi = new CGI();
    char[] name = escapeHTML(cgi.reqs["name"]);
	char[] pathinfo = Env["PATH_INFO"];

	pathinfo = pathinfo.replace("..","");
	if(pathinfo=="")pathinfo="/index.html";
	if(pathinfo=="/")pathinfo="/index.html";
version(GNU){
	if(0)
	if(fork()){
		printf("content-type:text/html;\n\n oo");
		wait(null);
	}else{
		printf("content-type:text/html;\n\n oo2");
		exit(0);
	}
}
	//.htaccessで名前変換して、.htmlを.tmplにした場合
	if(pathinfo.length>=5 && pathinfo[pathinfo.length-5..pathinfo.length]==".tmpl"){
		pathinfo[pathinfo.length-5..pathinfo.length]=".html";
	}
	//.htaccessで名前変換している場合に、自cgiを普通に使った場合
	if(pathinfo.length>=1+cgi.self_name.length && pathinfo[0..6]=="/"~cgi.self_name){
		pathinfo=pathinfo[1+cgi.self_name.length..pathinfo.length];
	}
	printf("Content-type: text/html\n");
	printf("Pragma: no-cache\n");
	printf("Cache-Control: no-cache\n");
	printf("Expires: "~getExpires()~"\n");
	try{
		char[] data = cast(char[])read("tmpl"~pathinfo);
		printf("\n%.*s\n",data);
//		char[][char[]]env = getenv();
//		foreach(char[]key,char[]value;env){
//			printf("%.*s: %.*s<br>\n");
//		}
	}catch(Exception e){
	    printf("Status: 404 Not Found");
	}

}
}
