From 6afa62b57cee77dae0c62ed4f192204127b3d402 Mon Sep 17 00:00:00 2001
From: Slava Pestov <slava@slava-pestovs-macbook-pro.local>
Date: Tue, 22 Apr 2008 20:19:54 -0500
Subject: [PATCH 1/9] Add RFC822 date parser

---
 extra/calendar/calendar-tests.factor          |   4 +
 extra/calendar/calendar.factor                |  35 +++---
 extra/calendar/format/format-tests.factor     |  33 ++++--
 extra/calendar/format/format.factor           | 100 +++++++++++++-----
 .../{blog-summary.xml => blog-admin-link.xml} |   0
 .../{mini-planet.xml => postings-summary.xml} |   0
 6 files changed, 124 insertions(+), 48 deletions(-)
 rename extra/webapps/planet/{blog-summary.xml => blog-admin-link.xml} (100%)
 rename extra/webapps/planet/{mini-planet.xml => postings-summary.xml} (100%)

diff --git a/extra/calendar/calendar-tests.factor b/extra/calendar/calendar-tests.factor
index e49d3ad894..c05d4f60eb 100755
--- a/extra/calendar/calendar-tests.factor
+++ b/extra/calendar/calendar-tests.factor
@@ -2,6 +2,10 @@ USING: arrays calendar kernel math sequences tools.test
 continuations system ;
 IN: calendar.tests
 
+\ time+ must-infer
+\ time* must-infer
+\ time- must-infer
+
 [ f ] [ 2004 12 32 0   0  0 instant <timestamp> valid-timestamp? ] unit-test
 [ f ] [ 2004  2 30 0   0  0 instant <timestamp> valid-timestamp? ] unit-test
 [ f ] [ 2003  2 29 0   0  0 instant <timestamp> valid-timestamp? ] unit-test
diff --git a/extra/calendar/calendar.factor b/extra/calendar/calendar.factor
index 8dcb4af7f1..2f93bf8218 100755
--- a/extra/calendar/calendar.factor
+++ b/extra/calendar/calendar.factor
@@ -211,12 +211,14 @@ M: duration time+
     #! Uses average month/year length since dt loses calendar
     #! data
     0 swap
-    [ year>> + ] keep
-    [ month>> months-per-year / + ] keep
-    [ day>> days-per-year / + ] keep
-    [ hour>> hours-per-year / + ] keep
-    [ minute>> minutes-per-year / + ] keep
-    second>> seconds-per-year / + ;
+    {
+        [ year>> + ]
+        [ month>> months-per-year / + ]
+        [ day>> days-per-year / + ]
+        [ hour>> hours-per-year / + ]
+        [ minute>> minutes-per-year / + ]
+        [ second>> seconds-per-year / + ]
+    } cleave ;
 
 M: duration <=> [ dt>years ] compare ;
 
@@ -252,14 +254,21 @@ M: timestamp time-
     #! Exact calendar-time difference
     (time-) seconds ;
 
+: time* ( obj1 obj2 -- obj3 )
+    dup real? [ swap ] when
+    dup real? [ * ] [
+        {
+            [   year>> * ]
+            [  month>> * ]
+            [    day>> * ]
+            [   hour>> * ]
+            [ minute>> * ]
+            [ second>> * ]
+        } 2cleave <duration>
+    ] if ;
+
 : before ( dt -- -dt )
-    [ year>>   neg ] keep
-    [ month>>  neg ] keep
-    [ day>>    neg ] keep
-    [ hour>>   neg ] keep
-    [ minute>> neg ] keep
-      second>> neg
-    <duration> ;
+    -1 time* ;
 
 M: duration time-
     before time+ ;
diff --git a/extra/calendar/format/format-tests.factor b/extra/calendar/format/format-tests.factor
index 88bd0733c0..1ba892bef3 100755
--- a/extra/calendar/format/format-tests.factor
+++ b/extra/calendar/format/format-tests.factor
@@ -1,26 +1,45 @@
-USING: calendar.format calendar kernel tools.test
-io.streams.string ;
+USING: calendar.format calendar kernel math tools.test
+io.streams.string accessors io ;
 IN: calendar.format.tests
 
 [ 0 ] [
-    "Z" [ read-rfc3339-gmt-offset ] with-string-reader
+    "Z" [ read1 read-rfc3339-gmt-offset ] with-string-reader dt>hours
 ] unit-test
 
 [ 1 ] [
-    "+01" [ read-rfc3339-gmt-offset ] with-string-reader
+    "+01" [ read1 read-rfc3339-gmt-offset ] with-string-reader dt>hours
 ] unit-test
 
 [ -1 ] [
-    "-01" [ read-rfc3339-gmt-offset ] with-string-reader
+    "-01" [ read1 read-rfc3339-gmt-offset ] with-string-reader dt>hours
 ] unit-test
 
 [ -1-1/2 ] [
-    "-01:30" [ read-rfc3339-gmt-offset ] with-string-reader
+    "-01:30" [ read1 read-rfc3339-gmt-offset ] with-string-reader dt>hours
 ] unit-test
 
 [ 1+1/2 ] [
-    "+01:30" [ read-rfc3339-gmt-offset ] with-string-reader
+    "+01:30" [ read1 read-rfc3339-gmt-offset ] with-string-reader dt>hours
 ] unit-test
 
 [ ] [ now timestamp>rfc3339 drop ] unit-test
 [ ] [ now timestamp>rfc822 drop ] unit-test
+
+[ 8/1000 -4 ] [
+    "2008-04-19T04:56:00.008-04:00" rfc3339>timestamp
+    [ second>> ] [ gmt-offset>> hour>> ] bi
+] unit-test
+
+[ T{ duration f 0 0 0 0 0 0 } ] [
+    "GMT" parse-rfc822-gmt-offset
+] unit-test
+
+[ T{ duration f 0 0 0 -5 0 0 } ] [
+    "-0500" parse-rfc822-gmt-offset
+] unit-test
+
+[ T{ timestamp f 2008 4 22 14 36 12 T{ duration f 0 0 0 0 0 0 } } ] [
+    "Tue, 22 Apr 2008 14:36:12 GMT" rfc822>timestamp
+] unit-test
+
+[ t ] [ now dup timestamp>rfc822 rfc822>timestamp time- 1 seconds before? ] unit-test
diff --git a/extra/calendar/format/format.factor b/extra/calendar/format/format.factor
index 26ed873fd3..7bdaea70b5 100755
--- a/extra/calendar/format/format.factor
+++ b/extra/calendar/format/format.factor
@@ -1,5 +1,6 @@
 USING: math math.parser kernel sequences io calendar
-accessors arrays io.streams.string combinators accessors ;
+accessors arrays io.streams.string splitting
+combinators accessors debugger ;
 IN: calendar.format
 
 GENERIC: day. ( obj -- )
@@ -58,11 +59,11 @@ M: timestamp year. ( timestamp -- )
     [ hour>> write-00 ] [ minute>> write-00 ] bi ;
 
 : write-gmt-offset ( gmt-offset -- )
-    dup instant <=> {
-        { [ dup 0 = ] [ 2drop "GMT" write ] }
-        { [ dup 0 < ] [ drop "-" write before (write-gmt-offset) ] }
-        { [ dup 0 > ] [ drop "+" write (write-gmt-offset) ] }
-    } cond ;
+    dup instant <=> sgn {
+        {  0 [ drop "GMT" write ] }
+        { -1 [ "-" write before (write-gmt-offset) ] }
+        {  1 [ "+" write (write-gmt-offset) ] }
+    } case ;
 
 : timestamp>rfc822 ( timestamp -- str )
     #! RFC822 timestamp format
@@ -83,20 +84,22 @@ M: timestamp year. ( timestamp -- )
     [ minute>> write-00 ] bi ;
 
 : write-rfc3339-gmt-offset ( duration -- )
-    dup instant <=> {
-        { [ dup 0 = ] [ 2drop "Z" write ] }
-        { [ dup 0 < ] [ drop CHAR: - write1 before (write-rfc3339-gmt-offset) ] }
-        { [ dup 0 > ] [ drop CHAR: + write1 (write-rfc3339-gmt-offset) ] }
-    } cond ;
+    dup instant <=> sgn {
+        {  0 [ drop "Z" write ] }
+        { -1 [ CHAR: - write1 before (write-rfc3339-gmt-offset) ] }
+        {  1 [ CHAR: + write1 (write-rfc3339-gmt-offset) ] }
+    } case ;
     
 : (timestamp>rfc3339) ( timestamp -- )
-    dup year>> number>string write CHAR: - write1
-    dup month>> write-00 CHAR: - write1
-    dup day>> write-00 CHAR: T write1
-    dup hour>> write-00 CHAR: : write1
-    dup minute>> write-00 CHAR: : write1
-    dup second>> >fixnum write-00
-    gmt-offset>> write-rfc3339-gmt-offset ;
+    {
+        [ year>> number>string write CHAR: - write1 ]
+        [ month>> write-00 CHAR: - write1 ]
+        [ day>> write-00 CHAR: T write1 ]
+        [ hour>> write-00 CHAR: : write1 ]
+        [ minute>> write-00 CHAR: : write1 ]
+        [ second>> >fixnum write-00 ]
+        [ gmt-offset>> write-rfc3339-gmt-offset ]
+    } cleave ;
 
 : timestamp>rfc3339 ( timestamp -- str )
     [ (timestamp>rfc3339) ] with-string-writer ;
@@ -106,14 +109,20 @@ M: timestamp year. ( timestamp -- )
 
 : read-00 2 read string>number ;
 
+: read-000 3 read string>number ;
+
 : read-0000 4 read string>number ;
 
-: read-rfc3339-gmt-offset ( -- n )
-    read1 dup CHAR: Z = [ drop 0 ] [
-        { { CHAR: + [ 1 ] } { CHAR: - [ -1 ] } } case
-        read-00
-        read1 { { CHAR: : [ read-00 ] } { f [ 0 ] } } case
-        60 / + *
+: signed-gmt-offset ( dt ch -- dt' )
+    { { CHAR: + [ 1 ] } { CHAR: - [ -1 ] } } case time* ;
+
+: read-rfc3339-gmt-offset ( ch -- dt )
+    dup CHAR: Z = [ drop instant ] [
+        >r
+        read-00 hours
+        read1 { { CHAR: : [ read-00 ] } { f [ 0 ] } } case minutes
+        time+
+        r> signed-gmt-offset
     ] if ;
 
 : read-ymd ( -- y m d )
@@ -126,26 +135,61 @@ M: timestamp year. ( timestamp -- )
     read-ymd
     "Tt" expect
     read-hms
-    read-rfc3339-gmt-offset ! timezone
+    read1 { { CHAR: . [ read-000 1000 / + read1 ] } [ ] } case
+    read-rfc3339-gmt-offset
     <timestamp> ;
 
 : rfc3339>timestamp ( str -- timestamp )
     [ (rfc3339>timestamp) ] with-string-reader ;
 
+ERROR: invalid-rfc822-date ;
+
+: check-rfc822-date ( obj/f -- obj ) [ invalid-rfc822-date ] unless* ;
+
+: read-token ( seps -- token )
+    [ read-until ] keep member? check-rfc822-date drop ;
+
+: read-sp ( -- token ) " " read-token ;
+
+: checked-number ( str -- n )
+    string>number check-rfc822-date ;
+
+: parse-rfc822-gmt-offset ( string -- dt )
+    dup "GMT" = [ drop instant ] [
+        unclip >r
+        2 cut [ string>number ] bi@ [ hours ] [ minutes ] bi* time+
+        r> signed-gmt-offset
+    ] if ;
+
+: (rfc822>timestamp) ( -- timestamp )
+    timestamp new
+        "," read-token day-abbreviations3 member? check-rfc822-date drop
+        read1 CHAR: \s assert=
+        read-sp checked-number >>day
+        read-sp month-abbreviations index check-rfc822-date >>month
+        read-sp checked-number >>year
+        ":" read-token checked-number >>hour
+        ":" read-token checked-number >>minute
+        " " read-token checked-number >>second
+        readln parse-rfc822-gmt-offset >>gmt-offset ;
+
+: rfc822>timestamp ( str -- timestamp )
+    [ (rfc822>timestamp) ] with-string-reader ;
+
 : (ymdhms>timestamp) ( -- timestamp )
-    read-ymd " " expect read-hms 0 <timestamp> ;
+    read-ymd " " expect read-hms instant <timestamp> ;
 
 : ymdhms>timestamp ( str -- timestamp )
     [ (ymdhms>timestamp) ] with-string-reader ;
 
 : (hms>timestamp) ( -- timestamp )
-    f f f read-hms f <timestamp> ;
+    f f f read-hms instant <timestamp> ;
 
 : hms>timestamp ( str -- timestamp )
     [ (hms>timestamp) ] with-string-reader ;
 
 : (ymd>timestamp) ( -- timestamp )
-    read-ymd f f f f <timestamp> ;
+    read-ymd f f f instant <timestamp> ;
 
 : ymd>timestamp ( str -- timestamp )
     [ (ymd>timestamp) ] with-string-reader ;
diff --git a/extra/webapps/planet/blog-summary.xml b/extra/webapps/planet/blog-admin-link.xml
similarity index 100%
rename from extra/webapps/planet/blog-summary.xml
rename to extra/webapps/planet/blog-admin-link.xml
diff --git a/extra/webapps/planet/mini-planet.xml b/extra/webapps/planet/postings-summary.xml
similarity index 100%
rename from extra/webapps/planet/mini-planet.xml
rename to extra/webapps/planet/postings-summary.xml

From bfa34122f3ac2eb429b33dd340dfebfaa4badff1 Mon Sep 17 00:00:00 2001
From: Slava Pestov <slava@slava-pestovs-macbook-pro.local>
Date: Tue, 22 Apr 2008 20:23:49 -0500
Subject: [PATCH 2/9] Fix chunked encoding

---
 extra/http/client/client.factor |  4 +--
 extra/http/http.factor          | 43 +++++++++++++++++++++++----------
 2 files changed, 32 insertions(+), 15 deletions(-)

diff --git a/extra/http/client/client.factor b/extra/http/client/client.factor
index ac5d220a52..8879a76a5c 100755
--- a/extra/http/client/client.factor
+++ b/extra/http/client/client.factor
@@ -74,8 +74,8 @@ PRIVATE>
     ] with-variable ;
 
 : read-chunks ( -- )
-    readln ";" split1 drop hex>
-    dup { f 0 } member? [ drop ] [ read % read-chunks ] if ;
+    read-crlf ";" split1 drop hex> dup { f 0 } member?
+    [ drop ] [ read % read-crlf "" assert= read-chunks ] if ;
 
 : do-chunked-encoding ( response stream -- response stream/string )
     over "transfer-encoding" header "chunked" = [
diff --git a/extra/http/http.factor b/extra/http/http.factor
index 5e90962b27..4aaab2205e 100755
--- a/extra/http/http.factor
+++ b/extra/http/http.factor
@@ -1,10 +1,18 @@
 ! Copyright (C) 2003, 2008 Slava Pestov.
 ! See http://factorcode.org/license.txt for BSD license.
-USING: fry hashtables io io.streams.string kernel math sets
-namespaces math.parser assocs sequences strings splitting ascii
-io.encodings.utf8 io.encodings.string io.sockets namespaces
-unicode.case combinators vectors sorting accessors calendar
-calendar.format quotations arrays combinators.lib byte-arrays ;
+USING: accessors kernel combinators math namespaces
+
+assocs sequences splitting sorting sets debugger
+strings vectors hashtables quotations arrays byte-arrays
+math.parser calendar calendar.format
+
+io io.streams.string io.encodings.utf8 io.encodings.string
+io.sockets
+
+unicode.case unicode.categories qualified ;
+
+EXCLUDE: fry => , ;
+
 IN: http
 
 : http-port 80 ; inline
@@ -13,11 +21,12 @@ IN: http
     #! In a URL, can this character be used without
     #! URL-encoding?
     {
-        [ dup letter? ]
-        [ dup LETTER? ]
-        [ dup digit? ]
-        [ dup "/_-.:" member? ]
-    } || nip ; foldable
+        { [ dup letter? ] [ t ] }
+        { [ dup LETTER? ] [ t ] }
+        { [ dup digit? ] [ t ] }
+        { [ dup "/_-.:" member? ] [ t ] }
+        [ f ]
+    } cond nip ; foldable
 
 : push-utf8 ( ch -- )
     1string utf8 encode
@@ -75,8 +84,16 @@ IN: http
         ] if
     ] if ;
 
+: read-lf ( -- string )
+    "\n" read-until CHAR: \n assert= ;
+
+: read-crlf ( -- string )
+    "\r" read-until
+    CHAR: \r assert=
+    read1 CHAR: \n assert= ;
+
 : read-header-line ( -- )
-    readln dup
+    read-crlf dup
     empty? [ drop ] [ header-line read-header-line ] if ;
 
 : read-header ( -- assoc )
@@ -224,7 +241,7 @@ cookies ;
     dup { "1.0" "1.1" } member? [ "Bad version" throw ] unless ;
 
 : read-request-version ( request -- request )
-    readln [ CHAR: \s = ] left-trim
+    read-crlf [ CHAR: \s = ] left-trim
     parse-version
     >>version ;
 
@@ -372,7 +389,7 @@ body ;
     >>code ;
 
 : read-response-message
-    readln >>message ;
+    read-crlf >>message ;
 
 : read-response-header
     read-header >>header

From f9ce5dd6c38ecbe1cd93af780a97e96e17a0ead4 Mon Sep 17 00:00:00 2001
From: Slava Pestov <slava@slava-pestovs-macbook-pro.local>
Date: Tue, 22 Apr 2008 20:23:54 -0500
Subject: [PATCH 3/9] Fix RSS2.0 support

---
 extra/rss/rss.factor | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/extra/rss/rss.factor b/extra/rss/rss.factor
index 2e735d2f06..5fc688967a 100644
--- a/extra/rss/rss.factor
+++ b/extra/rss/rss.factor
@@ -23,7 +23,7 @@ C: <entry> entry
     [ "link" tag-named children>string ] keep
     [ "description" tag-named children>string ] keep
     f "date" "http://purl.org/dc/elements/1.1/" <name>
-    tag-named dup [ children>string rfc3339>timestamp ] when
+    tag-named dup [ children>string rfc822>timestamp ] when
     <entry> ;
 
 : rss1.0 ( xml -- feed )
@@ -39,7 +39,7 @@ C: <entry> entry
     [ "link" tag-named ] keep
     [ "guid" tag-named dupd ? children>string ] keep
     [ "description" tag-named children>string ] keep
-    "pubDate" tag-named children>string rfc3339>timestamp <entry> ;
+    "pubDate" tag-named children>string rfc822>timestamp <entry> ;
 
 : rss2.0 ( xml -- feed )
     "channel" tag-named 
@@ -71,16 +71,12 @@ C: <entry> entry
         { "feed" [ atom1.0 ] }
     } case ;
 
-: read-feed ( stream -- feed )
-    [ read-xml ] with-html-entities xml>feed ;
+: read-feed ( string -- feed )
+    [ string>xml xml>feed ] with-html-entities ;
 
 : download-feed ( url -- feed )
     #! Retrieve an news syndication file, return as a feed tuple.
-    http-get-stream swap code>> success? [
-        read-feed
-    ] [
-        dispose "Error retrieving newsfeed file" throw
-    ] if ;
+    http-get read-feed ;
 
 ! Atom generation
 : simple-tag, ( content name -- )

From 138cff4d47f9fd9e63d358e436592c53141d683c Mon Sep 17 00:00:00 2001
From: Doug Coleman <doug.coleman@gmail.com>
Date: Tue, 22 Apr 2008 21:06:24 -0500
Subject: [PATCH 4/9] stack effects for recusive words

---
 extra/windows/windows.factor | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/extra/windows/windows.factor b/extra/windows/windows.factor
index 0e555ed7e9..3e7520d406 100644
--- a/extra/windows/windows.factor
+++ b/extra/windows/windows.factor
@@ -30,10 +30,10 @@ FUNCTION: void* error_message ( DWORD id ) ;
 : win32-error ( -- )
     GetLastError (win32-error) ;
 
-: win32-error=0/f { 0 f } member? [ win32-error ] when ;
-: win32-error>0 0 > [ win32-error ] when ;
-: win32-error<0 0 < [ win32-error ] when ;
-: win32-error<>0 zero? [ win32-error ] unless ;
+: win32-error=0/f ( n -- ) { 0 f } member? [ win32-error ] when ;
+: win32-error>0 ( n -- ) 0 > [ win32-error ] when ;
+: win32-error<0 ( n -- ) 0 < [ win32-error ] when ;
+: win32-error<>0 ( n -- ) zero? [ win32-error ] unless ;
 
 : invalid-handle? ( handle -- )
     INVALID_HANDLE_VALUE = [

From 6e89f7b085bd2ec63948296344ff7f89375169a3 Mon Sep 17 00:00:00 2001
From: Slava Pestov <slava@slava-pestovs-macbook-pro.local>
Date: Tue, 22 Apr 2008 21:08:27 -0500
Subject: [PATCH 5/9] Working on planet factor rewrite

---
 extra/http/server/auth/login/login.factor     |  2 +-
 .../server/boilerplate/boilerplate.factor     |  2 +-
 .../http/server/components/components.factor  | 29 +++++-
 extra/http/server/forms/forms.factor          |  2 +-
 extra/http/server/server.factor               | 29 +++---
 .../http/server/templating/chloe/chloe.factor |  3 +-
 .../http/server/templating/fhtml/fhtml.factor |  2 +-
 .../http/server/templating/templating.factor  | 17 +++-
 .../factor-website/factor-website.factor      | 38 ++++++++
 .../{planet => factor-website}/page.xml       | 35 ++++---
 extra/webapps/planet/admin.xml                | 13 +++
 extra/webapps/planet/entry.xml                | 14 ++-
 extra/webapps/planet/planet.factor            | 96 +++++++++++--------
 extra/webapps/planet/planet.xml               | 42 ++++----
 extra/webapps/planet/postings.xml             | 19 ++++
 extra/webapps/todo/page.xml                   | 45 ---------
 extra/webapps/todo/todo.css                   | 16 ----
 extra/webapps/todo/todo.factor                | 34 +------
 18 files changed, 236 insertions(+), 202 deletions(-)
 create mode 100644 extra/webapps/factor-website/factor-website.factor
 rename extra/webapps/{planet => factor-website}/page.xml (89%)
 create mode 100644 extra/webapps/planet/admin.xml
 create mode 100644 extra/webapps/planet/postings.xml
 delete mode 100644 extra/webapps/todo/page.xml

diff --git a/extra/http/server/auth/login/login.factor b/extra/http/server/auth/login/login.factor
index b0cc0c21d1..7593f217f7 100755
--- a/extra/http/server/auth/login/login.factor
+++ b/extra/http/server/auth/login/login.factor
@@ -363,7 +363,7 @@ M: login call-responder ( path responder -- response )
 
 : <login> ( responder -- auth )
     login new-dispatcher
-        swap <protected> >>default
+        swap >>default
         <login-action> <login-boilerplate> "login" add-responder
         <logout-action> <login-boilerplate> "logout" add-responder
         no-users >>users ;
diff --git a/extra/http/server/boilerplate/boilerplate.factor b/extra/http/server/boilerplate/boilerplate.factor
index 6c62452ec2..eabcefeb7f 100644
--- a/extra/http/server/boilerplate/boilerplate.factor
+++ b/extra/http/server/boilerplate/boilerplate.factor
@@ -48,7 +48,7 @@ SYMBOL: next-template
 : call-next-template ( -- )
     next-template get write ;
 
-M: f call-template drop call-next-template ;
+M: f call-template* drop call-next-template ;
 
 : with-boilerplate ( body template -- )
     [
diff --git a/extra/http/server/components/components.factor b/extra/http/server/components/components.factor
index bdcdd95c71..331231dfb3 100755
--- a/extra/http/server/components/components.factor
+++ b/extra/http/server/components/components.factor
@@ -280,6 +280,22 @@ TUPLE: date < string ;
 M: date component-string
     drop timestamp>string ;
 
+! Link components
+
+GENERIC: link-title ( obj -- string )
+GENERIC: link-href ( obj -- url )
+
+SINGLETON: link-renderer
+
+M: link-renderer render-view*
+    drop <a dup link-href =href a> link-title write </a> ;
+
+TUPLE: link < string ;
+
+: <link> ( id -- component )
+    link new-string
+        link-renderer >>renderer ;
+
 ! List components
 SYMBOL: +plain+
 SYMBOL: +ordered+
@@ -289,17 +305,20 @@ TUPLE: list-renderer component type ;
 
 C: <list-renderer> list-renderer
 
-: render-plain-list ( seq quot component -- )
-    swap '[ , @ ] each ; inline
+: render-plain-list ( seq component quot -- )
+    '[ , component>> renderer>> @ ] each ; inline
+
+: render-li-list ( seq component quot -- )
+    '[ <li> @ </li> ] render-plain-list ; inline
 
 : render-ordered-list ( seq quot component -- )
-    swap <ol> '[ <li> , @ </li> ] each </ol> ; inline
+    <ol> render-li-list </ol> ; inline
 
 : render-unordered-list ( seq quot component -- )
-    swap <ul> '[ <li> , @ </li> ] each </ul> ; inline
+    <ul> render-li-list </ul> ; inline
 
 : render-list ( value renderer quot -- )
-    swap [ component>> ] [ type>> ] bi {
+    over type>> {
         { +plain+     [ render-plain-list ] }
         { +ordered+   [ render-ordered-list ] }
         { +unordered+ [ render-unordered-list ] }
diff --git a/extra/http/server/forms/forms.factor b/extra/http/server/forms/forms.factor
index f45bf6ec65..60f3da25b6 100644
--- a/extra/http/server/forms/forms.factor
+++ b/extra/http/server/forms/forms.factor
@@ -78,4 +78,4 @@ M: form render-view*
     dup view-template>> render-form ;
 
 M: form render-edit*
-    dup edit-template>> render-form ;
+    nip dup edit-template>> render-form ;
diff --git a/extra/http/server/server.factor b/extra/http/server/server.factor
index db03645a24..d3bd6c6bbe 100755
--- a/extra/http/server/server.factor
+++ b/extra/http/server/server.factor
@@ -160,23 +160,30 @@ drop
 
 SYMBOL: development-mode
 
+: http-error. ( error -- )
+    "Internal server error" [
+        development-mode get [
+            [ print-error nl :c ] with-html-stream
+        ] [
+            500 "Internal server error"
+            trivial-response-body
+        ] if
+    ] simple-page ;
+
 : <500> ( error -- response )
     500 "Internal server error" <trivial-response>
-    swap '[
-        , "Internal server error" [
-            development-mode get [
-                [ print-error nl :c ] with-html-stream
-            ] [
-                500 "Internal server error"
-                trivial-response-body
-            ] if
-        ] simple-page
-    ] >>body ;
+    swap '[ , http-error. ] >>body ;
 
 : do-response ( response -- )
     dup write-response
     request get method>> "HEAD" =
-    [ drop ] [ write-response-body ] if ;
+    [ drop ] [
+        '[
+            , write-response-body
+        ] [
+            http-error.
+        ] recover
+    ] if ;
 
 LOG: httpd-hit NOTICE
 
diff --git a/extra/http/server/templating/chloe/chloe.factor b/extra/http/server/templating/chloe/chloe.factor
index 8142c5e3b7..685988dfaf 100644
--- a/extra/http/server/templating/chloe/chloe.factor
+++ b/extra/http/server/templating/chloe/chloe.factor
@@ -153,6 +153,7 @@ SYMBOL: tags
         { "form" [ form-tag ] }
         { "error" [ error-tag ] }
         { "if" [ if-tag ] }
+        { "comment" [ drop ] }
         { "call-next-template" [ drop call-next-template ] }
         [ "Unknown chloe tag: " swap append throw ]
     } case ;
@@ -189,7 +190,7 @@ SYMBOL: tags
         ] if
     ] with-scope ;
 
-M: chloe call-template
+M: chloe call-template*
     path>> utf8 <file-reader> read-xml process-chloe ;
 
 INSTANCE: chloe template
diff --git a/extra/http/server/templating/fhtml/fhtml.factor b/extra/http/server/templating/fhtml/fhtml.factor
index 1cba4b9b2e..2cc053a0ca 100755
--- a/extra/http/server/templating/fhtml/fhtml.factor
+++ b/extra/http/server/templating/fhtml/fhtml.factor
@@ -76,7 +76,7 @@ TUPLE: fhtml path ;
 
 C: <fhtml> fhtml
 
-M: fhtml call-template ( filename -- )
+M: fhtml call-template* ( filename -- )
     '[
         , path>> [
             "quiet" on
diff --git a/extra/http/server/templating/templating.factor b/extra/http/server/templating/templating.factor
index f69dd9bfe0..610ec78fed 100644
--- a/extra/http/server/templating/templating.factor
+++ b/extra/http/server/templating/templating.factor
@@ -1,10 +1,21 @@
-USING: accessors kernel fry io.encodings.utf8 io.files
-http http.server ;
+USING: accessors kernel fry io io.encodings.utf8 io.files
+http http.server debugger prettyprint continuations ;
 IN: http.server.templating
 
 MIXIN: template
 
-GENERIC: call-template ( template -- )
+GENERIC: call-template* ( template -- )
+
+ERROR: template-error template error ;
+
+M: template-error error.
+    "Error while processing template " write
+    [ template>> pprint ":" print nl ]
+    [ error>> error. ]
+    bi ;
+
+: call-template ( template -- )
+    [ call-template* ] [ template-error ] recover ;
 
 M: template write-response-body* call-template ;
 
diff --git a/extra/webapps/factor-website/factor-website.factor b/extra/webapps/factor-website/factor-website.factor
new file mode 100644
index 0000000000..3483d4321e
--- /dev/null
+++ b/extra/webapps/factor-website/factor-website.factor
@@ -0,0 +1,38 @@
+! Copyright (c) 2008 Slava Pestov
+! See http://factorcode.org/license.txt for BSD license.
+USING: accessors kernel sequences io.files io.sockets
+db.sqlite smtp namespaces db
+http.server.db
+http.server.sessions
+http.server.auth.login
+http.server.auth.providers.db
+http.server.sessions.storage.db
+http.server.boilerplate
+http.server.templating.chloe ;
+IN: webapps.factor-website
+
+: factor-template ( path -- template )
+    "resource:extra/webapps/factor-website/" swap ".xml" 3append <chloe> ;
+
+: test-db "todo.db" resource-path sqlite-db ;
+
+: <factor-boilerplate> ( responder -- responder' )
+    <login>
+        users-in-db >>users
+        allow-registration
+        allow-password-recovery
+        allow-edit-profile
+    <boilerplate>
+        "page" factor-template >>template
+    <url-sessions>
+        sessions-in-db >>sessions
+    test-db <db-persistence> ;
+
+: init-factor-website ( -- )
+    "factorcode.org" 25 <inet> smtp-server set-global
+    "todo@factorcode.org" lost-password-from set-global
+
+    test-db [
+        init-sessions-table
+        init-users-table
+    ] with-db ;
diff --git a/extra/webapps/planet/page.xml b/extra/webapps/factor-website/page.xml
similarity index 89%
rename from extra/webapps/planet/page.xml
rename to extra/webapps/factor-website/page.xml
index 1278c8174c..d929042320 100644
--- a/extra/webapps/planet/page.xml
+++ b/extra/webapps/factor-website/page.xml
@@ -10,52 +10,49 @@
 		<head>
 			<t:write-title />
 
-			<t:write-atom />
-
 			<t:style>
+				body, button {
+					font:9pt "Lucida Grande", "Lucida Sans Unicode", verdana, geneva, sans-serif;
+					color:#444;
+				}
+
 				.link-button {
 					padding: 0px;
 					background: none;
 					border: none;
 				}
 
-				.inline {
-					display: inline;
-				}
-
-				body, button {
-					font:9pt "Lucida Grande", "Lucida Sans Unicode", verdana, geneva, sans-serif;
-					color:#444;
-				}
-
 				a, .link {
 					color: #222;
 					border-bottom:1px dotted #666;
 					text-decoration:none;
 				}
 
-				h1 a {
-					border: none;
-				}
-
 				a:hover, .link:hover {
 					border-bottom:1px solid #66a;
 				}
 
 				.error { color: #a00; }
-				
+
 				.field-label {
 					text-align: right;
 				}
+
+				.inline {
+					display: inline;
+				}
+				
+				.navbar {
+					background-color: #eee;
+					padding: 5px;
+					border: 1px solid #ccc;
+				}
 			</t:style>
 
 			<t:write-style />
 		</head>
 
 		<body>
-
-			<h1><t:a href="planet"><t:write-title /></t:a></h1>
-
 			<t:call-next-template />
 		</body>
 
diff --git a/extra/webapps/planet/admin.xml b/extra/webapps/planet/admin.xml
new file mode 100644
index 0000000000..1a18cad94b
--- /dev/null
+++ b/extra/webapps/planet/admin.xml
@@ -0,0 +1,13 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+	<t:title>Planet Factor Administration</t:title>
+
+	<t:summary component="blogroll" />
+
+	<p>
+		<t:a href="edit-blog">Add Blog</t:a> | <t:a href="update">Update</t:a>
+	</p>
+
+</t:chloe>
diff --git a/extra/webapps/planet/entry.xml b/extra/webapps/planet/entry.xml
index a9383d16f2..bc89af3263 100644
--- a/extra/webapps/planet/entry.xml
+++ b/extra/webapps/planet/entry.xml
@@ -2,8 +2,16 @@
 
 <t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
 
-	<h2 class="posting-title"><t:view component="title" /></h2>
-	<p class="posting-body"> <t:view component="description" /> </p>
-	<p class="posting-date"> <t:view component="pub-date" /> </p>
+	<h2 class="posting-title">
+		<t:a value="link"><t:view component="title" /></t:a>
+	</h2>
+
+	<p class="posting-body">
+		<t:view component="description" />
+	</p>
+
+	<p class="posting-date">
+		<t:a value="link"><t:view component="pub-date" /></t:a>
+	</p>
 
 </t:chloe>
diff --git a/extra/webapps/planet/planet.factor b/extra/webapps/planet/planet.factor
index 966bcc1d0b..464e2bbfb3 100755
--- a/extra/webapps/planet/planet.factor
+++ b/extra/webapps/planet/planet.factor
@@ -1,7 +1,7 @@
 ! Copyright (C) 2008 Slava Pestov.
 ! See http://factorcode.org/license.txt for BSD license.
 USING: kernel accessors sequences sorting locals math
-calendar alarms logging concurrency.combinators
+calendar alarms logging concurrency.combinators namespaces
 db.types db.tuples db
 rss xml.writer
 http.server
@@ -10,11 +10,22 @@ http.server.forms
 http.server.actions
 http.server.boilerplate
 http.server.templating.chloe
-http.server.components ;
+http.server.components
+http.server.auth.login
+webapps.factor-website ;
 IN: webapps.planet
 
+TUPLE: planet-factor < dispatcher postings ;
+
+: planet-template ( name -- template )
+    "resource:extra/webapps/planet/" swap ".xml" 3append <chloe> ;
+
 TUPLE: blog id name www-url atom-url ;
 
+M: blog link-title name>> ;
+
+M: blog link-href www-url>> ;
+
 blog "BLOGS"
 {
     { "id" "ID" INTEGER +native-id+ }
@@ -29,8 +40,8 @@ blog "BLOGS"
     blog new
         swap >>id ;
 
-: planet-template ( name -- template )
-    "resource:extra/webapps/planet/" swap ".xml" 3append <chloe> ;
+: blogroll ( -- seq )
+    f <blog> select-tuples [ [ name>> ] compare ] sort ;
 
 : <entry-form> ( -- form )
     "entry" <form>
@@ -44,7 +55,7 @@ blog "BLOGS"
     "blog" <form>
         "edit-blog" planet-template >>edit-template
         "view-blog" planet-template >>view-template
-        "blog-summary" planet-template >>summary-template
+        "blog-admin-link" planet-template >>summary-template
         "id" <integer>
             hidden >>renderer
             add-field
@@ -60,15 +71,27 @@ blog "BLOGS"
 
 : <planet-factor-form> ( -- form )
     "planet-factor" <form>
-        "planet" planet-template >>view-template
-        "mini-planet" planet-template >>summary-template
+        "postings" planet-template >>view-template
+        "postings-summary" planet-template >>summary-template
         "postings" <entry-form> +plain+ <list> add-field
+        "blogroll" "blog" <link> +unordered+ <list> add-field ;
+
+: <admin-form> ( -- form )
+    "admin" <form>
+        "admin" planet-template >>view-template
         "blogroll" <blog-form> +unordered+ <list> add-field ;
 
-: blogroll ( -- seq )
-    f <blog> select-tuples [ [ name>> ] compare ] sort ;
+:: <edit-blogroll-action> ( planet -- action )
+    [let | form [ <admin-form> ] |
+        <action>
+            [
+                blank-values
 
-TUPLE: planet-factor < dispatcher postings ;
+                blogroll "blogroll" set-value
+
+                form view-form
+            ] >>display
+    ] ;
 
 :: <planet-action> ( planet -- action )
     [let | form [ <planet-factor-form> ] |
@@ -90,7 +113,7 @@ TUPLE: planet-factor < dispatcher postings ;
     feed new
         "[ planet-factor ]" >>title
         "http://planet.factorcode.org" >>link
-        planet postings>> 30 safe-head >>entries ;
+        planet postings>> 16 safe-head >>entries ;
 
 :: <feed-action> ( planet -- action )
     <action>
@@ -117,7 +140,8 @@ TUPLE: planet-factor < dispatcher postings ;
 
 : update-cached-postings ( planet -- )
     "webapps.planet" [
-        blogroll fetch-blogroll sort-entries >>postings drop
+        blogroll fetch-blogroll sort-entries 8 safe-head
+        >>postings drop
     ] with-logging ;
 
 :: <update-action> ( planet -- action )
@@ -127,16 +151,11 @@ TUPLE: planet-factor < dispatcher postings ;
             "" f <temporary-redirect>
         ] >>display ;
 
-: start-update-task ( planet -- )
-    [ update-cached-postings ] curry 10 minutes every drop ;
-
-:: <planet-factor> ( -- responder )
+:: <planet-factor-admin> ( planet-factor -- responder )
     [let | blog-form [ <blog-form> ]
            blog-ctor [ [ <blog> ] ] |
-        planet-factor new-dispatcher
-            dup <planet-action> >>default
-            dup <feed-action> "feed.xml" add-responder
-            dup <update-action> "update" add-responder
+        <dispatcher>
+            planet-factor <edit-blogroll-action> >>default
 
             ! Administrative CRUD
                       blog-ctor ""          <delete-action> "delete-blog" add-responder
@@ -144,30 +163,25 @@ TUPLE: planet-factor < dispatcher postings ;
             blog-form blog-ctor "view-blog" <edit-action>   "edit-blog"   add-responder
     ] ;
 
-USING: namespaces io.files io.sockets
-db.sqlite smtp
-http.server.db
-http.server.sessions
-http.server.auth.login
-http.server.auth.providers.db
-http.server.sessions.storage.db ;
-
-: test-db "planet.db" resource-path sqlite-db ;
-
-: <planet-app> ( -- responder )
-    <planet-factor>
+: <planet-factor> ( -- responder )
+    planet-factor new-dispatcher
+        dup <planet-action> >>default
+        dup <feed-action> "feed.xml" add-responder
+        dup <update-action> "update" add-responder
+        dup <planet-factor-admin> <protected> "admin" add-responder
     <boilerplate>
-        "page" planet-template >>template
-    ! <url-sessions>
-    !     sessions-in-db >>sessions
-    test-db <db-persistence> ;
+        "planet" planet-template >>template ;
+ 
+: <planet-app> ( -- responder )
+    <planet-factor> <factor-boilerplate> ;
+
+: start-update-task ( planet -- )
+    [ update-cached-postings ] curry 10 minutes every drop ;
 
 : init-planet ( -- )
-    ! test-db [
-    !     init-blog-table
-        ! init-users-table
-        ! init-sessions-table
-    ! ] with-db
+    test-db [
+        init-blog-table
+    ] with-db
 
     <dispatcher>
         <planet-app> "planet" add-responder
diff --git a/extra/webapps/planet/planet.xml b/extra/webapps/planet/planet.xml
index dc762fafc6..772f81906d 100644
--- a/extra/webapps/planet/planet.xml
+++ b/extra/webapps/planet/planet.xml
@@ -2,36 +2,30 @@
 
 <t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
 
-	<t:title>Planet Factor</t:title>
-
+<t:comment>
 	<t:atom title="Planet Factor - Atom" href="feed.xml" />
-
+</t:comment>
 	<t:style include="resource:extra/webapps/planet/planet.css" />
 
-	<table width="100%" cellpadding="10">
-		<tr>
-			<td> <t:view component="postings" /> </td>
+	<div class="navbar">
+		  <t:a href="list">Front Page</t:a>
+		| <t:a href="feed.xml">Atom Feed</t:a>
 
-			<td valign="top" width="25%" class="infobox">
-				<p>
-					<strong>planet-factor</strong> is an Atom feed aggregator that collects the
-					contents of <a href="http://factorcode.org/">Factor</a>-related blogs. It was inspired by
-					<a href="http://planet.lisp.org">Planet Lisp</a>.
-				</p>
-				<p>
-					<img src="http://planet.lisp.org/feed-icon-14x14.png" />
-					<a href="feed.xml"> Syndicate </a>
-				</p>
+		| <t:a href="admin">Admin</t:a>
 
-				<h2>Blogroll</h2>
+		<t:comment>
+		<t:if code="http.server.auth.login:allow-edit-profile?">
+			| <t:a href="edit-profile">Edit Profile</t:a>
+		</t:if>
 
-				<t:summary component="blogroll" />
+		<t:form action="logout" class="inline">
+			| <button type="submit" class="link-button link">Logout</button>
+		</t:form>
+		</t:comment>
+	</div>
 
-				Admin: <t:a href="edit-blog">Add Blog</t:a>
-				|
-				<t:a href="update">Update</t:a>
-			</td>
-		</tr>
-	</table>
+	<h1><t:write-title /></h1>
+
+        <t:call-next-template />
 
 </t:chloe>
diff --git a/extra/webapps/planet/postings.xml b/extra/webapps/planet/postings.xml
new file mode 100644
index 0000000000..f59a4f61b8
--- /dev/null
+++ b/extra/webapps/planet/postings.xml
@@ -0,0 +1,19 @@
+<?xml version='1.0' ?>
+
+<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
+
+	<t:title>Planet Factor</t:title>
+
+	<table width="100%" cellpadding="10">
+                <tr>
+                        <td> <t:view component="postings" /> </td>
+  
+                        <td valign="top" width="25%" class="infobox">
+                                <h2>Blogroll</h2>
+  
+                                <t:summary component="blogroll" />
+                        </td>
+                </tr>
+        </table>
+
+</t:chloe>
diff --git a/extra/webapps/todo/page.xml b/extra/webapps/todo/page.xml
deleted file mode 100644
index f40c79d299..0000000000
--- a/extra/webapps/todo/page.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version='1.0' ?>
-
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-
-	<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
-
-		<head>
-			<t:write-title />
-
-			<t:style>
-				body, button {
-					font:9pt "Lucida Grande", "Lucida Sans Unicode", verdana, geneva, sans-serif;
-					color:#444;
-				}
-
-				a, .link {
-					color: #222;
-					border-bottom:1px dotted #666;
-					text-decoration:none;
-				}
-				
-				a:hover, .link:hover {
-					border-bottom:1px solid #66a;
-				}
-
-				.error { color: #a00; }
-				
-				.field-label {
-					text-align: right;
-				}
-			</t:style>
-
-			<t:write-style />
-		</head>
-
-		<body>
-			<t:call-next-template />
-		</body>
-
-	</t:chloe>
-
-</html>
diff --git a/extra/webapps/todo/todo.css b/extra/webapps/todo/todo.css
index c2e8a7fd79..2520a56128 100644
--- a/extra/webapps/todo/todo.css
+++ b/extra/webapps/todo/todo.css
@@ -10,22 +10,6 @@
 	color: #000000;
 }
 
-.link-button {
-	padding: 0px;
-	background: none;
-	border: none;
-}
-
-.navbar {
-	background-color: #eeeeee;
-	padding: 5px;
-	border: 1px solid #ccc;
-}
-
-.inline {
-	display: inline;
-}
-
 pre {
 	font-size: 75%;
 }
diff --git a/extra/webapps/todo/todo.factor b/extra/webapps/todo/todo.factor
index 917b9bf7a7..08555b92ed 100755
--- a/extra/webapps/todo/todo.factor
+++ b/extra/webapps/todo/todo.factor
@@ -1,12 +1,13 @@
 ! Copyright (c) 2008 Slava Pestov
 ! See http://factorcode.org/license.txt for BSD license.
-USING: accessors kernel locals sequences
+USING: accessors kernel locals sequences namespaces
 db db.types db.tuples
 http.server.components http.server.components.farkup
 http.server.forms http.server.templating.chloe
 http.server.boilerplate http.server.crud http.server.auth
 http.server.actions http.server.db
-http.server ;
+http.server
+webapps.factor-website ;
 IN: webapps.todo
 
 TUPLE: todo uid id priority summary description ;
@@ -71,37 +72,10 @@ TUPLE: todo-responder < dispatcher ;
             "todo" todo-template >>template
     ] ;
 
-! What follows below is somewhat akin to a 'deployment descriptor'
-! for the todo application. The <todo-responder> can be integrated
-! into an existing web app that provides session management and
-! login facilities, or <todo-app> can be used to run a
-! self-contained todo instance.
-USING: namespaces io.files io.sockets
-db.sqlite smtp
-http.server.sessions
-http.server.auth.login
-http.server.auth.providers.db
-http.server.sessions.storage.db ;
-
-: test-db "todo.db" resource-path sqlite-db ;
-
 : <todo-app> ( -- responder )
-    <todo-responder>
-    <login>
-        users-in-db >>users
-        allow-registration
-        allow-password-recovery
-        allow-edit-profile
-    <boilerplate>
-        "page" todo-template >>template
-    <url-sessions>
-        sessions-in-db >>sessions
-    test-db <db-persistence> ;
+    <todo-responder> <protected> <factor-boilerplate> ;
 
 : init-todo ( -- )
-    "factorcode.org" 25 <inet> smtp-server set-global
-    "todo@factorcode.org" lost-password-from set-global
-
     test-db [
         init-todo-table
         init-users-table

From 6a0dc9f02451015d909f8941594a27250f89b4b6 Mon Sep 17 00:00:00 2001
From: Doug Coleman <doug.coleman@gmail.com>
Date: Wed, 23 Apr 2008 00:07:26 -0500
Subject: [PATCH 6/9] fix unit tests

---
 extra/db/tuples/tuples-tests.factor | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/extra/db/tuples/tuples-tests.factor b/extra/db/tuples/tuples-tests.factor
index 1c900edc68..32562a4ae8 100755
--- a/extra/db/tuples/tuples-tests.factor
+++ b/extra/db/tuples/tuples-tests.factor
@@ -80,9 +80,9 @@ SYMBOL: person4
             "teddy"
             10
             3.14
-            T{ timestamp f 2008 3 5 16 24 11 0 }
-            T{ timestamp f 2008 11 22 f f f f }
-            T{ timestamp f f f f 12 34 56 f }
+            T{ timestamp f 2008 3 5 16 24 11 T{ duration f 0 0 0 0 0 0 } }
+            T{ timestamp f 2008 11 22 f f f T{ duration f 0 0 0 0 0 0 } }
+            T{ timestamp f f f f 12 34 56 T{ duration f 0 0 0 0 0 0 } }
             B{ 115 116 111 114 101 105 110 97 98 108 111 98 }
         }
     ] [ T{ person f 3 } select-tuple ] unit-test
@@ -96,9 +96,9 @@ SYMBOL: person4
             "eddie"
             10
             3.14
-            T{ timestamp f 2008 3 5 16 24 11 0 }
-            T{ timestamp f 2008 11 22 f f f f }
-            T{ timestamp f f f f 12 34 56 f }
+            T{ timestamp f 2008 3 5 16 24 11 T{ duration f 0 0 0 0 0 0 } }
+            T{ timestamp f 2008 11 22 f f f T{ duration f 0 0 0 0 0 0 } }
+            T{ timestamp f f f f 12 34 56 T{ duration f 0 0 0 0 0 0 } }
             f
             H{ { 1 2 } { 3 4 } { 5 "lol" } }
         }

From f9659ecc7c412eaf986d14fd19b6d29c6de3d230 Mon Sep 17 00:00:00 2001
From: Eric Mertens <emertens@galois.com>
Date: Tue, 22 Apr 2008 23:45:30 -0700
Subject: [PATCH 7/9] Add sequences.lib.replicate

---
 extra/sequences/lib/lib.factor | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/extra/sequences/lib/lib.factor b/extra/sequences/lib/lib.factor
index b186ee7777..c648660d66 100755
--- a/extra/sequences/lib/lib.factor
+++ b/extra/sequences/lib/lib.factor
@@ -131,6 +131,10 @@ MACRO: firstn ( n -- )
     [ find drop [ head-slice ] when* ] curry
     [ dup ] swap compose keep like ;
 
+: replicate ( seq quot -- newseq )
+    #! quot: ( -- obj )
+    [ drop ] swap compose map ;
+
 ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
 <PRIVATE

From 916ed96ffb81b052ad9cdcbb41e982a64664c0ae Mon Sep 17 00:00:00 2001
From: Eric Mertens <emertens@gmail.com>
Date: Tue, 22 Apr 2008 01:44:50 -0700
Subject: [PATCH 8/9] Add project-euler.151

---
 extra/project-euler/151/151.factor | 40 ++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)
 create mode 100644 extra/project-euler/151/151.factor

diff --git a/extra/project-euler/151/151.factor b/extra/project-euler/151/151.factor
new file mode 100644
index 0000000000..85aad116b4
--- /dev/null
+++ b/extra/project-euler/151/151.factor
@@ -0,0 +1,40 @@
+! Copyright (c) 2008 Eric Mertens
+! See http://factorcode.org/license.txt for BSD license.
+USING: sequences combinators kernel sequences.lib math assocs namespaces ;
+IN: project-euler.151
+
+SYMBOL: table
+
+: (pick-sheet) ( seq i -- newseq )
+    [
+        <=> sgn
+        {
+            { -1 [ ] }
+            {  0 [ 1- ] }
+            {  1 [ 1+ ] }
+        } case
+    ] curry map-index ;
+
+DEFER: (euler151)
+
+: pick-sheet ( seq i -- res )
+    2dup swap nth dup zero? [
+        3drop 0
+    ] [
+        [ (pick-sheet) (euler151) ] dip *
+    ] if ;
+
+: (euler151) ( x -- y )
+    table get [ {
+        { { 0 0 0 1 } [ 0 ] }
+        { { 0 0 1 0 } [ { 0 0 0 1 } (euler151) 1+ ] }
+        { { 0 1 0 0 } [ { 0 0 1 1 } (euler151) 1+ ] }
+        { { 1 0 0 0 } [ { 0 1 1 1 } (euler151) 1+ ] }
+        [ [ dup length [ pick-sheet ] with map sum ] [ sum ] bi / ]
+     } case ] cache ;
+
+: euler151 ( -- n )
+    [
+        H{ } clone table set
+        { 1 1 1 1 } (euler151)
+    ] with-scope ;

From 57a15fb363f5f03c8f49e033879bb755763d9299 Mon Sep 17 00:00:00 2001
From: Eric Mertens <emertens@gmail.com>
Date: Tue, 22 Apr 2008 01:45:29 -0700
Subject: [PATCH 9/9] Add project-euler.100

---
 extra/project-euler/100/100.factor | 7 +++++++
 1 file changed, 7 insertions(+)
 create mode 100644 extra/project-euler/100/100.factor

diff --git a/extra/project-euler/100/100.factor b/extra/project-euler/100/100.factor
new file mode 100644
index 0000000000..d2d396a0e1
--- /dev/null
+++ b/extra/project-euler/100/100.factor
@@ -0,0 +1,7 @@
+USING: kernel sequences math.functions math ;
+IN: project-euler.100
+
+: euler100 ( -- n )
+    1 1
+    [ dup dup 1- * 2 * 10 24 ^ <= ]
+    [ tuck 6 * swap - 2 - ] [ ] while nip ;