#! /usr/bin/gforth \ Copyright (C) 2005 Jason Woofenden \ \ This file is part of filb. \ \ filb is free software; you can redistribute it and/or modify it \ under the terms of the GNU General Public License as published by \ the Free Software Foundation; either version 2, or (at your option) \ any later version. \ \ filb is distributed in the hope that it will be useful, but \ WITHOUT ANY WARRANTY; without even the implied warranty of \ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU \ General Public License for more details. \ \ You should have received a copy of the GNU General Public License \ along with filb; see the file COPYING. If not, write to the \ Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, \ MA 02111-1307, USA. \ OVERVIEW \ \ This program reads in the IRC protocol from a file and outputs an html \ document. It uses template.html to arrange the output. \ \ It could be run like so: \ \ gforth filb.gf \ \ If I've coded it correctly, it should not crash or spin or spit out any \ malicious html, but if input doesn't follow the spec it may look very wrong. \ \ Each nickname is given a span tag with it's own class so that the CSS \ stylesheet can contain different colors for different nicks. \ \ Long nicknames are silently cut short. include code/gforthcgi/gforthcgi.fs \ SETTINGS " channel #c4th : okheaders 200ok text/html ; : 404headers 404fnf text/html ; \ we can find the path to this folder by removing filb.fs from the end of SCRIPT_NAME \ we can find the garbage after it by removing the path to this folder from REQUEST_URI : script-name-len s" SCRIPT_NAME" env dup 0=; drop nip 2 - ; ( -- u ) : getarg s" REQUEST_URI" env dup 0=; drop script-name-len 2dup <= if drop drop drop 0 exit then tuck - >r + r> ; ( -- addr u | 0 ) return the string passed on the URL \ not using get variables anymore... just the whole string as one arg... get-parse \ Parse the GET variables for the variables defined above \ CHARS \ FIXME: switch to 'a or whatever (is it 'a' maybe?) 32 constant #sp char _ constant #_ char & constant #& char # constant ## char ; constant #; char ! constant #! char : constant #: char * constant #* \ STRINGS \ strings have the following format in memory: one cell for the count followed by \ the data. The pointer returned by the name points to the data _not_ the count. : string-allot 0 , here swap aligned allot constant ; : count 4 - @ ; : ctype dup count type ; : string-empty 0 swap 4 - ! ; ( string -- ) : string-append-c dup count 2dup 1 + swap 4 - ! + c! ; ( char string -- ) : string-cmp >r 4 - dup @ 4 + r> 4 - over compare not ; ( string string -- t/f ) \ VARIABLES/CONSTANTS 16 constant max-nick \ the irc spec I was looking at said that nicks are limited to 9 chars I'm not sure freenode enforces this. max-nick string-allot nick 10 constant max-command max-command string-allot command " privmsg PRIVMSG " class-chars abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_ " html-chars abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_`~!@#$% ^*(){}[]?/+=|\-:;"',. " nbsp variable last-key : nbspaces dup 0 = if exit then for nbsp ctype next ; ( x -- print x non-breaking spaces ) \ UTILS : char-in-string? dup count for 2dup c@ = if 2drop unloop 1 exit then 1 + next 2drop 0 ; ( char string -- t/f ) : html-entity #& emit ## emit .. #; emit ; ( char -- ) : html-emit dup html-chars char-in-string? if emit exit then html-entity ; ( char -- print char encoded for html ) : html-ctype dup count dup 0 = if 2drop exit then for dup c@ html-emit 1 + next drop ; ( string -- print string encoded for html ) \ PARSING variable fd \ file descriptor for open log file variable read-buf : last-char last-key @ ; : at-eof? last-char -1 = ; ( -- t/f ) have we hit end-of-file already? : -get-char at-eof? if -1 exit then read-buf 1 fd @ read-file drop 0 = if -1 exit then read-buf c@ ; : get-char -get-char dup 13 = if drop tail-recurse exit then dup last-key ! ; : at-eol? ( -- t/f have we hit end-of-line already? ) last-char 10 = at-eof? or ; : eat-c get-char drop ; ( -- ) : eat-host get-char #sp = at-eol? or if exit then tail-recurse ; ( -- ) : eat-channel get-char #sp = at-eol? or if exit then tail-recurse ; ( -- ) : eat-line eat-c at-eol? if exit then tail-recurse ; ( -- ) \ : eol? dup 13 = if eat-c drop -1 exit then -1 = ; ( char -- t/f ) return if it's a newline, and remove the following character if it's a 13 : eol? dup 10 = if drop -1 exit then -1 = ; \ PARSE NICK : nick-char? dup #! = if drop 0 exit then eol? not ; : ?pn-add-c dup nick-char? if nick string-append-c 0 exit then drop 1 ; : clear-nick nick string-empty ; : parse-nick clear-nick max-nick for get-char ?pn-add-c if unloop exit then next ; \ PARSE COMMAND : clear-command command string-empty ; ( -- ) : ?pc-add-c get-char #sp = at-eol? or if 1 exit then last-char command string-append-c 0 ; : parse-command clear-command max-command for ?pc-add-c if unloop exit then next ; ( -- ) : is-privmsg? command privmsg string-cmp ; : eat-action eat-c eat-c eat-c eat-c eat-c eat-c eat-c ; \ PARSE CHANNEL : check-channel parse-command command channel string-cmp ; : to-lower dup #AA < if; dup #ZZ > if; #AA - #a + ; ( X -- x ) return lowercase version of x. : nick-class-char to-lower dup class-chars char-in-string? if emit exit then drop #_ emit ; : %nchar dup count for dup c@ nick-class-char 1 + next drop ; ( string -- ) : %hchar -enc-html ; ( string -- ) : nick-pad max-nick nick count - nbspaces ; " s-normal-nick %h: " s-action-nick *%h : normal-nick nick dup s-normal-nick %type ; : action-nick nick dup s-action-nick %type ; : dump-to get-char over = at-eof? or if drop exit then last-char html-emit tail-recurse ; ( char -- ) dump characters until char or EOF is reached : print-action action-nick eat-action 1 dump-to eat-c ; : print-normal normal-nick last-char emit 10 dump-to ; : -print-message nick-pad get-char 1 = if print-action exit then print-normal ; : parse-message ."