From 063e4571095699d46f419a422258f8d9a72f61fc Mon Sep 17 00:00:00 2001
From: Slava Pestov <slava@slava-pestovs-macbook-pro.local>
Date: Thu, 26 Feb 2009 13:41:30 -0600
Subject: [PATCH 01/18] Remove superflous \

---
 basis/editors/emacs/emacs.factor | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/basis/editors/emacs/emacs.factor b/basis/editors/emacs/emacs.factor
index 05b879770e..366bc53104 100644
--- a/basis/editors/emacs/emacs.factor
+++ b/basis/editors/emacs/emacs.factor
@@ -11,7 +11,7 @@ M: object default-emacsclient ( -- path ) "emacsclient" ;
 
 : emacsclient ( file line -- )
     [
-        { [ \ emacsclient-path get ] [ default-emacsclient ] } 0|| ,
+        { [ emacsclient-path get ] [ default-emacsclient ] } 0|| ,
         "--no-wait" ,
         number>string "+" prepend ,
         ,

From 3f70bb3b22d2854c97fc08e4ad1bb6b6bd446b02 Mon Sep 17 00:00:00 2001
From: Slava Pestov <slava@slava-pestovs-macbook-pro.local>
Date: Thu, 26 Feb 2009 13:41:50 -0600
Subject: [PATCH 02/18] Update docs for improved error reporting

---
 basis/stack-checker/stack-checker-docs.factor | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/basis/stack-checker/stack-checker-docs.factor b/basis/stack-checker/stack-checker-docs.factor
index db8abac441..088fab34d0 100644
--- a/basis/stack-checker/stack-checker-docs.factor
+++ b/basis/stack-checker/stack-checker-docs.factor
@@ -21,7 +21,7 @@ $nl
 
 ARTICLE: "inference-combinators" "Combinator stack effects"
 "Without further information, one cannot say what the stack effect of " { $link call } " is; it depends on the given quotation. If the inferencer encounters a " { $link call } " without further information, a " { $link literal-expected } " error is raised."
-{ $example "[ dup call ] infer." "Literal value expected\n\nType :help for debugging help." }
+{ $example "[ dup call ] infer." "Got a computed value where a literal quotation was expected\n\nType :help for debugging help." }
 "On the other hand, the stack effect of applying " { $link call } " to a literal quotation or a " { $link curry } " of a literal quotation is easy to compute; it behaves as if the quotation was substituted at that point:"
 { $example "[ [ 2 + ] call ] infer." "( object -- object )" }
 "Consider a combinator such as " { $link keep } ". The combinator itself does not have a stack effect, because it applies " { $link call } " to a potentially arbitrary quotation. However, since the combinator is declared " { $link POSTPONE: inline } ", a given usage of it can have a stack effect:"
@@ -38,7 +38,7 @@ $nl
 { $example ": foo 0 [ + ] ; inline" "[ foo reduce ] infer." "( object -- object )" }
 "Passing a literal quotation on the data stack through an inlined recursive combinator nullifies its literal status. For example, the following will not infer:"
 { $example
-  "[ [ reverse ] swap [ reverse ] map swap call ] infer." "Literal value expected\n\nType :help for debugging help."
+  "[ [ reverse ] swap [ reverse ] map swap call ] infer." "Got a computed value where a literal quotation was expected\n\nType :help for debugging help."
 }
 "To make this work, pass the quotation on the retain stack instead:"
 { $example
@@ -67,11 +67,11 @@ $nl
 "If a recursive word takes quotation parameters from the stack and calls them, it must be declared " { $link POSTPONE: inline } " (as documented in " { $link "inference-combinators" } ") as well as " { $link POSTPONE: recursive } "."
 $nl
 "Furthermore, the input parameters which are quotations must be annotated in the stack effect. For example, the following will not infer:"
-{ $example ": bad ( quot -- ) [ call ] keep foo ; inline recursive" "[ [ ] bad ] infer." "Literal value expected\n\nType :help for debugging help." }
+{ $example ": bad ( quot -- ) [ call ] keep foo ; inline recursive" "[ [ ] bad ] infer." "Got a computed value where a literal quotation was expected\n\nType :help for debugging help." }
 "The following is correct:"
 { $example ": good ( quot: ( -- ) -- ) [ call ] keep good ; inline recursive" "[ [ ] good ] infer." "( -- )" }
 "An inline recursive word cannot pass a quotation on the data stack through the recursive call. For example, the following will not infer:"
-{ $example ": bad ( ? quot: ( ? -- ) -- ) 2dup [ not ] dip bad call ; inline recursive" "[ [ drop ] bad ] infer." "Literal value expected\n\nType :help for debugging help." }
+{ $example ": bad ( ? quot: ( ? -- ) -- ) 2dup [ not ] dip bad call ; inline recursive" "[ [ drop ] bad ] infer." "Got a computed value where a literal quotation was expected\n\nType :help for debugging help." }
 "However a small change can be made:"
 { $example ": good ( ? quot: ( ? -- ) -- ) [ good ] 2keep [ not ] dip call ; inline recursive" "[ [ drop ] good ] infer." "( object -- )" }
 "An inline recursive word must have a fixed stack effect in its base case. The following will not infer:"

From a2404fad9490e6ed2af2fd7a39fdf19bf6d7c522 Mon Sep 17 00:00:00 2001
From: Slava Pestov <slava@slava-pestovs-macbook-pro.local>
Date: Thu, 26 Feb 2009 13:44:37 -0600
Subject: [PATCH 03/18] Add missing file

---
 build-support/cleanup | 8 ++++++++
 1 file changed, 8 insertions(+)
 create mode 100644 build-support/cleanup

diff --git a/build-support/cleanup b/build-support/cleanup
new file mode 100644
index 0000000000..2d2aab0bba
--- /dev/null
+++ b/build-support/cleanup
@@ -0,0 +1,8 @@
+vm
+temp
+logs
+.git
+.gitignore
+Makefile
+unmaintained
+build-support

From bda8b2dda63687176c35602c86a10accb3f3b100 Mon Sep 17 00:00:00 2001
From: Slava Pestov <slava@slava-pestovs-macbook-pro.local>
Date: Thu, 26 Feb 2009 14:11:26 -0600
Subject: [PATCH 04/18] Better inlining for both-fixnums?

---
 basis/compiler/tree/cleanup/cleanup-tests.factor           | 5 +++++
 .../tree/propagation/known-words/known-words.factor        | 7 +++++--
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/basis/compiler/tree/cleanup/cleanup-tests.factor b/basis/compiler/tree/cleanup/cleanup-tests.factor
index 54f8aaf20e..4a2e8671fb 100755
--- a/basis/compiler/tree/cleanup/cleanup-tests.factor
+++ b/basis/compiler/tree/cleanup/cleanup-tests.factor
@@ -510,3 +510,8 @@ cell-bits 32 = [
     [ { array } declare 2 <groups> [ . . ] assoc-each ]
     \ nth-unsafe inlined?
 ] unit-test
+
+[ t ] [
+    [ { fixnum fixnum } declare = ]
+    \ both-fixnums? inlined?
+] unit-test
\ No newline at end of file
diff --git a/basis/compiler/tree/propagation/known-words/known-words.factor b/basis/compiler/tree/propagation/known-words/known-words.factor
index d5aa5318a4..ecfd415579 100644
--- a/basis/compiler/tree/propagation/known-words/known-words.factor
+++ b/basis/compiler/tree/propagation/known-words/known-words.factor
@@ -199,8 +199,11 @@ generic-comparison-ops [
 ] "outputs" set-word-prop
 
 \ both-fixnums? [
-    [ class>> fixnum classes-intersect? not ] either?
-    f <literal-info> object-info ?
+    [ class>> ] bi@ {
+        { [ 2dup [ fixnum classes-intersect? not ] either? ] [ f <literal-info> ] }
+        { [ 2dup [ fixnum class<= ] both? ] [ t <literal-info> ] }
+        [ object-info ]
+    } cond 2nip
 ] "outputs" set-word-prop
 
 {

From dc370e56abf0d6888388921a0a5d053847a3e84f Mon Sep 17 00:00:00 2001
From: Doug Coleman <doug.coleman@gmail.com>
Date: Thu, 26 Feb 2009 17:34:26 -0600
Subject: [PATCH 05/18] mmap now throws an understandable exception upon trying
 to mmap a zero length file.  fix a bug with calling |dispose on an integer if
 mmap failed on unix

---
 basis/io/mmap/mmap-tests.factor | 20 ++++++++++++++++++++
 basis/io/mmap/mmap.factor       |  9 +++++++--
 basis/io/mmap/unix/unix.factor  |  2 +-
 3 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/basis/io/mmap/mmap-tests.factor b/basis/io/mmap/mmap-tests.factor
index 166167a7e7..b892bded20 100644
--- a/basis/io/mmap/mmap-tests.factor
+++ b/basis/io/mmap/mmap-tests.factor
@@ -9,3 +9,23 @@ IN: io.mmap.tests
 [ 5 ] [ "mmap-test-file.txt" temp-file [ length ] with-mapped-char-file ] unit-test
 [ "22345" ] [ "mmap-test-file.txt" temp-file ascii file-contents ] unit-test
 [ "mmap-test-file.txt" temp-file delete-file ] ignore-errors
+
+
+[ ]
+[ "mmap-empty-file.txt" temp-file touch-file ] unit-test
+
+! Test for leaking resources bug on Unix
+[ ]
+[
+    100000 [
+        [
+            "mmap-empty-file.txt" temp-file [
+                drop
+            ] with-mapped-file
+        ] [ dup bad-mmap-size? [ drop ] [ rethrow ] if ] recover
+    ] times
+
+    "asdf" "mmap-asdf-file.txt" temp-file [ ascii set-file-contents ] keep [
+        drop
+    ] with-mapped-file
+] unit-test
diff --git a/basis/io/mmap/mmap.factor b/basis/io/mmap/mmap.factor
index 6f2fabb709..1a58471514 100644
--- a/basis/io/mmap/mmap.factor
+++ b/basis/io/mmap/mmap.factor
@@ -2,15 +2,20 @@
 ! See http://factorcode.org/license.txt for BSD license.
 USING: continuations destructors io.files io.files.info
 io.backend kernel quotations system alien alien.accessors
-accessors system vocabs.loader combinators alien.c-types ;
+accessors system vocabs.loader combinators alien.c-types
+math ;
 IN: io.mmap
 
 TUPLE: mapped-file address handle length disposed ;
 
 HOOK: (mapped-file) os ( path length -- address handle )
 
+ERROR: bad-mmap-size path size ;
+
 : <mapped-file> ( path -- mmap )
-    [ normalize-path ] [ file-info size>> ] bi [ (mapped-file) ] keep
+    [ normalize-path ] [ file-info size>> ] bi
+    dup 0 <= [ bad-mmap-size ] when
+    [ (mapped-file) ] keep
     f mapped-file boa ;
 
 HOOK: close-mapped-file io-backend ( mmap -- )
diff --git a/basis/io/mmap/unix/unix.factor b/basis/io/mmap/unix/unix.factor
index 9325dcd632..0fa8e1151f 100644
--- a/basis/io/mmap/unix/unix.factor
+++ b/basis/io/mmap/unix/unix.factor
@@ -9,7 +9,7 @@ IN: io.mmap.unix
 :: mmap-open ( path length prot flags -- alien fd )
     [
         f length prot flags
-        path open-r/w |dispose
+        path open-r/w [ <fd> |dispose drop ] keep
         [ 0 mmap dup MAP_FAILED = [ (io-error) ] when ] keep
     ] with-destructors ;
 

From e986a604876d66d27f08c04aa893b4b4372686cf Mon Sep 17 00:00:00 2001
From: Doug Coleman <doug.coleman@gmail.com>
Date: Thu, 26 Feb 2009 17:54:44 -0600
Subject: [PATCH 06/18] id3 shouldn't fail on files with length < 128

---
 extra/id3/id3.factor | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/extra/id3/id3.factor b/extra/id3/id3.factor
index f2bbd08996..289cc27b6b 100644
--- a/extra/id3/id3.factor
+++ b/extra/id3/id3.factor
@@ -1,6 +1,10 @@
 ! Copyright (C) 2009 Tim Wawrzynczak
 ! See http://factorcode.org/license.txt for BSD license.
-USING: sequences io io.encodings.binary io.files io.pathnames strings kernel math io.mmap io.mmap.uchar accessors syntax combinators math.ranges unicode.categories byte-arrays io.encodings.string io.encodings.utf8 assocs math.parser ;
+USING: sequences io io.encodings.binary io.files io.pathnames
+strings kernel math io.mmap io.mmap.uchar accessors syntax
+combinators math.ranges unicode.categories byte-arrays
+io.encodings.string io.encodings.utf8 assocs math.parser
+combinators.short-circuit ;
 IN: id3
 
 <PRIVATE
@@ -160,7 +164,7 @@ TUPLE: id3-info title artist album year comment genre ;
     "ID3" head? ;
 
 : id3v1? ( mmap -- ? )
-    128 tail-slice* "TAG" head? ;
+    { [ length 128 >= ] [ 128 tail-slice* "TAG" head? ] } 1&& ;
 
 : >28bitword ( seq -- int )
     0 [ swap 7 shift bitor ] reduce ;

From 16d3562b238382db24ef49df7a19a53dddc0adc9 Mon Sep 17 00:00:00 2001
From: Doug Coleman <doug.coleman@gmail.com>
Date: Thu, 26 Feb 2009 18:03:15 -0600
Subject: [PATCH 07/18] factor id3 a bit

---
 extra/id3/id3.factor | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/extra/id3/id3.factor b/extra/id3/id3.factor
index 289cc27b6b..abfe01c3e4 100644
--- a/extra/id3/id3.factor
+++ b/extra/id3/id3.factor
@@ -4,12 +4,11 @@ USING: sequences io io.encodings.binary io.files io.pathnames
 strings kernel math io.mmap io.mmap.uchar accessors syntax
 combinators math.ranges unicode.categories byte-arrays
 io.encodings.string io.encodings.utf8 assocs math.parser
-combinators.short-circuit ;
+combinators.short-circuit fry ;
 IN: id3
 
 <PRIVATE
 
-! genres
 CONSTANT: genres
   H{
     { 0 "Blues" }
@@ -225,7 +224,7 @@ TUPLE: id3-info title artist album year comment genre ;
 : read-v2-header ( mmap -- id3header )
     [ <header> ] dip
     {
-        [ read-header-supported-version?  >>version ]
+        [ read-header-supported-version? >>version ]
         [ read-header-flags >>flags ]
         [ read-header-size >>size ]
     } cleave ;
@@ -233,16 +232,19 @@ TUPLE: id3-info title artist album year comment genre ;
 : drop-header ( mmap -- seq1 seq2 )
     dup 10 tail-slice swap ;
 
+: frame-tag ( frame string -- tag/f )
+    '[ frame-id>> _ = ] find nip ; inline
+
 : parse-frames ( id3v2-info -- id3-info )
     [ <id3-info> ] dip frames>>
     {
-        [ [ frame-id>> "TIT2" = ] find nip [ data>> >>title ] when* ]
-        [ [ frame-id>> "TALB" = ] find nip [ data>> >>album ] when* ]
-        [ [ frame-id>> "TPE1" = ] find nip [ data>> >>artist ] when* ]
-        [ [ frame-id>> "TCON" = ] find nip [ data>> [ [ digit? ] filter string>number ] keep swap [ genres at nip ] when*
+        [ "TIT2" frame-tag [ data>> >>title ] when* ]
+        [ "TALB" frame-tag [ data>> >>album ] when* ]
+        [ "TPE1" frame-tag [ data>> >>artist ] when* ]
+        [ "TCON" frame-tag [ data>> [ [ digit? ] filter string>number ] keep swap [ genres at nip ] when*
           >>genre ] when* ]
-        [ [ frame-id>> "COMM" = ] find nip [ data>> >>comment ] when* ]
-        [ [ frame-id>> "TYER" = ] find nip [ data>> >>year ] when* ]
+        [ "COMM" frame-tag [ data>> >>comment ] when* ]
+        [ "TYER" frame-tag [ data>> >>year ] when* ]
     } cleave ;
 
 : read-v2-tag-data ( seq -- id3-info )

From a083832ab497392ed008467389554b63d4089f5f Mon Sep 17 00:00:00 2001
From: Doug Coleman <doug.coleman@gmail.com>
Date: Thu, 26 Feb 2009 18:10:11 -0600
Subject: [PATCH 08/18] fix typo in math docs

---
 core/math/math-docs.factor | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/core/math/math-docs.factor b/core/math/math-docs.factor
index 94ff2c1f29..101557d0cf 100644
--- a/core/math/math-docs.factor
+++ b/core/math/math-docs.factor
@@ -308,7 +308,7 @@ HELP: find-last-integer
 
 HELP: byte-array>bignum
 { $values { "byte-array" byte-array } { "n" integer } }
-{ $description "Converts a byte-array, interpreted as little-endian, into a bignum integer. User code should call " { $link >le } " or " { $link >be } " instead." } ;
+{ $description "Converts a byte-array, interpreted as little-endian, into a bignum integer. User code should call " { $link le> } " or " { $link be> } " instead." } ;
 
 ARTICLE: "division-by-zero" "Division by zero"
 "Floating point division never raises an error if the denominator is zero. This means that if at least one of the two inputs to " { $link / } ", " { $link /f } " or " { $link mod } " is a float, the result will be a floating point infinity or not a number value."

From 084311750e2b0e2be608dcfc17ddfb91d6ffce2f Mon Sep 17 00:00:00 2001
From: Doug Coleman <doug.coleman@gmail.com>
Date: Thu, 26 Feb 2009 18:11:07 -0600
Subject: [PATCH 09/18] add using to mmap tests

---
 basis/io/mmap/mmap-tests.factor | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/basis/io/mmap/mmap-tests.factor b/basis/io/mmap/mmap-tests.factor
index b892bded20..70a1869bd0 100644
--- a/basis/io/mmap/mmap-tests.factor
+++ b/basis/io/mmap/mmap-tests.factor
@@ -1,6 +1,6 @@
 USING: io io.mmap io.mmap.char io.files io.files.temp
 io.directories kernel tools.test continuations sequences
-io.encodings.ascii accessors ;
+io.encodings.ascii accessors math ;
 IN: io.mmap.tests
 
 [ "mmap-test-file.txt" temp-file delete-file ] ignore-errors

From 1bd35e6f625fc257ef677ccbc50696b9db0eafec Mon Sep 17 00:00:00 2001
From: Doug Coleman <doug.coleman@gmail.com>
Date: Thu, 26 Feb 2009 19:58:25 -0600
Subject: [PATCH 10/18] better io.mmap test

---
 basis/io/mmap/mmap-tests.factor | 18 ++++--------------
 1 file changed, 4 insertions(+), 14 deletions(-)

diff --git a/basis/io/mmap/mmap-tests.factor b/basis/io/mmap/mmap-tests.factor
index 70a1869bd0..a4d55f3c1e 100644
--- a/basis/io/mmap/mmap-tests.factor
+++ b/basis/io/mmap/mmap-tests.factor
@@ -11,21 +11,11 @@ IN: io.mmap.tests
 [ "mmap-test-file.txt" temp-file delete-file ] ignore-errors
 
 
-[ ]
-[ "mmap-empty-file.txt" temp-file touch-file ] unit-test
+[ "mmap-empty-file.txt" temp-file delete-file ] ignore-errors
+[ ] [ "mmap-empty-file.txt" temp-file touch-file ] unit-test
 
-! Test for leaking resources bug on Unix
-[ ]
 [
-    100000 [
-        [
-            "mmap-empty-file.txt" temp-file [
-                drop
-            ] with-mapped-file
-        ] [ dup bad-mmap-size? [ drop ] [ rethrow ] if ] recover
-    ] times
-
-    "asdf" "mmap-asdf-file.txt" temp-file [ ascii set-file-contents ] keep [
+    "mmap-empty-file.txt" temp-file [
         drop
     ] with-mapped-file
-] unit-test
+] [ bad-mmap-size? ] must-fail-with

From e1b4e8c66fcf81a5e3c60a47468e7fa8f53a27ea Mon Sep 17 00:00:00 2001
From: Doug Coleman <doug.coleman@gmail.com>
Date: Thu, 26 Feb 2009 21:39:53 -0600
Subject: [PATCH 11/18] id3 outputs id3v2-info objects or f now. id3v1 info is
 turned into id3v2.  you can access the title, album, etc fields by calling
 id3-title, etc, but also other frames are saved and can be accessed

---
 extra/id3/authors.txt      |   2 +-
 extra/id3/id3-docs.factor  |   2 +-
 extra/id3/id3-tests.factor |  59 +++---
 extra/id3/id3.factor       | 405 +++++++++++++++++++------------------
 4 files changed, 246 insertions(+), 222 deletions(-)

diff --git a/extra/id3/authors.txt b/extra/id3/authors.txt
index ece617b969..2bd5c6037e 100644
--- a/extra/id3/authors.txt
+++ b/extra/id3/authors.txt
@@ -1,2 +1,2 @@
 Tim Wawrzynczak
-
+Doug Coleman
diff --git a/extra/id3/id3-docs.factor b/extra/id3/id3-docs.factor
index a54bba1629..d171d03798 100644
--- a/extra/id3/id3-docs.factor
+++ b/extra/id3/id3-docs.factor
@@ -6,7 +6,7 @@ IN: id3
 HELP: file-id3-tags
 { $values 
     { "path" "a path string" } 
-    { "object/f" "a tuple storing ID3 metadata or f" } }
+    { "id3v2-info/f" "a tuple storing ID3v2 metadata or f" } }
     { $description "Return a tuple containing the ID3 information parsed out of the MP3 file, or " { $link f } " if no metadata is present.  Currently, the parser supports the following tags: "
       $nl { $link title>> }
       $nl { $link artist>> }
diff --git a/extra/id3/id3-tests.factor b/extra/id3/id3-tests.factor
index bcdc312440..eabbf00ad7 100644
--- a/extra/id3/id3-tests.factor
+++ b/extra/id3/id3-tests.factor
@@ -1,35 +1,42 @@
 ! Copyright (C) 2009 Tim Wawrzynczak
 ! See http://factorcode.org/license.txt for BSD license.
-USING: tools.test id3 id3.private ;
+USING: tools.test id3 combinators ;
 IN: id3.tests
 
-[
-    T{ id3-info
-       { title "BLAH" }
-       { artist "ARTIST" }
-       { album "ALBUM" }
-       { year "2009" }
-       { comment "COMMENT" }
-       { genre "Bluegrass" }
-    }
-] [ "resource:extra/id3/tests/blah.mp3" file-id3-tags ] unit-test
+: id3-params ( id3 -- title artist album year comment genre )
+    {
+        [ id3-title ]
+        [ id3-artist ]
+        [ id3-album ]
+        [ id3-year ]
+        [ id3-comment ]
+        [ id3-genre ]
+    } cleave ;
 
 [
-    T{ id3-info
-       { title "Anthem of the Trinity" }
-       { artist "Terry Riley" }
-       { album "Shri Camel" }
-       { genre "Classical" }
-    }
-] [ "resource:extra/id3/tests/blah2.mp3" file-id3-tags ] unit-test
+   "BLAH"
+   "ARTIST"
+   "ALBUM"
+   "2009"
+   "COMMENT"
+   "Bluegrass"
+] [ "resource:extra/id3/tests/blah.mp3" file-id3-tags id3-params ] unit-test
+
+[
+    "Anthem of the Trinity"
+    "Terry Riley"
+    "Shri Camel"
+    f
+    f
+    "Classical"
+] [ "resource:extra/id3/tests/blah2.mp3" file-id3-tags id3-params ] unit-test
 
 [    
-    T{ id3-info
-       { title "Stormy Weather" }
-       { artist "Frank Sinatra" }
-       { album "Night and Day Frank Sinatra" }
-       { comment "eng, AG# 08E1C12E" }
-       { genre "Big Band" }
-    }
-] [ "resource:extra/id3/tests/blah3.mp3" file-id3-tags ] unit-test
+   "Stormy Weather"
+   "Frank Sinatra"
+   "Night and Day Frank Sinatra"
+    f
+   "eng, AG# 08E1C12E"
+   "Big Band"
+] [ "resource:extra/id3/tests/blah3.mp3" file-id3-tags id3-params ] unit-test
 
diff --git a/extra/id3/id3.factor b/extra/id3/id3.factor
index abfe01c3e4..fba6188b1e 100644
--- a/extra/id3/id3.factor
+++ b/extra/id3/id3.factor
@@ -1,145 +1,144 @@
-! Copyright (C) 2009 Tim Wawrzynczak
+! Copyright (C) 2009 Tim Wawrzynczak, Doug Coleman.
 ! See http://factorcode.org/license.txt for BSD license.
 USING: sequences io io.encodings.binary io.files io.pathnames
 strings kernel math io.mmap io.mmap.uchar accessors syntax
 combinators math.ranges unicode.categories byte-arrays
 io.encodings.string io.encodings.utf8 assocs math.parser
-combinators.short-circuit fry ;
+combinators.short-circuit fry namespaces multiline
+combinators.smart splitting ;
 IN: id3
 
 <PRIVATE
 
 CONSTANT: genres
-  H{
-    { 0 "Blues" }
-    { 1 "Classic Rock" }
-    { 2 "Country" }
-    { 3 "Dance" }
-    { 4 "Disco" }
-    { 5 "Funk" }
-    { 6 "Grunge" }
-    { 7 "Hip-Hop" }
-    { 8 "Jazz" }
-    { 9 "Metal" }
-    { 10 "New Age" }
-    { 11 "Oldies" }
-    { 12 "Other" }
-    { 13 "Pop" }
-    { 14 "R&B" }
-    { 15 "Rap" }
-    { 16 "Reggae" }
-    { 17 "Rock" }
-    { 18 "Techno" }
-    { 19 "Industrial" }
-    { 20 "Alternative" }
-    { 21 "Ska" }
-    { 22 "Death Metal" }
-    { 23 "Pranks" }
-    { 24 "Soundtrack" }
-    { 25 "Euro-Techno" }
-    { 26 "Ambient" }
-    { 27 "Trip-Hop" }
-    { 28 "Vocal" }
-    { 29 "Jazz+Funk" }
-    { 30 "Fusion" }
-    { 31 "Trance" }
-    { 32 "Classical" }
-    { 33 "Instrumental" }
-    { 34 "Acid" }
-    { 35 "House" }
-    { 36 "Game" }
-    { 37 "Sound Clip" }
-    { 38 "Gospel" }
-    { 39 "Noise" }
-    { 40 "AlternRock" }
-    { 41 "Bass" }
-    { 42 "Soul" }
-    { 43 "Punk" }
-    { 44 "Space" }
-    { 45 "Meditative" }
-    { 46 "Instrumental Pop" }
-    { 47 "Instrumental Rock" }
-    { 48 "Ethnic" }
-    { 49 "Gothic" }
-    { 50 "Darkwave" }
-    { 51 "Techno-Industrial" }
-    { 52 "Electronic" }
-    { 53 "Pop-Folk" }
-    { 54 "Eurodance" }
-    { 55 "Dream" }
-    { 56 "Southern Rock" }
-    { 57 "Comedy" }
-    { 58 "Cult" }
-    { 59 "Gangsta" }
-    { 60 "Top 40" }
-    { 61 "Christian Rap" }
-    { 62 "Pop/Funk" }
-    { 63 "Jungle" }
-    { 64 "Native American" }
-    { 65 "Cabaret" }
-    { 66 "New Wave" }
-    { 67 "Psychedelic" }
-    { 68 "Rave" }
-    { 69 "Showtunes" }
-    { 70 "Trailer" }
-    { 71 "Lo-Fi" }
-    { 72 "Tribal" }
-    { 73 "Acid Punk" }
-    { 74 "Acid Jazz" }
-    { 75 "Polka" }
-    { 76 "Retro" }
-    { 77 "Musical" }
-    { 78 "Rock & Roll" }
-    { 79 "Hard Rock" }
-    { 80 "Folk" }
-    { 81 "Folk-Rock" }
-    { 82 "National Folk" }
-    { 83 "Swing" }
-    { 84 "Fast Fusion" }
-    { 85 "Bebop" }
-    { 86 "Latin" }
-    { 87 "Revival" }
-    { 88 "Celtic" }
-    { 89 "Bluegrass" }
-    { 90 "Avantgarde" }
-    { 91 "Gothic Rock" }
-    { 92 "Progressive Rock" }
-    { 93 "Psychedelic Rock" }
-    { 94 "Symphonic Rock" }
-    { 95 "Slow Rock" }
-    { 96 "Big Band" }
-    { 97 "Chorus" }
-    { 98 "Easy Listening" }
-    { 99 "Acoustic" }
-    { 100 "Humour" }
-    { 101 "Speech" }
-    { 102 "Chanson" }
-    { 103 "Opera" }
-    { 104 "Chamber Music" }
-    { 105 "Sonata" }
-    { 106 "Symphony" }
-    { 107 "Booty Bass" }
-    { 108 "Primus" }
-    { 109 "Porn Groove" }
-    { 110 "Satire" }
-    { 111 "Slow Jam" }
-    { 112 "Club" }
-    { 113 "Tango" }
-    { 114 "Samba" }
-    { 115 "Folklore" }
-    { 116 "Ballad" }
-    { 117 "Power Ballad" }
-    { 118 "Rhythmic Soul" }
-    { 119 "Freestyle" }
-    { 120 "Duet" }
-    { 121 "Punk Rock" }
-    { 122 "Drum Solo" }
-    { 123 "A capella" }
-    { 124 "Euro-House" }
-    { 125 "Dance Hall" }
-} ! end genre hashtable
-
-! tuples
+    {
+        "Blues" 
+        "Classic Rock" 
+        "Country" 
+        "Dance" 
+        "Disco" 
+        "Funk" 
+        "Grunge" 
+        "Hip-Hop" 
+        "Jazz" 
+        "Metal" 
+        "New Age" 
+        "Oldies" 
+        "Other" 
+        "Pop" 
+        "R&B" 
+        "Rap" 
+        "Reggae" 
+        "Rock" 
+        "Techno" 
+        "Industrial" 
+        "Alternative" 
+        "Ska" 
+        "Death Metal" 
+        "Pranks" 
+        "Soundtrack" 
+        "Euro-Techno" 
+        "Ambient" 
+        "Trip-Hop" 
+        "Vocal" 
+        "Jazz+Funk" 
+        "Fusion" 
+        "Trance" 
+        "Classical" 
+        "Instrumental" 
+        "Acid" 
+        "House" 
+        "Game" 
+        "Sound Clip" 
+        "Gospel" 
+        "Noise" 
+        "AlternRock" 
+        "Bass" 
+        "Soul" 
+        "Punk" 
+        "Space" 
+        "Meditative" 
+        "Instrumental Pop" 
+        "Instrumental Rock" 
+        "Ethnic" 
+        "Gothic" 
+        "Darkwave" 
+        "Techno-Industrial" 
+        "Electronic" 
+        "Pop-Folk" 
+        "Eurodance" 
+        "Dream" 
+        "Southern Rock" 
+        "Comedy" 
+        "Cult" 
+        "Gangsta" 
+        "Top 40" 
+        "Christian Rap" 
+        "Pop/Funk" 
+        "Jungle" 
+        "Native American" 
+        "Cabaret" 
+        "New Wave" 
+        "Psychedelic" 
+        "Rave" 
+        "Showtunes" 
+        "Trailer" 
+        "Lo-Fi" 
+        "Tribal" 
+        "Acid Punk" 
+        "Acid Jazz" 
+        "Polka" 
+        "Retro" 
+        "Musical" 
+        "Rock & Roll" 
+        "Hard Rock" 
+        "Folk" 
+        "Folk-Rock" 
+        "National Folk" 
+        "Swing" 
+        "Fast Fusion" 
+        "Bebop" 
+        "Latin" 
+        "Revival" 
+        "Celtic" 
+        "Bluegrass" 
+        "Avantgarde" 
+        "Gothic Rock" 
+        "Progressive Rock" 
+        "Psychedelic Rock" 
+        "Symphonic Rock" 
+        "Slow Rock" 
+        "Big Band" 
+        "Chorus" 
+        "Easy Listening" 
+        "Acoustic" 
+        "Humour" 
+        "Speech" 
+        "Chanson" 
+        "Opera" 
+        "Chamber Music" 
+        "Sonata" 
+        "Symphony" 
+        "Booty Bass" 
+        "Primus" 
+        "Porn Groove" 
+        "Satire" 
+        "Slow Jam" 
+        "Club" 
+        "Tango" 
+        "Samba" 
+        "Folklore" 
+        "Ballad" 
+        "Power Ballad" 
+        "Rhythmic Soul" 
+        "Freestyle" 
+        "Duet" 
+        "Punk Rock" 
+        "Drum Solo" 
+        "A capella" 
+        "Euro-House"
+        "Dance Hall"
+    }
 
 TUPLE: header version flags size ;
 
@@ -151,42 +150,58 @@ TUPLE: id3-info title artist album year comment genre ;
 
 : <id3-info> ( -- object ) id3-info new ;
 
-: <id3v2-info> ( header frames -- object ) id3v2-info boa ;
+: <id3v2-info> ( header frames -- object )
+    [ [ frame-id>> ] keep ] H{ } map>assoc
+    id3v2-info boa ;
 
 : <header> ( -- object ) header new ;
 
 : <frame> ( -- object ) frame new ;
 
-! utility words
-
-: id3v2? ( mmap -- ? )
-    "ID3" head? ;
+: id3v2? ( mmap -- ? ) "ID3" head? ; inline
 
 : id3v1? ( mmap -- ? )
-    { [ length 128 >= ] [ 128 tail-slice* "TAG" head? ] } 1&& ;
+    { [ length 128 >= ] [ 128 tail-slice* "TAG" head? ] } 1&& ; inline
+
+: id3v1-frame ( string key -- frame )
+    <frame>
+        swap >>frame-id
+        swap >>data ;
+
+: id3v1>id3v2 ( id3v1 -- id3v2 )
+    [
+        {
+            [ title>> "TIT2" id3v1-frame ]
+            [ artist>> "TPE1" id3v1-frame ]
+            [ album>> "TALB" id3v1-frame ]
+            [ year>> "TYER" id3v1-frame ]
+            [ comment>> "COMM" id3v1-frame ]
+            [ genre>> "TCON" id3v1-frame ]
+        } cleave
+    ] output>array f swap <id3v2-info> ;
 
 : >28bitword ( seq -- int )
-    0 [ swap 7 shift bitor ] reduce ;
+    0 [ [ 7 shift ] dip bitor ] reduce ; inline
 
 : filter-text-data ( data -- filtered )
-    [ printable? ] filter ;
+    [ printable? ] filter ; inline
 
 ! frame details stuff
 
 : valid-frame-id? ( id -- ? )
-    [ [ digit? ] [ LETTER? ] bi or ] all? ;
+    [ { [ digit? ] [ LETTER? ] } 1|| ] all? ; inline
 
 : read-frame-id ( mmap -- id )
-    4 head-slice ;
+    4 head-slice ; inline
 
 : read-frame-size ( mmap -- size )
-    [ 4 8 ] dip subseq ;
+    [ 4 8 ] dip subseq ; inline
 
 : read-frame-flags ( mmap -- flags )
-    [ 8 10 ] dip subseq ;
+    [ 8 10 ] dip subseq ; inline
 
 : read-frame-data ( frame mmap -- frame data )
-    [ 10 over size>> 10 + ] dip <slice> filter-text-data ;
+    [ 10 over size>> 10 + ] dip <slice> filter-text-data ; inline
 
 ! read whole frames
 
@@ -200,10 +215,11 @@ TUPLE: id3-info title artist album year comment genre ;
     } cleave ;
 
 : read-frame ( mmap -- frame/f )
-    dup read-frame-id valid-frame-id? [ (read-frame) ] [ drop f ] if ;
+    dup read-frame-id valid-frame-id?
+    [ (read-frame) ] [ drop f ] if ;
 
 : remove-frame ( mmap frame -- mmap )
-    size>> 10 + tail-slice ;
+    size>> 10 + tail-slice ; inline
 
 : read-frames ( mmap -- frames )
     [ dup read-frame dup ]
@@ -213,13 +229,12 @@ TUPLE: id3-info title artist album year comment genre ;
 ! header stuff
 
 : read-header-supported-version? ( mmap -- ? )
-    3 tail-slice [ { 4 } head? ] [ { 3 } head? ] bi or ;
+    3 tail-slice first { 3 4 } member? ; inline
 
-: read-header-flags ( mmap -- flags )
-    5 swap nth ;
+: read-header-flags ( mmap -- flags ) 5 swap nth ; inline
 
 : read-header-size ( mmap -- size )
-    [ 6 10 ] dip <slice> >28bitword ;
+    [ 6 10 ] dip <slice> >28bitword ; inline
 
 : read-v2-header ( mmap -- id3header )
     [ <header> ] dip
@@ -227,51 +242,30 @@ TUPLE: id3-info title artist album year comment genre ;
         [ read-header-supported-version? >>version ]
         [ read-header-flags >>flags ]
         [ read-header-size >>size ]
-    } cleave ;
+    } cleave ; inline
 
 : drop-header ( mmap -- seq1 seq2 )
-    dup 10 tail-slice swap ;
+    [ 10 tail-slice ] [ ] bi ; inline
 
-: frame-tag ( frame string -- tag/f )
-    '[ frame-id>> _ = ] find nip ; inline
-
-: parse-frames ( id3v2-info -- id3-info )
-    [ <id3-info> ] dip frames>>
-    {
-        [ "TIT2" frame-tag [ data>> >>title ] when* ]
-        [ "TALB" frame-tag [ data>> >>album ] when* ]
-        [ "TPE1" frame-tag [ data>> >>artist ] when* ]
-        [ "TCON" frame-tag [ data>> [ [ digit? ] filter string>number ] keep swap [ genres at nip ] when*
-          >>genre ] when* ]
-        [ "COMM" frame-tag [ data>> >>comment ] when* ]
-        [ "TYER" frame-tag [ data>> >>year ] when* ]
-    } cleave ;
-
-: read-v2-tag-data ( seq -- id3-info )
-    drop-header read-v2-header swap read-frames <id3v2-info> parse-frames ;
+: read-v2-tag-data ( seq -- id3v2-info )
+    drop-header read-v2-header
+    swap read-frames <id3v2-info> ; inline
     
 ! v1 information
 
-: skip-to-v1-data ( seq -- seq )
-    125 tail-slice* ;
+: skip-to-v1-data ( seq -- seq ) 125 tail-slice* ; inline
 
-: read-title ( seq -- title )
-    30 head-slice ;
+: read-title ( seq -- title ) 30 head-slice ; inline
 
-: read-artist ( seq -- title )
-    [ 30 60 ] dip subseq ;
+: read-artist ( seq -- title ) [ 30 60 ] dip subseq ; inline
 
-: read-album ( seq -- album )
-    [ 60 90 ] dip subseq ;
+: read-album ( seq -- album ) [ 60 90 ] dip subseq ; inline
 
-: read-year ( seq -- year )
-    [ 90 94 ] dip subseq ;
+: read-year ( seq -- year ) [ 90 94 ] dip subseq ; inline
 
-: read-comment ( seq -- comment )
-    [ 94 124 ] dip subseq ;
+: read-comment ( seq -- comment ) [ 94 124 ] dip subseq ; inline
 
-: read-genre ( seq -- genre )
-    [ 124 ] dip nth ;
+: read-genre ( seq -- genre ) [ 124 ] dip nth ; inline
 
 : (read-v1-tag-data) ( seq -- mp3-file )
     [ <id3-info> ] dip
@@ -281,23 +275,46 @@ TUPLE: id3-info title artist album year comment genre ;
         [ read-album   utf8 decode  filter-text-data >>album   ]
         [ read-year    utf8 decode  filter-text-data >>year    ]
         [ read-comment utf8 decode  filter-text-data >>comment ]
-        [ read-genre   >fixnum       genres at       >>genre ]
-    } cleave ;
+        [ read-genre   number>string                 >>genre   ]
+    } cleave ; inline
 
 : read-v1-tag-data ( seq -- mp3-file )
-    skip-to-v1-data (read-v1-tag-data) ;
+    skip-to-v1-data (read-v1-tag-data) ; inline
+
+: parse-genre ( string -- n/f )
+    dup "(" ?head-slice drop ")" ?tail-slice drop
+    string>number dup number? [
+        genres ?nth swap or
+    ] [
+        drop
+    ] if ; inline
 
 PRIVATE>
 
-! public interface
+: frame-named ( id3 name quot -- obj )
+    [ swap frames>> at* ] dip
+    [ data>> ] prepose [ drop f ] if ; inline
 
-: file-id3-tags ( path -- object/f )
+: id3-title ( id3 -- title/f ) "TIT2" [ ] frame-named ; inline
+
+: id3-artist ( id3 -- artist/f ) "TPE1" [ ] frame-named ; inline
+
+: id3-album ( id3 -- album/f ) "TALB" [ ] frame-named ; inline
+
+: id3-year ( id3 -- year/f ) "TYER" [ ] frame-named ; inline
+
+: id3-comment ( id3 -- comment/f ) "COMM" [ ] frame-named ; inline
+
+: id3-genre ( id3 -- genre/f )
+    "TCON" [ parse-genre ] frame-named ; inline
+
+: id3-frame ( id3 key -- value/f ) [ ] frame-named ; inline
+
+: file-id3-tags ( path -- id3v2-info/f )
     [
         {
-            { [ dup id3v2? ] [ read-v2-tag-data ] } ! ( ? -- id3v2 )
-            { [ dup id3v1? ] [ read-v1-tag-data ] } ! ( ? -- id3-info )
-            [ drop f ] ! ( mmap -- f )
+            { [ dup id3v2? ] [ read-v2-tag-data ] }
+            { [ dup id3v1? ] [ read-v1-tag-data id3v1>id3v2 ] }
+            [ drop f ]
         } cond
     ] with-mapped-uchar-file ;
-
-! end

From de9154fc5e7cf979e1d59205433c4b2d2ae25b44 Mon Sep 17 00:00:00 2001
From: Doug Coleman <doug.coleman@gmail.com>
Date: Thu, 26 Feb 2009 21:50:01 -0600
Subject: [PATCH 12/18] make find-all-files and find-in-program-files not take
 the traversal method

---
 basis/io/directories/search/search-docs.factor     | 2 +-
 basis/io/directories/search/search-tests.factor    | 2 +-
 basis/io/directories/search/search.factor          | 3 ++-
 basis/io/directories/search/windows/windows.factor | 4 ++--
 4 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/basis/io/directories/search/search-docs.factor b/basis/io/directories/search/search-docs.factor
index 99135b7953..818899606d 100644
--- a/basis/io/directories/search/search-docs.factor
+++ b/basis/io/directories/search/search-docs.factor
@@ -38,7 +38,7 @@ HELP: find-in-directories
 
 HELP: find-all-files
 { $values
-     { "path" "a pathname string" } { "bfs?" "a boolean, breadth-first or depth-first" } { "quot" quotation }
+     { "path" "a pathname string" } { "quot" quotation }
      { "paths/f" "a sequence of pathname strings or f" }
 }
 { $description "Finds all files in the input directory matching the predicate quotation in a breadth-first or depth-first traversal." } ;
diff --git a/basis/io/directories/search/search-tests.factor b/basis/io/directories/search/search-tests.factor
index a8b8bf9215..ba1b9cdbe1 100644
--- a/basis/io/directories/search/search-tests.factor
+++ b/basis/io/directories/search/search-tests.factor
@@ -5,6 +5,6 @@ IN: io.directories.search.tests
 [ t ] [
     [
         10 [ "io.paths.test" "gogogo" make-unique-file ] replicate
-        current-temporary-directory get t [ ] find-all-files
+        current-temporary-directory get [ ] find-all-files
     ] with-unique-directory drop [ natural-sort ] bi@ =
 ] unit-test
diff --git a/basis/io/directories/search/search.factor b/basis/io/directories/search/search.factor
index b56fb7b6a3..ee8fd129a7 100755
--- a/basis/io/directories/search/search.factor
+++ b/basis/io/directories/search/search.factor
@@ -51,7 +51,8 @@ PRIVATE>
         [ keep and ] curry iterate-directory
     ] [ drop f ] recover ; inline
 
-: find-all-files ( path bfs? quot: ( obj -- ? ) -- paths/f )
+: find-all-files ( path quot: ( obj -- ? ) -- paths/f )
+    f swap
     '[
         _ _ _ [ <directory-iterator> ] dip
         pusher [ [ f ] compose iterate-directory drop ] dip
diff --git a/basis/io/directories/search/windows/windows.factor b/basis/io/directories/search/windows/windows.factor
index 755710befd..cda9403417 100644
--- a/basis/io/directories/search/windows/windows.factor
+++ b/basis/io/directories/search/windows/windows.factor
@@ -7,7 +7,7 @@ IN: io.directories.search.windows
 : program-files-directories ( -- array )
     program-files program-files-x86 2array harvest ; inline
 
-: find-in-program-files ( base-directory bfs? quot -- path )
-    [
+: find-in-program-files ( base-directory quot -- path )
+    t swap [
         [ program-files-directories ] dip '[ _ append-path ] map
     ] 2dip find-in-directories ; inline

From efe701af9b614255ab1ca75a9806e06e5abc8fda Mon Sep 17 00:00:00 2001
From: Doug Coleman <doug.coleman@gmail.com>
Date: Thu, 26 Feb 2009 21:50:20 -0600
Subject: [PATCH 13/18] fix editors for find-in-program-files change

---
 basis/editors/editpadlite/editpadlite.factor | 2 +-
 basis/editors/editpadpro/editpadpro.factor   | 2 +-
 basis/editors/editplus/editplus.factor       | 2 +-
 basis/editors/emacs/windows/windows.factor   | 4 ++--
 basis/editors/emeditor/emeditor.factor       | 2 +-
 basis/editors/etexteditor/etexteditor.factor | 2 +-
 basis/editors/gvim/windows/windows.factor    | 2 +-
 basis/editors/notepadpp/notepadpp.factor     | 2 +-
 basis/editors/scite/scite.factor             | 4 ++--
 basis/editors/ted-notepad/ted-notepad.factor | 2 +-
 basis/editors/textpad/textpad.factor         | 2 +-
 basis/editors/ultraedit/ultraedit.factor     | 2 +-
 basis/editors/wordpad/wordpad.factor         | 2 +-
 13 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/basis/editors/editpadlite/editpadlite.factor b/basis/editors/editpadlite/editpadlite.factor
index d487ca776f..043ef7ef27 100644
--- a/basis/editors/editpadlite/editpadlite.factor
+++ b/basis/editors/editpadlite/editpadlite.factor
@@ -5,7 +5,7 @@ IN: editors.editpadlite
 
 : editpadlite-path ( -- path )
     \ editpadlite-path get-global [
-        "JGsoft" t [ >lower "editpadlite.exe" tail? ] find-in-program-files
+        "JGsoft" [ >lower "editpadlite.exe" tail? ] find-in-program-files
         [ "editpadlite.exe" ] unless*
     ] unless* ;
 
diff --git a/basis/editors/editpadpro/editpadpro.factor b/basis/editors/editpadpro/editpadpro.factor
index 09bfd69de8..571c20fd6a 100644
--- a/basis/editors/editpadpro/editpadpro.factor
+++ b/basis/editors/editpadpro/editpadpro.factor
@@ -5,7 +5,7 @@ IN: editors.editpadpro
 
 : editpadpro-path ( -- path )
     \ editpadpro-path get-global [
-        "JGsoft" t [ >lower "editpadpro.exe" tail? ] find-in-program-files
+        "JGsoft" [ >lower "editpadpro.exe" tail? ] find-in-program-files
         [ "editpadpro.exe" ] unless*
     ] unless* ;
 
diff --git a/basis/editors/editplus/editplus.factor b/basis/editors/editplus/editplus.factor
index affbcd4eb6..a3150dc961 100644
--- a/basis/editors/editplus/editplus.factor
+++ b/basis/editors/editplus/editplus.factor
@@ -5,7 +5,7 @@ IN: editors.editplus
 
 : editplus-path ( -- path )
     \ editplus-path get-global [
-        "EditPlus 2" t [ "editplus.exe" tail? ] find-in-program-files
+        "EditPlus 2" [ "editplus.exe" tail? ] find-in-program-files
         [ "editplus.exe" ] unless*
     ] unless* ;
 
diff --git a/basis/editors/emacs/windows/windows.factor b/basis/editors/emacs/windows/windows.factor
index e18c39ed60..91d6e878e4 100755
--- a/basis/editors/emacs/windows/windows.factor
+++ b/basis/editors/emacs/windows/windows.factor
@@ -6,7 +6,7 @@ IN: editors.emacs.windows
 
 M: windows default-emacsclient
     {
-        [ "Emacs" t [ "emacsclientw.exe" tail? ] find-in-program-files ]
-        [ "Emacs" t [ "emacsclient.exe" tail? ] find-in-program-files ]
+        [ "Emacs" [ "emacsclientw.exe" tail? ] find-in-program-files ]
+        [ "Emacs" [ "emacsclient.exe" tail? ] find-in-program-files ]
         [ "emacsclient.exe" ]
     } 0|| ;
diff --git a/basis/editors/emeditor/emeditor.factor b/basis/editors/emeditor/emeditor.factor
index 52c52bbb8b..3380f5c974 100644
--- a/basis/editors/emeditor/emeditor.factor
+++ b/basis/editors/emeditor/emeditor.factor
@@ -5,7 +5,7 @@ IN: editors.emeditor
 
 : emeditor-path ( -- path )
     \ emeditor-path get-global [
-        "EmEditor" t [ "EmEditor.exe" tail? ] find-in-program-files
+        "EmEditor" [ "EmEditor.exe" tail? ] find-in-program-files
         [ "EmEditor.exe" ] unless*
     ] unless* ;
 
diff --git a/basis/editors/etexteditor/etexteditor.factor b/basis/editors/etexteditor/etexteditor.factor
index 37c8d1b572..8b76b3b473 100755
--- a/basis/editors/etexteditor/etexteditor.factor
+++ b/basis/editors/etexteditor/etexteditor.factor
@@ -6,7 +6,7 @@ IN: editors.etexteditor
 
 : etexteditor-path ( -- str )
     \ etexteditor-path get-global [
-        "e" t [ "e.exe" tail? ] find-in-program-files
+        "e" [ "e.exe" tail? ] find-in-program-files
         [ "e" ] unless*
     ] unless* ;
 
diff --git a/basis/editors/gvim/windows/windows.factor b/basis/editors/gvim/windows/windows.factor
index 4edc13b90c..3fe228a403 100644
--- a/basis/editors/gvim/windows/windows.factor
+++ b/basis/editors/gvim/windows/windows.factor
@@ -5,6 +5,6 @@ IN: editors.gvim.windows
 
 M: windows gvim-path
     \ gvim-path get-global [
-        "vim" t [ "gvim.exe" tail? ] find-in-program-files
+        "vim" [ "gvim.exe" tail? ] find-in-program-files
         [ "gvim.exe" ] unless*
     ] unless* ;
diff --git a/basis/editors/notepadpp/notepadpp.factor b/basis/editors/notepadpp/notepadpp.factor
index 1c856bd761..7b0f2bb72a 100644
--- a/basis/editors/notepadpp/notepadpp.factor
+++ b/basis/editors/notepadpp/notepadpp.factor
@@ -4,7 +4,7 @@ IN: editors.notepadpp
 
 : notepadpp-path ( -- path )
     \ notepadpp-path get-global [
-        "notepad++" t [ "notepad++.exe" tail? ] find-in-program-files
+        "notepad++" [ "notepad++.exe" tail? ] find-in-program-files
         [ "notepad++.exe" ] unless*
     ] unless* ;
 
diff --git a/basis/editors/scite/scite.factor b/basis/editors/scite/scite.factor
index fc7e9e319e..7e8a540b73 100644
--- a/basis/editors/scite/scite.factor
+++ b/basis/editors/scite/scite.factor
@@ -7,11 +7,11 @@ IN: editors.scite
 
 : scite-path ( -- path )
     \ scite-path get-global [
-        "Scintilla Text Editor" t
+        "Scintilla Text Editor"
         [ >lower "scite.exe" tail? ] find-in-program-files
 
         [
-            "SciTE Source Code Editor" t
+            "SciTE Source Code Editor"
             [ >lower "scite.exe" tail? ] find-in-program-files
         ] unless*
         [ "scite.exe" ] unless*
diff --git a/basis/editors/ted-notepad/ted-notepad.factor b/basis/editors/ted-notepad/ted-notepad.factor
index 301e82225c..6f954febe8 100644
--- a/basis/editors/ted-notepad/ted-notepad.factor
+++ b/basis/editors/ted-notepad/ted-notepad.factor
@@ -4,7 +4,7 @@ IN: editors.ted-notepad
 
 : ted-notepad-path ( -- path )
     \ ted-notepad-path get-global [
-        "TED Notepad" t [ "TedNPad.exe" tail? ] find-in-program-files
+        "TED Notepad" [ "TedNPad.exe" tail? ] find-in-program-files
         [ "TedNPad.exe" ] unless*
     ] unless* ;
 
diff --git a/basis/editors/textpad/textpad.factor b/basis/editors/textpad/textpad.factor
index ca9d5c486a..925f75400f 100644
--- a/basis/editors/textpad/textpad.factor
+++ b/basis/editors/textpad/textpad.factor
@@ -5,7 +5,7 @@ IN: editors.textpad
 
 : textpad-path ( -- path )
     \ textpad-path get-global [
-        "TextPad 5" t [ "TextPad.exe" tail? ] find-in-program-files
+        "TextPad 5" [ "TextPad.exe" tail? ] find-in-program-files
         [ "TextPad.exe" ] unless*
     ] unless* ;
 
diff --git a/basis/editors/ultraedit/ultraedit.factor b/basis/editors/ultraedit/ultraedit.factor
index b5bc229743..3069d78925 100644
--- a/basis/editors/ultraedit/ultraedit.factor
+++ b/basis/editors/ultraedit/ultraedit.factor
@@ -4,7 +4,7 @@ IN: editors.ultraedit
 
 : ultraedit-path ( -- path )
     \ ultraedit-path get-global [
-        "IDM Computer Solutions" t [ "uedit32.exe" tail? ] find-in-program-files
+        "IDM Computer Solutions" [ "uedit32.exe" tail? ] find-in-program-files
         [ "uedit32.exe" ] unless*
     ] unless* ;
 
diff --git a/basis/editors/wordpad/wordpad.factor b/basis/editors/wordpad/wordpad.factor
index ef670d5d28..103b69ba4c 100644
--- a/basis/editors/wordpad/wordpad.factor
+++ b/basis/editors/wordpad/wordpad.factor
@@ -4,7 +4,7 @@ IN: editors.wordpad
 
 : wordpad-path ( -- path )
     \ wordpad-path get [
-        "Windows NT\\Accessories" t
+        "Windows NT\\Accessories"
         [ "wordpad.exe" tail? ] find-in-program-files
     ] unless* ;
 

From 63c9b1a6b8d0dae85a1c0c7aba6ad7a81c9560c2 Mon Sep 17 00:00:00 2001
From: Doug Coleman <doug.coleman@gmail.com>
Date: Thu, 26 Feb 2009 22:33:43 -0600
Subject: [PATCH 14/18] try to detect the encoding for id3 headers.  need to
 try this on some mp3s from the wild

---
 extra/id3/id3.factor | 29 +++++++++++++++++------------
 1 file changed, 17 insertions(+), 12 deletions(-)

diff --git a/extra/id3/id3.factor b/extra/id3/id3.factor
index fba6188b1e..a4adeedaa5 100644
--- a/extra/id3/id3.factor
+++ b/extra/id3/id3.factor
@@ -3,9 +3,9 @@
 USING: sequences io io.encodings.binary io.files io.pathnames
 strings kernel math io.mmap io.mmap.uchar accessors syntax
 combinators math.ranges unicode.categories byte-arrays
-io.encodings.string io.encodings.utf8 assocs math.parser
+io.encodings.string io.encodings.utf16 assocs math.parser
 combinators.short-circuit fry namespaces multiline
-combinators.smart splitting ;
+combinators.smart splitting io.encodings.ascii ;
 IN: id3
 
 <PRIVATE
@@ -205,13 +205,18 @@ TUPLE: id3-info title artist album year comment genre ;
 
 ! read whole frames
 
+: decode-text ( string -- string' )
+    dup 2 short head
+    { { HEX: ff HEX: fe } { HEX: fe HEX: ff } } member?
+    utf16 ascii ? decode ; inline
+
 : (read-frame) ( mmap -- frame )
     [ <frame> ] dip
     {
-        [ read-frame-id    utf8 decode  >>frame-id ]
-        [ read-frame-flags >byte-array  >>flags ]
-        [ read-frame-size  >28bitword   >>size ]
-        [ read-frame-data  utf8 decode  >>data ]
+        [ read-frame-id decode-text >>frame-id ]
+        [ read-frame-flags >byte-array >>flags ]
+        [ read-frame-size >28bitword >>size ]
+        [ read-frame-data decode-text >>data ]
     } cleave ;
 
 : read-frame ( mmap -- frame/f )
@@ -270,12 +275,12 @@ TUPLE: id3-info title artist album year comment genre ;
 : (read-v1-tag-data) ( seq -- mp3-file )
     [ <id3-info> ] dip
     {
-        [ read-title   utf8 decode  filter-text-data >>title   ]
-        [ read-artist  utf8 decode  filter-text-data >>artist  ]
-        [ read-album   utf8 decode  filter-text-data >>album   ]
-        [ read-year    utf8 decode  filter-text-data >>year    ]
-        [ read-comment utf8 decode  filter-text-data >>comment ]
-        [ read-genre   number>string                 >>genre   ]
+        [ read-title   decode-text filter-text-data >>title   ]
+        [ read-artist  decode-text filter-text-data >>artist  ]
+        [ read-album   decode-text filter-text-data >>album   ]
+        [ read-year    decode-text filter-text-data >>year    ]
+        [ read-comment decode-text filter-text-data >>comment ]
+        [ read-genre   number>string                >>genre   ]
     } cleave ; inline
 
 : read-v1-tag-data ( seq -- mp3-file )

From 11117648ea21caa85d057117a2e546c89b6fdee3 Mon Sep 17 00:00:00 2001
From: Slava Pestov <slava@slava-pestovs-macbook-pro.local>
Date: Thu, 26 Feb 2009 23:30:48 -0600
Subject: [PATCH 15/18] Fix stack checker regressions

---
 basis/stack-checker/errors/errors.factor           | 2 +-
 basis/stack-checker/known-words/known-words.factor | 8 +++-----
 basis/stack-checker/stack-checker-tests.factor     | 7 +++++--
 3 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/basis/stack-checker/errors/errors.factor b/basis/stack-checker/errors/errors.factor
index 6a9a7cb8af..7f35ece714 100644
--- a/basis/stack-checker/errors/errors.factor
+++ b/basis/stack-checker/errors/errors.factor
@@ -80,4 +80,4 @@ TUPLE: inconsistent-recursive-call-error word ;
 TUPLE: unknown-primitive-error ;
 
 : unknown-primitive-error ( -- * )
-    \ unknown-primitive-error inference-error ;
+    \ unknown-primitive-error inference-warning ;
diff --git a/basis/stack-checker/known-words/known-words.factor b/basis/stack-checker/known-words/known-words.factor
index 0c20c41d99..1b4d9012db 100644
--- a/basis/stack-checker/known-words/known-words.factor
+++ b/basis/stack-checker/known-words/known-words.factor
@@ -141,9 +141,7 @@ M: object infer-call*
     apply-word/effect ;
 
 : infer-exit ( -- )
-    \ exit
-    { integer } { } t >>terminated? <effect>
-    apply-word/effect ;
+    \ exit (( n -- * )) apply-word/effect ;
 
 : infer-load-locals ( -- )
     pop-literal nip
@@ -189,7 +187,7 @@ M: object infer-call*
         { \ load-locals [ infer-load-locals ] }
         { \ get-local [ infer-get-local ] }
         { \ drop-locals [ infer-drop-locals ] }
-        { \ do-primitive [ unknown-primitive-error inference-warning ] }
+        { \ do-primitive [ unknown-primitive-error ] }
         { \ alien-invoke [ infer-alien-invoke ] }
         { \ alien-indirect [ infer-alien-indirect ] }
         { \ alien-callback [ infer-alien-callback ] }
@@ -207,7 +205,7 @@ M: object infer-call*
 {
     declare call (call) slip 2slip 3slip dip 2dip 3dip
     curry compose execute (execute) if dispatch <tuple-boa>
-    (throw) load-local load-locals get-local drop-locals do-primitive
+    (throw) exit load-local load-locals get-local drop-locals do-primitive
     alien-invoke alien-indirect alien-callback
 } [ t "special" set-word-prop ] each
 
diff --git a/basis/stack-checker/stack-checker-tests.factor b/basis/stack-checker/stack-checker-tests.factor
index bc6eb9f092..fadfadd885 100644
--- a/basis/stack-checker/stack-checker-tests.factor
+++ b/basis/stack-checker/stack-checker-tests.factor
@@ -6,7 +6,8 @@ quotations effects tools.test continuations generic.standard
 sorting assocs definitions prettyprint io inspector
 classes.tuple classes.union classes.predicate debugger
 threads.private io.streams.string io.timeouts io.thread
-sequences.private destructors combinators eval locals.backend ;
+sequences.private destructors combinators eval locals.backend
+system ;
 IN: stack-checker.tests
 
 \ infer. must-infer
@@ -581,4 +582,6 @@ DEFER: eee'
 : debugging-curry-folding ( quot -- )
     [ debugging-curry-folding ] curry call ; inline recursive
 
-[ [ ] debugging-curry-folding ] must-infer
\ No newline at end of file
+[ [ ] debugging-curry-folding ] must-infer
+
+[ [ exit ] [ 1 2 3 ] if ] must-infer
\ No newline at end of file

From 16e394517c278639bc7a05012a33cffc12a0a0b3 Mon Sep 17 00:00:00 2001
From: sheeple <sheeple@oberon.local>
Date: Fri, 27 Feb 2009 00:23:04 -0600
Subject: [PATCH 16/18] use vocab: in id3 tests, remove lots of helper words
 because it's clear what they do

---
 extra/id3/id3-tests.factor |   6 +-
 extra/id3/id3.factor       | 229 ++++++++-----------------------------
 2 files changed, 52 insertions(+), 183 deletions(-)

diff --git a/extra/id3/id3-tests.factor b/extra/id3/id3-tests.factor
index eabbf00ad7..aefbec8550 100644
--- a/extra/id3/id3-tests.factor
+++ b/extra/id3/id3-tests.factor
@@ -20,7 +20,7 @@ IN: id3.tests
    "2009"
    "COMMENT"
    "Bluegrass"
-] [ "resource:extra/id3/tests/blah.mp3" file-id3-tags id3-params ] unit-test
+] [ "vocab:id3/tests/blah.mp3" file-id3-tags id3-params ] unit-test
 
 [
     "Anthem of the Trinity"
@@ -29,7 +29,7 @@ IN: id3.tests
     f
     f
     "Classical"
-] [ "resource:extra/id3/tests/blah2.mp3" file-id3-tags id3-params ] unit-test
+] [ "vocab:id3/tests/blah2.mp3" file-id3-tags id3-params ] unit-test
 
 [    
    "Stormy Weather"
@@ -38,5 +38,5 @@ IN: id3.tests
     f
    "eng, AG# 08E1C12E"
    "Big Band"
-] [ "resource:extra/id3/tests/blah3.mp3" file-id3-tags id3-params ] unit-test
+] [ "vocab:id3/tests/blah3.mp3" file-id3-tags id3-params ] unit-test
 
diff --git a/extra/id3/id3.factor b/extra/id3/id3.factor
index a4adeedaa5..aa27fb95c7 100644
--- a/extra/id3/id3.factor
+++ b/extra/id3/id3.factor
@@ -4,140 +4,39 @@ USING: sequences io io.encodings.binary io.files io.pathnames
 strings kernel math io.mmap io.mmap.uchar accessors syntax
 combinators math.ranges unicode.categories byte-arrays
 io.encodings.string io.encodings.utf16 assocs math.parser
-combinators.short-circuit fry namespaces multiline
-combinators.smart splitting io.encodings.ascii ;
+combinators.short-circuit fry namespaces combinators.smart
+splitting io.encodings.ascii arrays ;
 IN: id3
 
 <PRIVATE
 
 CONSTANT: genres
     {
-        "Blues" 
-        "Classic Rock" 
-        "Country" 
-        "Dance" 
-        "Disco" 
-        "Funk" 
-        "Grunge" 
-        "Hip-Hop" 
-        "Jazz" 
-        "Metal" 
-        "New Age" 
-        "Oldies" 
-        "Other" 
-        "Pop" 
-        "R&B" 
-        "Rap" 
-        "Reggae" 
-        "Rock" 
-        "Techno" 
-        "Industrial" 
-        "Alternative" 
-        "Ska" 
-        "Death Metal" 
-        "Pranks" 
-        "Soundtrack" 
-        "Euro-Techno" 
-        "Ambient" 
-        "Trip-Hop" 
-        "Vocal" 
-        "Jazz+Funk" 
-        "Fusion" 
-        "Trance" 
-        "Classical" 
-        "Instrumental" 
-        "Acid" 
-        "House" 
-        "Game" 
-        "Sound Clip" 
-        "Gospel" 
-        "Noise" 
-        "AlternRock" 
-        "Bass" 
-        "Soul" 
-        "Punk" 
-        "Space" 
-        "Meditative" 
-        "Instrumental Pop" 
-        "Instrumental Rock" 
-        "Ethnic" 
-        "Gothic" 
-        "Darkwave" 
-        "Techno-Industrial" 
-        "Electronic" 
-        "Pop-Folk" 
-        "Eurodance" 
-        "Dream" 
-        "Southern Rock" 
-        "Comedy" 
-        "Cult" 
-        "Gangsta" 
-        "Top 40" 
-        "Christian Rap" 
-        "Pop/Funk" 
-        "Jungle" 
-        "Native American" 
-        "Cabaret" 
-        "New Wave" 
-        "Psychedelic" 
-        "Rave" 
-        "Showtunes" 
-        "Trailer" 
-        "Lo-Fi" 
-        "Tribal" 
-        "Acid Punk" 
-        "Acid Jazz" 
-        "Polka" 
-        "Retro" 
-        "Musical" 
-        "Rock & Roll" 
-        "Hard Rock" 
-        "Folk" 
-        "Folk-Rock" 
-        "National Folk" 
-        "Swing" 
-        "Fast Fusion" 
-        "Bebop" 
-        "Latin" 
-        "Revival" 
-        "Celtic" 
-        "Bluegrass" 
-        "Avantgarde" 
-        "Gothic Rock" 
-        "Progressive Rock" 
-        "Psychedelic Rock" 
-        "Symphonic Rock" 
-        "Slow Rock" 
-        "Big Band" 
-        "Chorus" 
-        "Easy Listening" 
-        "Acoustic" 
-        "Humour" 
-        "Speech" 
-        "Chanson" 
-        "Opera" 
-        "Chamber Music" 
-        "Sonata" 
-        "Symphony" 
-        "Booty Bass" 
-        "Primus" 
-        "Porn Groove" 
-        "Satire" 
-        "Slow Jam" 
-        "Club" 
-        "Tango" 
-        "Samba" 
-        "Folklore" 
-        "Ballad" 
-        "Power Ballad" 
-        "Rhythmic Soul" 
-        "Freestyle" 
-        "Duet" 
-        "Punk Rock" 
-        "Drum Solo" 
-        "A capella" 
-        "Euro-House"
-        "Dance Hall"
+        "Blues" "Classic Rock" "Country" "Dance" "Disco" "Funk" 
+        "Grunge" "Hip-Hop" "Jazz" "Metal" "New Age" "Oldies" "Other" 
+        "Pop" "R&B" "Rap" "Reggae" "Rock" "Techno" "Industrial" 
+        "Alternative" "Ska" "Death Metal" "Pranks" "Soundtrack" 
+        "Euro-Techno" "Ambient" "Trip-Hop" "Vocal" "Jazz+Funk" 
+        "Fusion" "Trance" "Classical" "Instrumental" "Acid" "House" 
+        "Game" "Sound Clip" "Gospel" "Noise" "AlternRock" "Bass" 
+        "Soul" "Punk" "Space" "Meditative" "Instrumental Pop" 
+        "Instrumental Rock" "Ethnic" "Gothic" "Darkwave" 
+        "Techno-Industrial" "Electronic" "Pop-Folk" "Eurodance" 
+        "Dream" "Southern Rock" "Comedy" "Cult" "Gangsta" "Top 40" 
+        "Christian Rap" "Pop/Funk" "Jungle" "Native American" 
+        "Cabaret" "New Wave" "Psychedelic" "Rave" "Showtunes" 
+        "Trailer" "Lo-Fi" "Tribal" "Acid Punk" "Acid Jazz" "Polka" 
+        "Retro" "Musical" "Rock & Roll" "Hard Rock" "Folk" 
+        "Folk-Rock" "National Folk" "Swing" "Fast Fusion" "Bebop" 
+        "Latin" "Revival" "Celtic" "Bluegrass" "Avantgarde" 
+        "Gothic Rock" "Progressive Rock" "Psychedelic Rock" 
+        "Symphonic Rock" "Slow Rock" "Big Band" "Chorus" 
+        "Easy Listening" "Acoustic" "Humour" "Speech" "Chanson" 
+        "Opera" "Chamber Music" "Sonata" "Symphony" "Booty Bass" 
+        "Primus" "Porn Groove" "Satire" "Slow Jam" "Club" "Tango" 
+        "Samba" "Folklore" "Ballad" "Power Ballad" "Rhythmic Soul" 
+        "Freestyle" "Duet" "Punk Rock" "Drum Solo" "A capella" 
+        "Euro-House" "Dance Hall"
     }
 
 TUPLE: header version flags size ;
@@ -186,25 +85,12 @@ TUPLE: id3-info title artist album year comment genre ;
 : filter-text-data ( data -- filtered )
     [ printable? ] filter ; inline
 
-! frame details stuff
-
 : valid-frame-id? ( id -- ? )
     [ { [ digit? ] [ LETTER? ] } 1|| ] all? ; inline
 
-: read-frame-id ( mmap -- id )
-    4 head-slice ; inline
-
-: read-frame-size ( mmap -- size )
-    [ 4 8 ] dip subseq ; inline
-
-: read-frame-flags ( mmap -- flags )
-    [ 8 10 ] dip subseq ; inline
-
 : read-frame-data ( frame mmap -- frame data )
     [ 10 over size>> 10 + ] dip <slice> filter-text-data ; inline
 
-! read whole frames
-
 : decode-text ( string -- string' )
     dup 2 short head
     { { HEX: ff HEX: fe } { HEX: fe HEX: ff } } member?
@@ -213,14 +99,14 @@ TUPLE: id3-info title artist album year comment genre ;
 : (read-frame) ( mmap -- frame )
     [ <frame> ] dip
     {
-        [ read-frame-id decode-text >>frame-id ]
-        [ read-frame-flags >byte-array >>flags ]
-        [ read-frame-size >28bitword >>size ]
+        [ 4 head-slice decode-text >>frame-id ]
+        [ [ 4 8 ] dip subseq >28bitword >>size ]
+        [ [ 8 10 ] dip subseq >byte-array >>flags ]
         [ read-frame-data decode-text >>data ]
     } cleave ;
 
 : read-frame ( mmap -- frame/f )
-    dup read-frame-id valid-frame-id?
+    dup 4 head-slice valid-frame-id?
     [ (read-frame) ] [ drop f ] if ;
 
 : remove-frame ( mmap frame -- mmap )
@@ -233,54 +119,32 @@ TUPLE: id3-info title artist album year comment genre ;
     
 ! header stuff
 
-: read-header-supported-version? ( mmap -- ? )
-    3 tail-slice first { 3 4 } member? ; inline
-
-: read-header-flags ( mmap -- flags ) 5 swap nth ; inline
-
-: read-header-size ( mmap -- size )
-    [ 6 10 ] dip <slice> >28bitword ; inline
-
-: read-v2-header ( mmap -- id3header )
+: read-v2-header ( seq -- id3header )
     [ <header> ] dip
     {
-        [ read-header-supported-version? >>version ]
-        [ read-header-flags >>flags ]
-        [ read-header-size >>size ]
+        [ [ 3 5 ] dip <slice> >array >>version ]
+        [ [ 5 ] dip nth >>flags ]
+        [ [ 6 10 ] dip <slice> >28bitword >>size ]
     } cleave ; inline
 
-: drop-header ( mmap -- seq1 seq2 )
-    [ 10 tail-slice ] [ ] bi ; inline
-
 : read-v2-tag-data ( seq -- id3v2-info )
-    drop-header read-v2-header
-    swap read-frames <id3v2-info> ; inline
+    10 cut-slice
+    [ read-v2-header ]
+    [ read-frames ] bi* <id3v2-info> ; inline
     
 ! v1 information
 
 : skip-to-v1-data ( seq -- seq ) 125 tail-slice* ; inline
 
-: read-title ( seq -- title ) 30 head-slice ; inline
-
-: read-artist ( seq -- title ) [ 30 60 ] dip subseq ; inline
-
-: read-album ( seq -- album ) [ 60 90 ] dip subseq ; inline
-
-: read-year ( seq -- year ) [ 90 94 ] dip subseq ; inline
-
-: read-comment ( seq -- comment ) [ 94 124 ] dip subseq ; inline
-
-: read-genre ( seq -- genre ) [ 124 ] dip nth ; inline
-
 : (read-v1-tag-data) ( seq -- mp3-file )
     [ <id3-info> ] dip
     {
-        [ read-title   decode-text filter-text-data >>title   ]
-        [ read-artist  decode-text filter-text-data >>artist  ]
-        [ read-album   decode-text filter-text-data >>album   ]
-        [ read-year    decode-text filter-text-data >>year    ]
-        [ read-comment decode-text filter-text-data >>comment ]
-        [ read-genre   number>string                >>genre   ]
+        [ 30 head-slice decode-text filter-text-data >>title ]
+        [ [ 30 60 ] dip subseq decode-text filter-text-data >>artist ]
+        [ [ 60 90 ] dip subseq decode-text filter-text-data >>album ]
+        [ [ 90 94 ] dip subseq decode-text filter-text-data >>year ]
+        [ [ 94 124 ] dip subseq decode-text filter-text-data >>comment ]
+        [ [ 124 ] dip nth number>string >>genre ]
     } cleave ; inline
 
 : read-v1-tag-data ( seq -- mp3-file )
@@ -323,3 +187,8 @@ PRIVATE>
             [ drop f ]
         } cond
     ] with-mapped-uchar-file ;
+
+: write-id3-tags ( id3v2-info path -- )
+    binary [
+        
+    ] with-file-writer ;

From ec51a3a1a1fce19fc6ec3e1707d8d919e461f77f Mon Sep 17 00:00:00 2001
From: sheeple <sheeple@oberon.local>
Date: Fri, 27 Feb 2009 00:27:39 -0600
Subject: [PATCH 17/18] id3-info -> id3v1-info, remove work in progress

---
 extra/id3/id3.factor | 11 +++--------
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/extra/id3/id3.factor b/extra/id3/id3.factor
index aa27fb95c7..d1397285d7 100644
--- a/extra/id3/id3.factor
+++ b/extra/id3/id3.factor
@@ -45,9 +45,9 @@ TUPLE: frame frame-id flags size data ;
 
 TUPLE: id3v2-info header frames ;
 
-TUPLE: id3-info title artist album year comment genre ;
+TUPLE: id3v1-info title artist album year comment genre ;
 
-: <id3-info> ( -- object ) id3-info new ;
+: <id3v1-info> ( -- object ) id3v1-info new ;
 
 : <id3v2-info> ( header frames -- object )
     [ [ frame-id>> ] keep ] H{ } map>assoc
@@ -137,7 +137,7 @@ TUPLE: id3-info title artist album year comment genre ;
 : skip-to-v1-data ( seq -- seq ) 125 tail-slice* ; inline
 
 : (read-v1-tag-data) ( seq -- mp3-file )
-    [ <id3-info> ] dip
+    [ <id3v1-info> ] dip
     {
         [ 30 head-slice decode-text filter-text-data >>title ]
         [ [ 30 60 ] dip subseq decode-text filter-text-data >>artist ]
@@ -187,8 +187,3 @@ PRIVATE>
             [ drop f ]
         } cond
     ] with-mapped-uchar-file ;
-
-: write-id3-tags ( id3v2-info path -- )
-    binary [
-        
-    ] with-file-writer ;

From 2c462745f126a2fba9563a2516c8ce321229f5ea Mon Sep 17 00:00:00 2001
From: Daniel Ehrenberg <littledan@Macintosh-122.local>
Date: Fri, 27 Feb 2009 00:53:05 -0600
Subject: [PATCH 18/18] Redoing string streams and byte-array streams without
 copying

---
 basis/hints/hints.factor                      |  2 -
 basis/io/streams/byte-array/byte-array.factor | 15 ++++++-
 core/checksums/crc32/crc32.factor             |  2 +-
 core/io/streams/sequence/sequence.factor      | 38 ++++++++++++++++
 core/io/streams/string/string-tests.factor    | 10 ++---
 core/io/streams/string/string.factor          | 44 +++++--------------
 6 files changed, 68 insertions(+), 43 deletions(-)
 create mode 100644 core/io/streams/sequence/sequence.factor

diff --git a/basis/hints/hints.factor b/basis/hints/hints.factor
index b6af773ce5..4093666eb7 100644
--- a/basis/hints/hints.factor
+++ b/basis/hints/hints.factor
@@ -96,8 +96,6 @@ M: object specializer-declaration class ;
 { string string }
 "specializer" set-word-prop
 
-\ find-last-sep { string sbuf } "specializer" set-word-prop
-
 \ >string { sbuf } "specializer" set-word-prop
 
 \ >array { { vector } } "specializer" set-word-prop
diff --git a/basis/io/streams/byte-array/byte-array.factor b/basis/io/streams/byte-array/byte-array.factor
index 9d89c3d814..b877e97cf1 100644
--- a/basis/io/streams/byte-array/byte-array.factor
+++ b/basis/io/streams/byte-array/byte-array.factor
@@ -1,5 +1,8 @@
+! Copyright (C) 2008, 2009 Daniel Ehrenberg
+! See http://factorcode.org/license.txt for BSD license.
 USING: byte-arrays byte-vectors kernel io.encodings io.streams.string
-sequences io namespaces io.encodings.private accessors ;
+sequences io namespaces io.encodings.private accessors sequences.private
+io.streams.sequence destructors ;
 IN: io.streams.byte-array
 
 : <byte-writer> ( encoding -- stream )
@@ -9,8 +12,16 @@ IN: io.streams.byte-array
     [ <byte-writer> ] dip [ output-stream get ] compose with-output-stream*
     dup encoder? [ stream>> ] when >byte-array ; inline
 
+TUPLE: byte-reader { underlying byte-array read-only } { i array-capacity } ;
+
+M: byte-reader stream-read-partial stream-read ;
+M: byte-reader stream-read sequence-read ;
+M: byte-reader stream-read1 sequence-read1 ;
+M: byte-reader stream-read-until sequence-read-until ;
+M: byte-reader dispose drop ;
+
 : <byte-reader> ( byte-array encoding -- stream )
-    [ >byte-vector dup reverse-here ] dip <decoder> ;
+    [ B{ } like 0 byte-reader boa ] dip <decoder> ;
 
 : with-byte-reader ( byte-array encoding quot -- )
     [ <byte-reader> ] dip with-input-stream* ; inline
diff --git a/core/checksums/crc32/crc32.factor b/core/checksums/crc32/crc32.factor
index 7ea2964411..47da144d4d 100644
--- a/core/checksums/crc32/crc32.factor
+++ b/core/checksums/crc32/crc32.factor
@@ -1,7 +1,7 @@
 ! Copyright (C) 2006 Doug Coleman
 ! See http://factorcode.org/license.txt for BSD license.
 USING: kernel math sequences sequences.private namespaces
-words io io.binary io.files io.streams.string quotations
+words io io.binary io.files quotations
 definitions checksums ;
 IN: checksums.crc32
 
diff --git a/core/io/streams/sequence/sequence.factor b/core/io/streams/sequence/sequence.factor
new file mode 100644
index 0000000000..bbb3576c05
--- /dev/null
+++ b/core/io/streams/sequence/sequence.factor
@@ -0,0 +1,38 @@
+! Copyright (C) 2009 Daniel Ehrenberg
+! See http://factorcode.org/license.txt for BSD license.
+USING: sequences io kernel accessors math math.order ;
+IN: io.streams.sequence
+
+SLOT: underlying
+SLOT: i
+
+: >sequence-stream< ( stream -- i underlying )
+    [ i>> ] [ underlying>> ] bi ; inline
+
+: next ( stream -- )
+    [ 1+ ] change-i drop ;
+
+: sequence-read1 ( stream -- elt/f )
+    [ >sequence-stream< ?nth ]
+    [ next ] bi ; inline
+
+: add-length ( n stream -- i+n )
+    [ i>> + ] [ underlying>> length ] bi min  ;
+
+: (sequence-read) ( n stream -- seq/f )
+    [ add-length ] keep
+    [ [ swap dup ] change-i drop ]
+    [ underlying>> ] bi
+    subseq ; inline
+
+: sequence-read ( n stream -- seq/f )
+    dup >sequence-stream< bounds-check?
+    [ (sequence-read) ] [ 2drop f ] if ; inline
+
+: find-sep ( seps stream -- sep/f n )
+    swap [ >sequence-stream< ] dip
+    [ memq? ] curry find-from swap ; inline
+
+: sequence-read-until ( separators stream -- seq sep/f )
+    [ find-sep ] keep
+    [ sequence-read ] [ next ] bi swap ; inline
diff --git a/core/io/streams/string/string-tests.factor b/core/io/streams/string/string-tests.factor
index a6502046c8..967c0d4613 100644
--- a/core/io/streams/string/string-tests.factor
+++ b/core/io/streams/string/string-tests.factor
@@ -15,12 +15,12 @@ unit-test
 
 [ "xyzzy" ] [ [ "xyzzy" write ] with-string-writer ] unit-test
 
-[ "a" ] [ 1 SBUF" cba" stream-read ] unit-test
-[ "ab" ] [ 2 SBUF" cba" stream-read ] unit-test
-[ "abc" ] [ 3 SBUF" cba" stream-read ] unit-test
-[ "abc" ] [ 4 SBUF" cba" stream-read ] unit-test
+[ "a" ] [ 1 "abc" <string-reader> stream-read ] unit-test
+[ "ab" ] [ 2 "abc" <string-reader> stream-read ] unit-test
+[ "abc" ] [ 3 "abc" <string-reader> stream-read ] unit-test
+[ "abc" ] [ 4 "abc" <string-reader> stream-read ] unit-test
 [ "abc" f ] [
-    3 SBUF" cba" [ stream-read ] keep stream-read1
+    3 "abc" <string-reader> [ stream-read ] keep stream-read1
 ] unit-test
 
 [
diff --git a/core/io/streams/string/string.factor b/core/io/streams/string/string.factor
index 4582490726..73bf5f5efe 100644
--- a/core/io/streams/string/string.factor
+++ b/core/io/streams/string/string.factor
@@ -1,18 +1,12 @@
-! Copyright (C) 2003, 2009 Slava Pestov.
+! Copyright (C) 2003, 2009 Slava Pestov, Daniel Ehrenberg.
 ! See http://factorcode.org/license.txt for BSD license.
 USING: accessors io kernel math namespaces sequences sbufs
-strings generic splitting continuations destructors
-io.streams.plain io.encodings math.order growable ;
+strings generic splitting continuations destructors sequences.private
+io.streams.plain io.encodings math.order growable io.streams.sequence ;
 IN: io.streams.string
 
 <PRIVATE
 
-: harden-as ( seq growble-exemplar -- newseq )
-    underlying>> like ;
-
-: growable-read-until ( growable n -- str )
-    >fixnum dupd tail-slice swap harden-as dup reverse-here ;
-
 SINGLETON: null-encoding
 
 M: null-encoding decode-char drop stream-read1 ;
@@ -32,34 +26,18 @@ M: growable stream-flush drop ;
     <string-writer> swap [ output-stream get ] compose with-output-stream*
     >string ; inline
 
-M: growable stream-read1 [ f ] [ pop ] if-empty ;
+! New implementation
 
-: find-last-sep ( seq seps -- n )
-    swap [ memq? ] curry find-last drop ;
+TUPLE: string-reader { underlying string read-only } { i array-capacity } ;
 
-M: growable stream-read-until
-    [ find-last-sep ] keep over [
-        [ swap 1+ growable-read-until ] 2keep [ nth ] 2keep
-        set-length
-    ] [
-        [ swap drop 0 growable-read-until f like f ] keep
-        delete-all
-    ] if ;
-
-M: growable stream-read
-    [
-        drop f
-    ] [
-        [ length swap - 0 max ] keep
-        [ swap growable-read-until ] 2keep
-        set-length
-    ] if-empty ;
-
-M: growable stream-read-partial
-    stream-read ;
+M: string-reader stream-read-partial stream-read ;
+M: string-reader stream-read sequence-read ;
+M: string-reader stream-read1 sequence-read1 ;
+M: string-reader stream-read-until sequence-read-until ;
+M: string-reader dispose drop ;
 
 : <string-reader> ( str -- stream )
-    >sbuf dup reverse-here null-encoding <decoder> ;
+    0 string-reader boa null-encoding <decoder> ;
 
 : with-string-reader ( str quot -- )
     [ <string-reader> ] dip with-input-stream ; inline