diff --git a/biopsl.cabal b/biopsl.cabal
--- a/biopsl.cabal
+++ b/biopsl.cabal
@@ -1,5 +1,5 @@
 Name:                biopsl
-Version:             0.2
+Version:             0.3
 Synopsis:            Library and executables for working with PSL files
 Description:         The library contains the functionality for reading and writing
 		     PSL files (alignment data, e.g. from BLAT output). It duplicates
@@ -11,8 +11,13 @@
 Stability:           Experimental
 Category:            Bioinformatics
 Build-type:          Simple
-Cabal-version:       >=1.2
+Cabal-version:       >=1.6
 
+Source-repository this
+   Type:      darcs
+   Location:  http://malde.org/~ketil/biohaskell/biosff
+   Tag:       0.3
+
 Library
   Exposed-modules: Bio.Alignment.PSL
   Build-depends:   base >= 3 && < 5, biocore >= 0.1, bytestring
@@ -21,5 +26,16 @@
 
 Executable psluniq
   Main-Is:        PslUniq.hs
+  Hs-Source-Dirs: src, examples
+  Ghc-Options:    -Wall
+
+Executable pslfilter
+  Main-Is:        PslFilter.hs
+  Hs-Source-Dirs: src, examples
+  Build-Depends:  cmdargs
+  Ghc-Options:    -Wall
+
+Executable pslcov
+  Main-Is:        PslCov.hs
   Hs-Source-Dirs: src, examples
   Ghc-Options:    -Wall
diff --git a/examples/PslCov.hs b/examples/PslCov.hs
new file mode 100644
--- /dev/null
+++ b/examples/PslCov.hs
@@ -0,0 +1,23 @@
+-- PslCov: extract complete hits (more than x% of query covered)
+
+module Main where
+
+import Bio.Alignment.PSL
+import System.Environment (getArgs)
+
+main :: IO ()
+main = do
+  args <- getArgs
+  case args of 
+    [pq,pt,f] -> printPSL . filter (isHit (read pq) (read pt)) =<< readPSL f
+    _ -> error ("Usage: pslcov p q pslfile\n"++
+                "where p is the minimum query coverage\n"++
+                "and q is the minium target coverage.")
+
+isHit :: Double -> Double -> PSL -> Bool
+isHit p q x = m / qs > p && m / ts > q
+  where qs = fromIntegral $ qsize x
+        ts = fromIntegral $ tsize x
+        m  = fromIntegral $ match x
+         
+
diff --git a/examples/PslFilter.hs b/examples/PslFilter.hs
new file mode 100644
--- /dev/null
+++ b/examples/PslFilter.hs
@@ -0,0 +1,61 @@
+{-| Select a subset of PSL records according to specified criteria:
+  e.g. pslfilter --identity-min --overhang-max ... --count-max
+-}
+
+{-# Language DeriveDataTypeable, OverloadedStrings #-}
+
+import Bio.Alignment.PSL
+import System.Console.CmdArgs
+import qualified Data.ByteString.Lazy as L
+
+data Options = Opts 
+  { input :: Maybe FilePath
+  , identity, queryCov, targetCov :: Maybe Double
+  , overhang :: Maybe Int
+  } deriving (Data,Typeable)
+
+defs :: Options
+defs = Opts 
+  { input = Nothing &= args
+  , identity = Nothing &= help "minimum identiy for matches" &= name "i"
+  , overhang = Nothing &= help "maximum overhang for matches" &= name "h"
+  , queryCov = Nothing &= help "minimum query coverage" &= name "q"
+  , targetCov = Nothing &= help "minimum target coverage" &= name "t"
+  } &= summary "pslfilter - extract a subset of PSL records"
+    &= program "pslfilter"
+
+main :: IO ()
+main = do
+  opts <- cmdArgs defs
+  ps <- case input opts of 
+        Nothing  -> (parsePSL `fmap` L.getContents)
+        Just f -> readPSL f
+  let idfilter = case identity opts of
+        Nothing -> const True
+        Just d  -> (\x -> local_identity x >= d)
+      ohfilter = case overhang opts of  
+        Nothing -> const True
+        Just o  -> (\x -> (uncurry max $ overhangs x) < o)
+      qcfilter = case queryCov opts of
+          Nothing -> const True
+          Just q -> (\x -> fromIntegral (match x) / fromIntegral (qsize x) >= q)
+      tcfilter = case targetCov opts of
+          Nothing -> const True
+          Just t -> (\x -> fromIntegral (match x) / fromIntegral (tsize x) >= t)
+  printPSL $ filter (idfilter .&. ohfilter .&. qcfilter .&. tcfilter ) ps
+
+
+(.&.) :: (t -> Bool) -> (t -> Bool) -> t -> Bool
+(.&.) f g = \x -> f x && g x
+
+-- | Calculate the smallest overhang, i.e. the unmatched parts
+--   of either query or target on each side of the local alignment.
+overhangs :: PSL -> (Int,Int)
+overhangs p = case strand p of
+  "+" -> (min (qstart p) (tstart p), min (qsize p-qend p) (tsize p-tend p))
+  "-" -> (min (qstart p) (tsize p-tend p), min (qsize p-qend p) (tstart p))
+  _ -> error ("I couldn't understand strand :"++show (strand p))
+
+-- | Calculate the identity score of the matched region
+local_identity :: PSL -> Double
+local_identity p = fromIntegral (match p) / fromIntegral (match p + mismatch p)
diff --git a/examples/PslUniq.hs b/examples/PslUniq.hs
--- a/examples/PslUniq.hs
+++ b/examples/PslUniq.hs
@@ -13,8 +13,7 @@
         [] -> (parsePSL `fmap` L.getContents)
         [f] -> readPSL f
         _ -> error "Usage: psluniq [pslfile]"
-  L.putStr pslHeader
-  L.putStr $ unparsePSL $ uniq ps
+  printPSL $ uniq ps
   
 uniq :: [PSL] -> [PSL]
 uniq [] = []
diff --git a/src/Bio/Alignment/PSL.hs b/src/Bio/Alignment/PSL.hs
--- a/src/Bio/Alignment/PSL.hs
+++ b/src/Bio/Alignment/PSL.hs
@@ -4,6 +4,8 @@
 
     See <http://genome.ucsc.edu/FAQ/FAQformat#format2> for details.
 -}
+
+{-# Options -funbox-strict-fields #-} -- makes no difference
 module Bio.Alignment.PSL where
 
 import qualified Data.ByteString.Lazy.Char8 as B
@@ -31,6 +33,11 @@
 -- | Create a PSL file from a list of alignments.
 writePSL :: FilePath -> [PSL] -> IO ()
 writePSL f =  B.writeFile f . B.append pslHeader . unparsePSL 
+
+printPSL :: [PSL] -> IO ()
+printPSL ps = do
+  B.putStr pslHeader
+  B.putStr $ unparsePSL ps
 
 -- | Parse a 'ByteString' as a PSL file (note that it must contain the PSL header).
 parsePSL :: ByteString -> [PSL]
