diff --git a/Sloane/Bloom.hs b/Sloane/Bloom.hs
--- a/Sloane/Bloom.hs
+++ b/Sloane/Bloom.hs
@@ -20,7 +20,7 @@
                 , ByteString, ByteString, ByteString
                 , ByteString, ByteString, ByteString )
 
-type BlF = Bloom T9
+type BF = Bloom T9
 
 instance Hashable T9 where
     hashIO32 (T9 (a,b,c,d,e,f,g,h,i)) salt =
@@ -38,7 +38,7 @@
 
 -- | Make a Bloom filter of all nine integer segments of all sequences
 -- in the given data base.
-mkBloomFilter :: DB Sequences -> BlF
+mkBloomFilter :: DB Seqs -> BF
 mkBloomFilter (DB db) = F.fromList (cheapHashes numHashes) numBits ts
   where
     ts = ninegrams (parseTermsOfRecords db)
@@ -48,5 +48,5 @@
 -- | Are all the nine element factors of the given (packed) sequence
 -- members of the Bloom filter. May give a false positive answer, but
 -- never a false negative answer.
-isFactorOf :: PackedSeq -> BlF -> Bool
+isFactorOf :: PackedSeq -> BF -> Bool
 isFactorOf (PSeq s) bf = all (`F.elem` bf) $ ninegrams (parseTermsErr s)
diff --git a/Sloane/Config.hs b/Sloane/Config.hs
--- a/Sloane/Config.hs
+++ b/Sloane/Config.hs
@@ -1,5 +1,5 @@
 -- |
--- Copyright   : Anders Claesson 2014-2015
+-- Copyright   : Anders Claesson 2014-2016
 -- Maintainer  : Anders Claesson <anders.claesson@gmail.com>
 -- License     : BSD-3
 --
@@ -10,7 +10,6 @@
     , getConfig
     ) where
 
-import System.Console.Terminal.Size (width, size)
 import System.FilePath ((</>))
 import System.Directory
 
@@ -25,19 +24,15 @@
     , seqDBPath   :: FilePath
     -- | Path to 'names' file.
     , namesDBPath :: FilePath
-    -- | The width of the terminal.
-    , termWidth   :: Int
     }
 
 -- | Get configuration.
 getConfig :: IO Config
 getConfig = do
-    w <- maybe maxBound width `fmap` size
     h <- getHomeDirectory
     let c = Config { home        = h
                    , sloaneDir   = h </> ".oeis-data"
                    , seqDBPath   = sloaneDir c </> "stripped"
                    , namesDBPath = sloaneDir c </> "names"
-                   , termWidth   = w
                    }
     return c
diff --git a/Sloane/DB.hs b/Sloane/DB.hs
--- a/Sloane/DB.hs
+++ b/Sloane/DB.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE EmptyDataDecls #-}
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE PolyKinds #-}
@@ -10,16 +9,14 @@
 --
 
 module Sloane.DB
-    ( DB (..), Sequences, Names
+    ( DB (..), Seqs, Names
     , readSeqDB
     , readNamesDB
     , grepN
     , grep
     ) where
 
-#if __GLASGOW_HASKELL__ < 710
 import Control.Applicative
-#endif
 import Data.Maybe
 import Data.List
 import Data.ByteString (ByteString)
@@ -30,7 +27,7 @@
 import Sloane.Config
 
 -- | An empty data declaration used with the phantom `DB` data type.
-data Sequences
+data Seqs
 
 -- | An empty data declaration used with the phantom `DB` data type.
 data Names
@@ -45,7 +42,7 @@
          else error "No local database; run 'sloane --update' first."
 
 -- | Read the sequence DB (derived from \"stripped.gz\").
-readSeqDB :: Config -> IO (DB Sequences)
+readSeqDB :: Config -> IO (DB Seqs)
 readSeqDB = readDB . seqDBPath
 
 -- | Read the names DB (derived from \"names.gz\").
@@ -54,7 +51,7 @@
 
 -- | Return all A-numbers whose associated sequence contains a given
 -- sequence as a factor.
-grep :: PackedSeq -> DB Sequences -> [ANum]
+grep :: PackedSeq -> DB Seqs -> [ANum]
 grep (PSeq p) (DB bs) = mapMaybe locateANum (S.indices q bs)
   where
     q = B.snoc (B.cons ',' p) ','
@@ -66,5 +63,5 @@
         ]
 
 -- | Similar to `grep` but return at most 'n' unique hits.
-grepN :: Int -> PackedSeq -> DB Sequences -> [ANum]
+grepN :: Int -> PackedSeq -> DB Seqs -> [ANum]
 grepN n q db = Prelude.take n $ map head $ group (grep q db)
diff --git a/Sloane/Entry.hs b/Sloane/Entry.hs
--- a/Sloane/Entry.hs
+++ b/Sloane/Entry.hs
@@ -1,54 +1,69 @@
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE DeriveGeneric #-}
 
 -- |
--- Copyright   : Anders Claesson 2015
+-- Copyright   : Anders Claesson 2015, 2016
 -- Maintainer  : Anders Claesson <anders.claesson@gmail.com>
 -- License     : BSD-3
 --
 
 module Sloane.Entry
-    ( PackedPrg (..)
-    , PackedEntry (..)
-    , parsePackedEntry
-    , parsePackedEntryErr
+    ( Prg (..)
+    , Name (..)
+    , Trail
+    , Entry (..)
     ) where
 
-import GHC.Generics (Generic)
+import Data.Aeson
 import Data.Maybe
 import Data.ByteString.Char8 (ByteString)
-import qualified Data.ByteString.Char8 as B
-import qualified Data.Attoparsec.ByteString.Char8 as Ch
-import Data.Attoparsec.ByteString.Char8
+import Data.Text.Encoding (encodeUtf8, decodeUtf8)
 import Control.Monad
-#if __GLASGOW_HASKELL__ < 710
-import Control.Applicative
-#endif
-import Sloane.Utils
-import Sloane.OEIS
 
--- | A compact `ByteString` representation of a `Prg`.
-newtype PackedPrg = PPrg ByteString deriving (Eq, Show, Generic)
+newtype Prg = Prg ByteString deriving (Show, Eq)
 
--- | Similary, a packed entry consists of a packed program together with
--- a packed sequence.
-data PackedEntry = PackedEntry
-    { getPackedPrg :: PackedPrg
-    , getPackedSeq :: PackedSeq
-    } deriving (Eq, Show, Generic)
+type Trail = [Prg]
 
-packedEntry :: Parser PackedEntry
-packedEntry =
-    let f w = if B.last w == '=' then return (PPrg (B.init w)) else mzero
-    in PackedEntry <$> (Ch.takeWhile1 (/='>') >>= f)
-                   <*> (char '>' *> packedSeq)
+newtype Name = Name ByteString deriving (Show, Eq)
 
--- | A parser for packed entries.
-parsePackedEntry :: ByteString -> Maybe PackedEntry
-parsePackedEntry = parse_ packedEntry . B.filter (/=' ')
+instance ToJSON Prg where
+    toJSON (Prg bs) = String (decodeUtf8 bs)
 
--- | Like `parsePackedEntry` but throws an error rather than returning
--- `Nothing` in case the parse fails.
-parsePackedEntryErr :: ByteString -> PackedEntry
-parsePackedEntryErr = fromMaybe (error "cannot parse input") . parsePackedEntry
+instance FromJSON Prg where
+    parseJSON (String s) = return $ Prg (encodeUtf8 s)
+    parseJSON _ = mzero
+
+instance ToJSON Name where
+    toJSON (Name bs) = String (decodeUtf8 bs)
+
+instance FromJSON Name where
+    parseJSON (String s) = return $ Name (encodeUtf8 s)
+    parseJSON _ = mzero
+
+-- | An entry consists of a program together with a list of rational
+-- numbers.
+data Entry = Entry
+    { getPrg   :: Prg
+    , getSeq   :: [Integer]
+    , getDens  :: Maybe [Integer]
+    , getName  :: Maybe Name
+    , getTrail :: [Prg]
+    } deriving (Eq, Show)
+
+instance ToJSON Entry where
+    toJSON (Entry prg s dens name trail) =
+        object ([ "hops"         .= toJSON prg
+                , "seq"          .= toJSON s ] ++
+                [ "denominators" .= toJSON dens  | isJust dens ] ++
+                [ "name"         .= toJSON name  | isJust name ] ++
+                [ "trail"        .= toJSON trail | not (null trail) ]
+               )
+
+instance FromJSON Entry where
+    parseJSON (Object v) = do
+        prg   <- v .:  "hops"
+        ns    <- v .:  "seq"
+        dens  <- v .:? "denominators"
+        name  <- v .:? "name"
+        trail <- v .:? "trail"
+        return $ Entry prg ns dens name (fromMaybe [] trail)
+    parseJSON _ = mzero
diff --git a/Sloane/OEIS.hs b/Sloane/OEIS.hs
--- a/Sloane/OEIS.hs
+++ b/Sloane/OEIS.hs
@@ -1,9 +1,8 @@
-{-# LANGUAGE CPP #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE DeriveGeneric #-}
 
 -- |
--- Copyright   : Anders Claesson 2015
+-- Copyright   : Anders Claesson 2015, 2016
 -- Maintainer  : Anders Claesson <anders.claesson@gmail.com>
 -- License     : BSD-3
 --
@@ -12,40 +11,28 @@
     (
     -- * Types
       URL
-    , Key
-    , Name
     , ANum (..)
     , PackedSeq (..)
-    , Table (..)
-    , Reply (..)
+    , OEISEntry (..)
     -- * Parse names.gz and stripped.gz
     , parseNames
     , parseStripped
     , parseTermsErr
     , parseTermsOfRecords
     -- * Parse replies from oeis.org
-    , oeisKeys
-    , parseReplies
+    , parseOEISEntries
     -- * Parse sequences
     , shave
-    , parseSeqErr
     , parseIntegerSeq
-    , packedSeq
     , packSeq
     -- * Parse A-numbers and B-numbers
-    , aNumInt
     , parseANum
-    , packANum
-    , tag
     ) where
 
 import GHC.Generics (Generic)
 import Data.Maybe
-#if __GLASGOW_HASKELL__ < 710
 import Data.Monoid
-#endif
 import Data.String
-import Data.Ratio
 import Data.Map (Map)
 import qualified Data.Map as M
 import Data.ByteString.Char8 (ByteString)
@@ -54,29 +41,32 @@
 import Data.Text.Encoding (encodeUtf8, decodeUtf8)
 import Data.Aeson
 import qualified Data.Attoparsec.ByteString as A
-import qualified Data.Attoparsec.ByteString.Char8 as Ch
 import Data.Attoparsec.ByteString.Char8
 import Control.Monad
 import Control.Applicative
 import Sloane.Utils
+import Sloane.Entry
 
 -- | An OEIS key is `Char`.
-type Key  = Char
-
--- | The name of an OEIS entry is a short description of the
--- sequence. Here represented as a `ByteString`.
-type Name = ByteString
+type Key = Char
 
-type Row  = (Key, ANum, ByteString)
+type Row = (Key, ANum, ByteString)
 
 -- | A URL is currently just a synonym for `String`.
-type URL  = String
+type URL = String
 
 -- | An A-number is the character \'A\' followed by a six digit
 -- number. Here we represent that by a wrapped (7 character)
 -- `ByteString`.
 newtype ANum = ANum {unANum :: ByteString} deriving (Eq, Ord, Show, Generic)
 
+instance ToJSON ANum where
+    toJSON (ANum bs) = String (decodeUtf8 bs)
+
+instance FromJSON ANum where
+    parseJSON (String s) = pure $ ANum (encodeUtf8 s)
+    parseJSON _ = mzero
+
 -- | A `PackedSeq` is a wrapped `ByteString`.
 newtype PackedSeq = PSeq {unPSeq :: ByteString} deriving (Eq, Show, Generic)
 
@@ -87,21 +77,6 @@
 instance IsString PackedSeq where
     fromString = PSeq . fromString
 
--- | A `Table` represents an OEIS entry. It is a `Map` from OEIS keys to
--- lists of `ByteString`s.
-newtype Table = Table (Map Key [ByteString]) deriving Show
-
--- | A `Reply` is an A-number together with an associated `Table` (OEIS
--- entry).
-data Reply = Reply ANum Table deriving Show
-
-instance ToJSON ANum where
-    toJSON (ANum bs) = String (decodeUtf8 bs)
-
-instance FromJSON ANum where
-    parseJSON (String s) = pure $ ANum (encodeUtf8 s)
-    parseJSON _ = mzero
-
 instance ToJSON PackedSeq where
     toJSON (PSeq bs) = String (decodeUtf8 bs)
 
@@ -109,26 +84,19 @@
     parseJSON (String s) = pure $ PSeq (encodeUtf8 s)
     parseJSON _ = mzero
 
-instance ToJSON Table where
-    toJSON (Table tbl) =
-        object [ T.singleton key .= toJSON (map decodeUtf8 ls)
-               | (key, ls) <- M.toList tbl
-               ]
+data OEISEntry = OEISEntry ANum (Map Key [ByteString]) deriving Show
 
-instance ToJSON Reply where
-    toJSON (Reply anum table) =
-        object [ "A-number" .= toJSON anum
-               , "table" .= toJSON table
-               ]
+instance ToJSON OEISEntry where
+    toJSON (OEISEntry anum tbl) =
+        object ("A-number" .= toJSON anum :
+                [ T.singleton key .= toJSON (map decodeUtf8 ls)
+                | (key, ls) <- M.toList tbl
+                ])
 
-instance FromJSON Table where
+instance FromJSON OEISEntry where
     parseJSON (Object v) =
         let f k = (,) <$> pure k <*> (map encodeUtf8 <$> v .: T.singleton k)
-        in Table . M.fromList <$> mapM f oeisKeys
-    parseJSON _ = mzero
-
-instance FromJSON Reply where
-    parseJSON (Object v) = Reply <$> v .: "A-number" <*> v .: "table"
+        in OEISEntry <$> (v .: "A-number") <*> (M.fromList <$> mapM f oeisKeys)
     parseJSON _ = mzero
 
 spc :: Parser Char
@@ -155,8 +123,8 @@
 --
 -- > A000108 Catalan numbers: C(n) = binomial(2n,n)/(n+1) = (2n)!/(n!(n+1)!).
 --
-parseNames :: ByteString -> [(ANum, ByteString)]
-parseNames = parseRecords
+parseNames :: ByteString -> [(ANum, Name)]
+parseNames bs = [ (a, Name n) | (a, n) <- parseRecords bs ]
 
 -- | Parse a list of A-number-sequence pairs. It's purpose is to parse
 -- lines of the @stripped@ file. A typical line of that file looks like
@@ -164,8 +132,8 @@
 --
 -- > A000108 ,1,1,2,5,14,42,132,429,1430,4862,16796,58786,208012,742900,
 --
-parseStripped :: ByteString -> [(ANum, PackedSeq)]
-parseStripped bs = [ (anum, PSeq (shave s)) | (anum, s) <- parseRecords bs ]
+parseStripped :: ByteString -> [(ANum, [Integer])]
+parseStripped bs = [ (anum, parseIntegerSeqErr (shave s)) | (anum, s) <- parseRecords bs ]
 
 -------------------------------------------------------------------------------
 -- Building the Bloom filter
@@ -203,61 +171,43 @@
   where
     rest = A.takeTill isEndOfLine <* endOfLine
 
-rows :: Parser Reply
-rows = mkMap1 <$> many1 row
-  where
-    mkMap2 = Table . M.fromListWith (flip (++))
-    mkMap1 rs@((_,a,_):_) = Reply a $ mkMap2 (map (\(key, _, r) -> (key, [r])) rs)
-    mkMap1 [] = error "internal error"
-
 noise :: Parser ()
 noise = skipMany (notChar '%')
 
-replies :: Parser [Reply]
-replies = concat <$> (noise *> (many1 rows `sepBy` many1 endOfLine) <* noise)
+oeisEntry :: Parser OEISEntry
+oeisEntry = mkMap1 <$> many1 row
+  where
+    mkMap2 = M.fromListWith (flip (++))
+    mkMap1 rs@((_,a,_):_) = OEISEntry a $ mkMap2 (map (\(key, _, r) -> (key, [r])) rs)
+    mkMap1 [] = error "internal error"
 
--- | Parse OEIS replies as recieved from @oeis.org/search?fmt=text@.
-parseReplies :: ByteString -> [Reply]
-parseReplies = fromMaybe [] . parse_ replies
+oeisEntries :: Parser [OEISEntry]
+oeisEntries = concat <$> (noise *> (many1 oeisEntry `sepBy` many1 endOfLine) <* noise)
 
+parseOEISEntries :: ByteString -> [OEISEntry]
+parseOEISEntries = fromMaybe [] . parse_ oeisEntries
+
 -------------------------------------------------------------------------------
 -- Parse sequences
 -------------------------------------------------------------------------------
 
-rat :: Parser Rational
-rat = (%) <$> signed decimal <*> ((char '/' *> decimal) <|> return 1)
-
-ratSeq :: Parser [Rational]
-ratSeq = rat `sepBy` char ','
-
 integerSeq :: Parser [Integer]
 integerSeq = signed decimal `sepBy` char ','
 
-parseSeq :: ByteString -> Maybe [Rational]
-parseSeq = parse_ (ratSeq <* endOfInput) . B.filter (/=' ')
-
--- | Parse a sequence of `Rational`s.
-parseSeqErr :: ByteString -> [Rational]
-parseSeqErr = fromMaybe (error "error parsing sequence") . parseSeq
-
 -- | Parse a sequence of `Integer`s.
 parseIntegerSeq :: ByteString -> Maybe [Integer]
 parseIntegerSeq = parse_ (integerSeq <* endOfInput) . B.filter (/=' ')
 
--- | Parser for `PackedSeq`.
-packedSeq :: Parser PackedSeq
-packedSeq = PSeq <$> (char '{' *> Ch.takeWhile (/='}') <* char '}')
+-- | Parse a sequence of `Integer`s or throw an error.
+parseIntegerSeqErr :: ByteString -> [Integer]
+parseIntegerSeqErr = fromMaybe (error "error parsing sequence") . parseIntegerSeq
 
--- | Pack a sequence of `Rational`s into a `PackedSeq`. E.g.
+-- | Pack a sequence of `Integers`s into a `PackedSeq`. E.g.
 --
--- > packSeq [1,1/2,1/3] = PSeq {unPSeq = "1,1/2,1/3"}
+-- > packSeq [1,-1,3] = PSeq {unPSeq = "1,-1,3"}
 --
-packSeq :: [Rational] -> PackedSeq
-packSeq = PSeq . B.intercalate (B.pack ",") . map (B.pack . f)
-  where
-    f r = case (numerator r, denominator r) of
-            (n, 1) -> show n
-            (n, d) -> show n ++ '/':show d
+packSeq :: [Integer] -> PackedSeq
+packSeq = PSeq . B.intercalate (B.pack ",") . map (B.pack . show)
 
 -------------------------------------------------------------------------------
 -- Utility functions
@@ -274,10 +224,6 @@
 aNumInt :: Parser Int
 aNumInt = char 'A' >> decimal
 
--- | Run the `aNumInt` parser.
-parseANum :: ByteString -> Maybe ANum
-parseANum = parse_ (packANum <$> aNumInt)
-
 -- | Pack an A-number given as an `Int` into a wrapped `ByteString`
 -- consistsing of an \'A\' followed by six digits. E.g.
 --
@@ -286,6 +232,6 @@
 packANum :: Int -> ANum
 packANum anum = ANum $ B.cons 'A' (pad 6 anum)
 
--- | A parser for tags (B-numbers) as `Int`s.
-tag :: Parser Int
-tag = string "TAG" >> decimal
+-- | Run the `aNumInt` parser.
+parseANum :: ByteString -> Maybe ANum
+parseANum = parse_ (packANum <$> aNumInt)
diff --git a/Sloane/Options.hs b/Sloane/Options.hs
--- a/Sloane/Options.hs
+++ b/Sloane/Options.hs
@@ -1,46 +1,27 @@
 -- |
--- Copyright   : Anders Claesson 2015
+-- Copyright   : Anders Claesson 2015, 2016
 -- Maintainer  : Anders Claesson <anders.claesson@gmail.com>
 -- License     : BSD-3
 --
 -- Command line options for sloane.
 
 module Sloane.Options
-    ( Palette
-    , monochrome
-    , Options (..)
+    ( Options (..)
     , getOptions
     ) where
 
 import Options.Applicative
-import Sloane.OEIS
 
--- | A palette is either colorful or monochrome.
-data Palette = Colorful | Monochrome deriving (Eq, Enum, Show, Read)
-
--- | Is the palette monochrome?
-monochrome :: Palette -> Bool
-monochrome Monochrome = True
-monochrome Colorful   = False
-
 -- | Command line options:
 data Options = Options
     {
     -- | Search oeis.org.
       oeis           :: Bool
-    -- | Print all fields?
-    , longFormat     :: Bool
-    -- | Keys of fields to print.
-    , keys           :: String
     -- | Fetch at most this many entries.
     , limit          :: Int
     -- | Fetch all entries (that match).
     , limitless      :: Bool
-    -- | Should we colorize the output?
-    , palette        :: Palette
     -- | Filter out sequences in local DB.
-    , tojson         :: Bool
-    -- | Return sequences NOT in DB
     , filtr          :: Bool
     -- | Return sequences NOT in DB
     , invert         :: Bool
@@ -48,7 +29,7 @@
     , update         :: Bool
     -- | Show version info
     , version        :: Bool
-    -- Search terms or programs
+    -- | Search terms or hops entries
     , terms          :: [String]
     }
 
@@ -59,14 +40,6 @@
     <$> switch
         ( long "oeis"
        <> help "Search oeis.org" )
-    <*> switch
-        ( long "long"
-       <> help "Long format; print all fields" )
-    <*> strOption
-        ( short 'k'
-       <> metavar "KEYS"
-       <> value "SN"
-       <> help "Keys of fields to print [default: SN]" )
     <*> option auto
         ( short 'n'
        <> metavar "N"
@@ -75,13 +48,7 @@
     <*> switch
         ( long "all"
        <> help "Fetch all matching entries (equivalent to -n 999999)" )
-    <*> (toEnum . fromEnum <$> switch
-        ( long "monochrome"
-       <> help "Do not colorize the output" ) )
     <*> switch
-        ( long "json"
-       <> help "Return results is JSON format" )
-    <*> switch
         ( long "filter"
        <> help ("Read sequences from stdin and return"
             ++ " those that are in the local database") )
@@ -98,8 +65,6 @@
 
 -- | Run the command line options parser (above).
 getOptions :: IO Options
-getOptions = updateOpts <$> execParser (info optionsParser fullDesc)
+getOptions = updateLimit <$> execParser (info optionsParser fullDesc)
   where
-    updateOpts = updateLimit . updateKeys
-    updateKeys  opts = if longFormat opts then opts {keys = oeisKeys} else opts
-    updateLimit opts = if limitless  opts then opts {limit = 999999}  else opts
+    updateLimit opts = if limitless opts then opts {limit = 999999} else opts
diff --git a/sloane.1 b/sloane.1
--- a/sloane.1
+++ b/sloane.1
@@ -1,24 +1,35 @@
-.\" Automatically generated by Pandoc 1.15.1
+.\" Automatically generated by Pandoc 1.16.0.2
 .\"
+.TH "SLOANE" "1" "22 March 2016" "User Manual" "Version 5.0.0"
 .hy
-.TH "SLOANE" "1" "4 Sep 2015" "User Manual" "Version 4.2.0"
 .SH NAME
 .PP
 sloane \- lookup integer sequences, OEIS A\-numbers, etc.
 .SH SYNOPSIS
 .PP
-\f[C]sloane\ [\-\-long]\ [\-k\ KEYS]\ [\-n\ N]\ [\-\-all]\ [\-\-monochrome]\ [\-\-json]\ [\-\-oeis]\ TERMS...\f[]
+\f[C]sloane\ [\-n\ N]\ [\-\-all]\ [\-\-oeis]\ TERMS...\f[]
 .PD 0
 .P
 .PD
 \f[C]sloane\ [\-\-invert]\ \-\-filter\f[]
+.PD 0
+.P
+.PD
+\f[C]sloane\ (\-\-update|\-\-version|\-\-help)\f[]
 .SH DESCRIPTION
 .PP
 \f[C]sloane\f[] provides a command line interface to Sloane\[aq]s OEIS
 (The On\-Line Encyclopedia of Integer Sequences).
 It can be used offline (the default) as well as online (with the
 \f[C]\-\-oeis\f[] option).
-Local searches are faster but a bit less flexible.
+The first time \f[C]sloane\f[] is used in offline mode the user will be
+asked to run \f[C]sloane\ \-\-update\f[].
+This will download \f[C]https://oeis.org/{names.gz,stripped.gz}\f[] and
+unpack them into \f[C]\&.oeis\-data/{names,stripped}\f[] in the home
+directory.
+Alternatively, one can do this by hand using \f[C]wget\f[] and
+\f[C]gunzip\f[], say, if prefered.
+.PP
 A common way to use \f[C]sloane\f[] is to search for entries matching a
 sequence of consecutive terms:
 .IP
@@ -32,48 +43,24 @@
 .IP
 .nf
 \f[C]
-S\ A006531\ 1,1,3,19,183,2371,38703,763099,17648823,468603091,14050842303,
-N\ A006531\ Semiorders\ on\ n\ elements.
-\f[]
-.fi
-.PP
-As shown here the default is to return the sequence (S) and the name (N)
-fields.
-These are the only fields that are available in offline mode.
-In online mode all fields are available.
-To select among the fields use the \f[C]\-k\f[] option, or, to select
-all fields, use to \f[C]\-\-long\f[] option.
-The following search would show the sequence, name, comments, and
-formula fields:
-.IP
-.nf
-\f[C]
-sloane\ \-k\ SNCF\ \-\-oeis\ 1,3,19,183,2371,38703
+{
+\ \ "trail":\ ["{1,3,19,183,2371,38703}"],
+\ \ "hops":\ "A006531",
+\ \ "name":\ "Semiorders\ on\ n\ elements.",
+\ \ "seq":\ [1,1,3,19,183,2371,38703,763099,17648823,468603091,...]
+}
 \f[]
 .fi
 .PP
-One can also lookup A\-numbers or do a free text search; see the
-\f[B]EXAMPLES\f[] section.
-.PP
 The \f[C]\-\-filter\f[] option can be very useful when checking a large
 number of sequences.
-In this mode \f[C]sloane\f[] reads standard input line\-by\-line, if the
-sequence read is in the local database, then it is returned to standard
-output; if not, it is ignored.
+In this mode \f[C]sloane\f[] expects input in the form returned by
+\f[B]hops\f[](1).
+It reads standard input line\-by\-line, if the sequence in the right
+hand side of the entry read is in the local database, then the entry is
+returned to standard output; if not, it is ignored.
 This way one can quickly filter out the sequences from the input that
 are in the local database.
-To be concrete, assume that \f[I]FILE\f[] contains one sequence per
-line.
-Then
-.IP
-.nf
-\f[C]
-sloane\ \-\-filter\ <FILE
-\f[]
-.fi
-.PP
-returns the subset of the sequences in \f[I]FILE\f[] that are in the
-local database.
 To also lookup the names of those sequences one could run
 .IP
 .nf
@@ -82,8 +69,10 @@
 \f[]
 .fi
 .PP
-The way this works is that if \f[C]sloane\f[] is called without search
-terms then it reads from standard input.
+If the sequences one wishes to filter are not already in form returned
+by \f[C]hops\f[] then one may mold them into that form using
+\f[C]hops\ \-\-tag\f[].
+See the \f[B]EXAMPLES\f[] section for more usage examples.
 .SH OPTIONS
 .TP
 .B \-\-oeis
@@ -92,16 +81,6 @@
 .RS
 .RE
 .TP
-.B \-k \f[I]KEYS\f[]
-Keys of fields to print (default: SN).
-.RS
-.RE
-.TP
-.B \-\-long
-Long format; print all fields.
-.RS
-.RE
-.TP
 .B \-n \f[I]N\f[]
 Fetch at most this many entries (default: 5).
 .RS
@@ -112,17 +91,6 @@
 .RS
 .RE
 .TP
-.B \-\-monochrome
-Do not colorize the output.
-Useful when piping the output to another program.
-.RS
-.RE
-.TP
-.B \-\-json
-Output results is JSON format.
-.RS
-.RE
-.TP
 .B \-\-invert
 Return sequences \f[I]not\f[] in the database (used with
 \f[C]\-\-filter\f[]).
@@ -150,12 +118,16 @@
 .nf
 \f[C]
 $\ sloane\ A000111\ A000112
-
-S\ A000111\ 1,1,1,2,5,16,61,272,1385,7936,50521,353792,2702765,
-N\ A000111\ Euler\ or\ up/down\ numbers:\ e.g.f.\ sec(x)\ +\ tan(x)..
-
-S\ A000112\ 1,1,2,5,16,63,318,2045,16999,183231,2567284,46749427,
-N\ A000112\ Number\ of\ partially\ ordered\ sets\ ("posets")\ with\ n..
+{
+\ \ "hops":\ "A000111",
+\ \ "name":\ "Euler\ or\ up/down\ numbers:\ e.g.f.\ sec(x)\ +\ tan(x)...",
+\ \ "seq":\ [1,1,1,2,5,16,61,272,1385,7936,50521,353792,2702765,22368256,...]
+}
+{
+\ \ "hops":\ "A000112",
+\ \ "name":\ "Number\ of\ partially\ ordered\ sets\ (\\"posets\\")\ with\ n\ unlabeled\ elements.",
+\ \ "seq":\ [1,1,2,5,16,63,318,2045,16999,183231,2567284,46749427,...]
+}
 \f[]
 .fi
 .PP
@@ -163,13 +135,19 @@
 .IP
 .nf
 \f[C]
-$\ sloane\ \-n2\ 1,1,2,5,15,52,203,877,4140,21147,115975,678570,
-
-S\ A000110\ 1,1,2,5,15,52,203,877,4140,21147,115975,678570,4213597,
-N\ A000110\ Bell\ or\ exponential\ numbers:\ number\ of\ ways\ to\ partition..
-
-S\ A192128\ 1,1,2,5,15,52,203,877,4140,21147,115975,678570,4213597,
-N\ A192128\ Number\ of\ set\ partitions\ of\ {1,\ ...,\ n}\ that\ avoid..
+$\ sloane\ \-n2\ 1,1,2,5,15,52,203,877,4140,21147,115975,678570
+{
+\ \ "trail":\ ["{1,1,2,5,15,52,203,877,4140,21147,115975,678570}"],
+\ \ "hops":\ "A000110",
+\ \ "name":\ "Bell\ or\ exponential\ numbers:\ number\ of\ ways\ to\ partition\ a\ set\ of\ n...",
+\ \ "seq":\ [1,1,2,5,15,52,203,877,4140,21147,115975,678570,4213597,27644437,...]
+}
+{
+\ \ "trail":\ ["{1,1,2,5,15,52,203,877,4140,21147,115975,678570}"],
+\ \ "hops":\ "A192127",
+\ \ "name":\ "Number\ of\ set\ partitions\ of\ {1,\ ...,\ n}\ that\ avoid\ 6\-nestings.",
+\ \ "seq":\ [1,1,2,5,15,52,203,877,4140,21147,115975,678570,4213596,27644383,...]
+}
 \f[]
 .fi
 .PP
@@ -178,21 +156,25 @@
 .nf
 \f[C]
 $\ hops\ \[aq]y=1+integral(2*y^2\-y);laplace(y)\[aq]\ |\ sloane
-
-S\ A000670\ 1,1,3,13,75,541,4683,47293,545835,7087261,102247563,
-N\ A000670\ Fubini\ numbers:\ number\ of\ preferential\ arrangements\ of..
-
-S\ A034172\ 1,1,3,13,75,541,4683,47293,545835,7087261,102247563,
-N\ A034172\ Nearest\ integer\ to\ n!/(2*log(2)^(n+1)).
+{
+\ \ "trail":["y=1+integral(2*y^2\-y);laplace(y)"],
+\ \ "hops":"A000670",
+\ \ "name":"Fubini\ numbers:\ number\ of\ preferential\ arrangements\ of\ n\ labeled\ ...",
+\ \ "seq":[1,1,3,13,75,541,4683,47293,545835,7087261,102247563,1622632573,...]
+}
+{
+\ \ "trail":["y=1+integral(2*y^2\-y);laplace(y)"],
+\ \ "hops":"A034172","name":"Nearest\ integer\ to\ n!/(2*log(2)^(n+1)).",
+\ \ "seq":[1,1,3,13,75,541,4683,47293,545835,7087261,102247563,1622632573,...]
+}
 \f[]
 .fi
 .PP
-Show the sequence, name, comments, and formula fields of the sequence
-whose A\-number is A006531:
+Fetch the OEIS entry whose A\-number is A006531:
 .IP
 .nf
 \f[C]
-sloane\ \-k\ SNCF\ \-\-oeis\ id:A006531
+sloane\ \-\-oeis\ id:A006531
 \f[]
 .fi
 .PP
@@ -201,50 +183,6 @@
 .nf
 \f[C]
 sloane\ \-n\ 3\ \-\-oeis\ "(2+2)\-free\ posets"
-\f[]
-.fi
-.PP
-\f[C]sloane\f[] normally crops long lines to fit the widths of the
-terminal.
-If this is unwanted, pipe the output through cat or less:
-.IP
-.nf
-\f[C]
-sloane\ \-\-long\ \-\-oeis\ id:A000110\ |\ less\ \-R
-\f[]
-.fi
-.SH KEYS
-.PP
-These are the keys used by the OEIS (http://oeis.org/eishelp2.html).
-.IP
-.nf
-\f[C]
-I\ \ ID\ number
-
-S\ \ 1st\ line\ of\ unsigned\ sequence
-T\ \ 2nd\ line\ of\ unsigned\ sequence
-U\ \ 3rd\ line\ of\ unsigned\ sequence
-
-V\ \ 1st\ line\ of\ signed\ sequence
-W\ \ 2nd\ line\ of\ signed\ sequence
-X\ \ 3rd\ line\ of\ signed\ sequence
-
-N\ \ Name
-C\ \ Comments
-D\ \ References
-H\ \ Links
-F\ \ Formula
-e\ \ Examples
-
-p\ \ Maple\ program
-t\ \ Mathematica\ program
-o\ \ Program\ in\ other\ language
-
-Y\ \ Cross\-references
-K\ \ Keywords
-O\ \ Offset
-A\ \ Author
-E\ \ Extensions\ and\ errors
 \f[]
 .fi
 .SH NOTES
diff --git a/sloane.cabal b/sloane.cabal
--- a/sloane.cabal
+++ b/sloane.cabal
@@ -1,5 +1,5 @@
 Name:                sloane
-Version:             4.2.0
+Version:             5.0.0
 Synopsis:            A command line interface to Sloane's OEIS.
 
 Description:         A command line interface to Sloane's On-Line Encyclopedia of
@@ -49,7 +49,6 @@
                        http-types >=0.8,
                        optparse-applicative >=0.10,
                        stringsearch >=0.3,
-                       terminal-size >=0.2,
                        text >=0.11,
                        transformers >=0.3,
                        resourcet >=1.1
diff --git a/sloane.hs b/sloane.hs
--- a/sloane.hs
+++ b/sloane.hs
@@ -4,14 +4,13 @@
 {-# LANGUAGE PolyKinds #-}
 
 -- |
--- Copyright   : Anders Claesson 2012-2015
+-- Copyright   : Anders Claesson 2012-2016
 -- Maintainer  : Anders Claesson <anders.claesson@gmail.com>
 -- License     : BSD-3
 --
 
 module Main (main) where
 
-import Data.List
 import Data.Aeson
 import Data.Bits (xor)
 import Data.Maybe
@@ -20,13 +19,8 @@
 import qualified Data.Map as M
 import qualified Data.ByteString.Char8 as B
 import qualified Data.ByteString.Lazy.Char8 as BL
-import Data.Text.Encoding (decodeUtf8)
-import qualified Data.Text as T
-import qualified Data.Text.IO as IO
 import Control.Applicative
-import Control.Monad
 import System.Directory
-import System.Console.ANSI
 import System.IO
 import Sloane.OEIS
 import Sloane.Entry
@@ -36,37 +30,35 @@
 import Sloane.Bloom
 import Sloane.DB
 
-nameVer  = "sloane 4.2.0"                 :: String
-oeisURL  = "https://oeis.org/search"      :: URL
-strpdURL = "https://oeis.org/stripped.gz" :: URL
-namesURL = "https://oeis.org/names.gz"    :: URL
+versionString :: String
+versionString = "5.0.0"
 
-type Query  = B.ByteString
-type Width  = Int
-type Limit  = Int
-type View   = (Width, Palette, [Key])
+oeisURL :: URL
+oeisURL = "https://oeis.org/search"
 
-data QA = QA Query [Reply]
+seqsURL :: URL
+seqsURL = "https://oeis.org/stripped.gz"
 
-instance ToJSON QA where
-    toJSON (QA q rs) =
-        object [ "query" .= String (decodeUtf8 q)
-               , "reply" .= toJSON rs
-               ]
+namesURL :: URL
+namesURL = "https://oeis.org/names.gz"
 
+type Limit = Int
+
 data Input
-    = SearchLocalDB (DB Sequences) (DB Names) Bool Limit View [Either ANum PackedSeq]
-    | SearchOEIS Bool Limit View String
-    | FilterSeqs (DB Sequences) Bool [PackedEntry]
+    = SearchLocalDB (DB Seqs) (DB Names) Limit [(Trail, Either ANum PackedSeq)]
+    | SearchOEIS Limit String
+    | FilterSeqs (DB Seqs) Bool [Entry]
     | UpdateDBs FilePath FilePath FilePath
     | Empty
 
 data Output
-    = OEISReplies View [QA]
-    | OEISRepliesJSON [QA]
-    | Entries [PackedEntry]
+    = OEISReplies [OEISEntry]
+    | Entries [Entry]
     | NOP
 
+decodeErr :: B.ByteString -> Entry
+decodeErr = fromMaybe (error "error decoding JSON") . decodeStrict
+
 nonEmptyLines :: BL.ByteString -> [B.ByteString]
 nonEmptyLines = map BL.toStrict . filter (not . BL.null) . BL.lines
 
@@ -82,124 +74,79 @@
 
     | filtr opts = do
         db <- readSeqDB cfg
-        FilterSeqs db (invert opts) . map parsePackedEntryErr <$> readStdin
+        FilterSeqs db (invert opts) . map decodeErr <$> readStdin
 
     | oeis opts =
-        return $ SearchOEIS (tojson opts) (limit opts) view (unwords (terms opts))
+        return $ SearchOEIS (limit opts) (unwords (terms opts))
 
     | otherwise = do
         sdb <- readSeqDB cfg
         ndb <- readNamesDB cfg
-        let parseInp t = fromMaybe (error "cannot parse input")
-                       $  (Right . getPackedSeq <$> parsePackedEntry t)
-                      <|> (Left <$> parseANum t)
-                      <|> (Right . packSeq . map fromIntegral <$> parseIntegerSeq t)
-        SearchLocalDB sdb ndb (tojson opts) (limit opts) view . map parseInp
-            <$> case map B.pack (terms opts) of
-                  [] -> readStdin
-                  ts -> return ts
-  where
-    view = (termWidth cfg, palette opts, keys opts)
-
-few :: [a] -> Bool
-few (_:_:_) = False
-few _       = True
-
-allANum :: [QA] -> Bool
-allANum= all (\(QA q _) -> B.head q == 'A')
-
-printReply :: View -> Reply -> IO ()
-printReply (width, pal, ks) (Reply (ANum anum) (Table table)) =
-    forM_ (ks `intersect` M.keys table) $ \key -> do
-        let entry = table ! key
-        let crop' = crop key (width - 10)
-        let mono  = monochrome pal
-        forM_ entry $ \line -> do
-            unless mono $ setSGR [ SetColor Foreground Dull Green ]
-            putStr [key]
-            unless mono $ setSGR [ SetColor Foreground Dull Yellow ]
-            B.putStr $ indent anum
-            unless mono $ setSGR []
-            IO.putStrLn $ indent (crop' (decodeUtf8 line))
-  where
-    indent s = " " <> s
-    dropLastField = T.reverse . T.dropWhile (/= ',') . T.reverse
-    cropText f cap s = if cap < T.length s then f cap s else s
-    crop key = cropText $ \cap s ->
-        if key `elem` ['S'..'X']
-            then dropLastField (T.take cap s)
-            else T.take (cap-2) s <> ".."
+        inp <- case terms opts of
+                 [] -> readStdin
+                 ts -> return (map B.pack ts)
+        let parseInp t =
+              case decodeStrict t of
+                Just e  -> (getPrg e : getTrail e, Right (packSeq (getSeq e)))
+                Nothing -> case parseIntegerSeq t of
+                             Just s  -> let s' = packSeq s
+                                        in ([Prg ("{" <> unPSeq s' <> "}")], Right s')
+                             Nothing -> case parseANum t of
+                                          Just a  -> ([], Left a)
+                                          Nothing -> error "cannot parse input"
+        return $ SearchLocalDB sdb ndb (limit opts) (map parseInp inp)
 
 printOutput :: Output -> IO ()
 printOutput NOP = return ()
-printOutput (Entries es) =
-    forM_ es $ \(PackedEntry (PPrg p) (PSeq s)) ->
-        B.putStrLn $ p <> " => {" <> s <> "}"
-
-printOutput (OEISRepliesJSON rss) = mapM_ (BL.putStrLn . encode) rss
-
-printOutput (OEISReplies view rss) = do
-    let hideQuery = few rss || allANum rss
-    let noReplies = null $ concat [ rs | QA _ rs <- rss ]
-    forM_ (zip [1::Int ..] rss) $ \(i, QA q replies) -> do
-        unless hideQuery $ do
-            when (i > 1) $ putStrLn ""
-            B.putStrLn ("query: " <> q)
-        forM_ replies $ \r -> do
-            putStrLn ""
-            printReply view r
-    unless noReplies $ putStrLn ""
+printOutput (Entries es) = mapM_ (BL.putStrLn . encode) es
+printOutput (OEISReplies rs) = mapM_ (BL.putStrLn . encode) rs
 
 -- Construct a list of replies associated with a list of A-numbers.
-mkReplies :: Map ANum PackedSeq -> Map ANum Name -> [ANum] -> [Reply]
-mkReplies s n anums =
-    [ Reply k (Table $ M.fromList [('S', [unPSeq (s!k)]), ('N', [n!k])])
-    | k <- anums
-    , M.member k s && M.member k n
+mkReplies :: Map ANum [Integer] -> Map ANum Name -> [(Trail, ANum)] -> [Entry]
+mkReplies s n tas =
+    [ Entry (Prg b) (s!a) Nothing (Just (n!a)) trail
+    | (trail, a@(ANum b)) <- tas
+    , M.member a s && M.member a n
     ]
 
 sloane :: Input -> IO Output
 sloane inp =
     case inp of
 
-      SearchLocalDB sdb ndb jsonflag maxReplies view ts -> do
+      SearchLocalDB sdb ndb maxReplies ts -> do
           let sm = M.fromList $ parseStripped (unDB sdb)
           let nm = M.fromList $ parseNames (unDB ndb)
-          let qas = [ QA q (mkReplies sm nm ks)
-                    | (q, ks) <-
-                        [ case t of
-                            Right s@(PSeq r) -> (r, grepN maxReplies s sdb)
-                            Left (ANum anum) -> (anum, [ANum anum])
-                        | t <- ts
-                        ]
-                    ]
-          return $ if jsonflag then OEISRepliesJSON qas else OEISReplies view qas
+          let anums (trail, Left  a) = [ (trail, a) ]
+              anums (trail, Right s) = [ (trail, a) | a <- grepN maxReplies s sdb ]
+          return $ Entries (mkReplies sm nm . anums =<< ts)
 
-      SearchOEIS jsonflag lim view q -> do
+      SearchOEIS lim q -> do
           let kvs = [("n", B.pack (show lim)), ("q", B.pack q), ("fmt", "text")]
           replies <- requestPage oeisURL kvs
-          let qas = [QA (B.pack q) (parseReplies replies)]
-          return $ if jsonflag then OEISRepliesJSON qas else OEISReplies view qas
+          return $ OEISReplies (parseOEISEntries replies)
 
       FilterSeqs db invFlag es -> do
           let bloom = mkBloomFilter db
           return $ Entries
-              [ e | e@(PackedEntry _ s) <- es
+              [ e | e <- es
+              , let s = packSeq (getSeq e)
               , not (B.null (unPSeq s))
               , invFlag `xor` (s `isFactorOf` bloom && not (null (grep s db)))
               ]
 
       UpdateDBs sloanedir sdbPath ndbPath -> do
           createDirectoryIfMissing False sloanedir
-          let msg1 = "Downloading " ++ strpdURL ++ ": "
+          let msg1 = "Downloading " ++ seqsURL ++ ": "
           let msg2 = "Downloading " ++ namesURL ++ ": "
           putStr msg1 >> hFlush stdout
-          download (length msg1) strpdURL sdbPath >> putStrLn ""
+          download (length msg1) seqsURL sdbPath >> putStrLn ""
           putStr msg2 >> hFlush stdout
           download (length msg2) namesURL ndbPath >> putStrLn ""
           return NOP
 
-      Empty -> putStrLn nameVer >> return NOP
+      Empty -> do
+          putStrLn $ "sloane " ++ versionString
+          return NOP
 
 -- | Main function and entry point for sloane.
 main :: IO ()
