diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c)2011, Felipe Lessa
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Felipe Lessa nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/biostockholm.cabal b/biostockholm.cabal
new file mode 100644
--- /dev/null
+++ b/biostockholm.cabal
@@ -0,0 +1,56 @@
+Name:                biostockholm
+Version:             0.1
+Synopsis:            Reading and writing Stockholm files (multiple sequence alignment, used by Rfam and Infernal).
+License:             BSD3
+License-file:        LICENSE
+Author:              Felipe Lessa
+Maintainer:          felipe.lessa@gmail.com
+Category:            Bioinformatics
+Build-type:          Simple
+Cabal-version:       >=1.8
+Description:
+  Parsing and pretty printing of files in Stockholm 1.0 format.  See:
+  .
+  * <http://sonnhammer.sbc.su.se/Stockholm.html>
+  .
+  * <ftp://ftp.sanger.ac.uk/pub/databases/Pfam/current_release/relnotes.txt>
+  .
+  * <http://en.wikipedia.org/wiki/Stockholm_format>
+
+Source-repository head
+  Type:     darcs
+  Location: http://patch-tag.com/r/felipe/biostockholm
+
+Library
+  Hs-Source-Dirs: src
+  Exposed-modules:
+    Bio.Sequence.Stockholm
+  Ghc-Options: -Wall
+  Build-depends:
+        base               >= 3     && < 5
+      , containers         >= 0.2   && < 0.5
+      , bytestring         == 0.9.*
+      , deepseq            == 1.1.*
+      , explicit-exception == 0.1.*
+
+      , biocore            >= 0.1   && < 0.3
+
+Test-suite runtests
+  Type: exitcode-stdio-1.0
+  Hs-Source-Dirs: src
+  Main-is: runtests.hs
+  Other-modules:
+    Bio.Sequence.Stockholm
+  Ghc-Options: -Wall
+  Cpp-Options: -DTEST
+  Build-depends:
+        base               >= 3     && < 5
+      , containers         >= 0.2   && < 0.5
+      , bytestring         == 0.9.*
+      , deepseq            == 1.1.*
+      , explicit-exception == 0.1.*
+
+      , biocore            >= 0.1   && < 0.3
+
+      , hspec              == 0.9.*
+      , HUnit              == 1.2.*
diff --git a/src/Bio/Sequence/Stockholm.hs b/src/Bio/Sequence/Stockholm.hs
new file mode 100644
--- /dev/null
+++ b/src/Bio/Sequence/Stockholm.hs
@@ -0,0 +1,550 @@
+{-# LANGUAGE CPP, EmptyDataDecls, DeriveDataTypeable, OverloadedStrings #-}
+
+-- | Parsing and pretty printing of files in Stockholm 1.0
+-- format.  See:
+--
+--    - <http://sonnhammer.sbc.su.se/Stockholm.html>
+--
+--    - <ftp://ftp.sanger.ac.uk/pub/databases/Pfam/current_release/relnotes.txt>
+--
+--    - <http://en.wikipedia.org/wiki/Stockholm_format>
+
+module Bio.Sequence.Stockholm
+    (-- * Data types
+     Stockholm(..)
+    ,StockholmSeq(..)
+    ,Ann(..)
+    ,FileAnnotation(..)
+    ,SequenceAnnotation(..)
+    ,ColumnAnnotation(..)
+    ,InFile
+    ,InSeq
+    ,findAnn
+
+     -- * Parsing
+    ,parseStockholm
+    ,StockholmExc(..)
+
+     -- * Printing
+    ,prettyPrintStockholm
+
+#ifdef TEST
+     -- * Test cases
+    ,test_Stockholm
+#endif
+    )
+    where
+
+-- from base
+import Control.Applicative ((<$>))
+import Control.Arrow (second)
+import Control.DeepSeq (NFData(..))
+import Control.Monad (mplus)
+import Data.Char (isSpace)
+import Data.List (foldl', find)
+import Data.Maybe (fromMaybe)
+import Data.Typeable (Typeable)
+import Text.Show (showParen, showString)
+
+-- from containers
+import qualified Data.Map as M
+
+-- from bytestring
+import qualified Data.ByteString.Lazy.Char8 as B
+import Data.ByteString.Lazy.Char8 (ByteString)
+
+-- from biocore
+import Bio.Core.Sequence
+
+-- from explicit-exception
+import Control.Monad.Exception.Synchronous (Exceptional, throw)
+
+
+#ifdef TEST
+import Test.Hspec.Monadic
+import Test.Hspec.HUnit ()
+import Test.HUnit
+#endif
+
+
+-- | An Stockholm 1.0 formatted file represented in memory.
+data Stockholm = Stockholm [Ann FileAnnotation]
+                           [Ann (ColumnAnnotation InFile)]
+                           [StockholmSeq]
+                 deriving (Show, Eq, Typeable)
+
+instance NFData Stockholm where
+    rnf (Stockholm file clmn seqs) = rnf file `seq` rnf clmn `seq` rnf seqs
+
+-- | A sequence in Stockholm 1.0 format.
+data StockholmSeq = StSeq !SeqLabel
+                          !SeqData
+                          [Ann SequenceAnnotation]
+                          [Ann (ColumnAnnotation InSeq)]
+                    deriving (Eq, Typeable)
+
+-- We don't derive Show to be able support biocore-0.1, which
+-- doesn't have Show instances for SeqLabel and SeqData.
+instance Show StockholmSeq where
+    showsPrec prec (StSeq (SeqLabel l) (SeqData d) sa ca) =
+        showParen (prec > 10) $
+          showString "StSeq (SeqLabel " .
+          showsPrec 11 l .
+          showString ") (SeqData " .
+          showsPrec 11 d .
+          (')':) . (' ':) .
+          showsPrec 11 sa .
+          (' ':) .
+          showsPrec 11 ca
+
+
+instance NFData StockholmSeq where
+    rnf (StSeq _ _ sa ca) = rnf sa `seq` rnf ca
+
+instance BioSeq StockholmSeq where
+    seqlabel  (StSeq sl _ _ _) = sl
+    seqdata   (StSeq _ sd _ _) = sd
+    seqlength (StSeq _ sd _ _) = Offset $ B.length (unSD sd)
+
+-- | A generic annotation.
+data Ann d = Ann { feature :: !d
+                 , text    :: !ByteString
+                 }
+             deriving (Show, Eq, Ord, Typeable)
+
+instance NFData (Ann d) where
+    -- already strict, default instance
+
+-- | Possible file annotations.
+data FileAnnotation =
+    AC -- ^ Accession number:    Accession number in form PFxxxxx.version or PBxxxxxx.
+  | ID -- ^ Identification:      One word name for family.
+  | DE -- ^ Definition:          Short description of family.
+  | AU -- ^ Author:              Authors of the entry.
+  | SE -- ^ Source of seed:      The source suggesting the seed members belong to one family.
+  | GA -- ^ Gathering method:    Search threshold to build the full alignment.
+  | TC -- ^ Trusted Cutoff:      Lowest sequence score and domain score of match in the full alignment.
+  | NC -- ^ Noise Cutoff:        Highest sequence score and domain score of match not in full alignment.
+  | TP -- ^ Type:                Type of family (presently Family, Domain, Motif or Repeat).
+  | SQ -- ^ Sequence:            Number of sequences in alignment.
+  | AM -- ^ Alignment Method:    The order ls and fs hits are aligned to the model to build the full align.
+
+  | DC -- ^ Database Comment:    Comment about database reference.
+  | DR -- ^ Database Reference:  Reference to external database.
+  | RC -- ^ Reference Comment:   Comment about literature reference.
+  | RN -- ^ Reference Number:    Reference Number.
+  | RM -- ^ Reference Medline:   Eight digit medline UI number.
+  | RT -- ^ Reference Title:     Reference Title.
+  | RA -- ^ Reference Author:    Reference Author
+  | RL -- ^ Reference Location:  Journal location.
+  | PI -- ^ Previous identifier: Record of all previous ID lines.
+  | KW -- ^ Keywords:            Keywords.
+  | CC -- ^ Comment:             Comments.
+  | NE -- ^ Pfam accession:      Indicates a nested domain.
+  | NL -- ^ Location:            Location of nested domains - sequence ID, start and end of insert.
+
+  | F_Other !ByteString -- ^ Other file annotation.
+    deriving (Show, Eq, Ord, Typeable)
+
+-- | Possible column annotations.  Phantom type can be 'InFile'
+-- or 'InSeq'.
+data ColumnAnnotation a =
+    SS -- ^ Secondary structure.
+  | SA -- ^ Surface accessibility.
+  | TM -- ^ TransMembrane.
+  | PP -- ^ Posterior probability.
+  | LI -- ^ LIgand binding.
+  | AS -- ^ Active site.
+  | PAS -- ^ AS - Pfam predicted.
+  | SAS -- ^ AS - from SwissProt.
+  | IN -- ^ INtron (in or after).
+
+  | C_Other !ByteString -- ^ Other column annotation.
+    deriving (Show, Eq, Ord, Typeable)
+
+-- | Phantom type for 'ColumnAnnotation's of the whole file.
+data InFile
+-- | Phantom type for 'ColumnAnnotation's of a single sequence.
+data InSeq
+
+-- | Possible sequence annotations.
+data SequenceAnnotation =
+    S_AC -- ^ Accession number
+  | S_DE -- ^ Description
+  | S_DR -- ^ Database reference
+  | OS -- ^ Organism (species)
+  | OC -- ^ Organism classification (clade, etc.)
+  | LO -- ^ Look (Color, etc.)
+
+  | S_Other !ByteString -- ^ Other sequence annotation.
+    deriving (Show, Eq, Ord, Typeable)
+
+
+-- | Class used internally below just to simplify the code.
+class IsAnnotation a where
+    parseAnn :: ByteString -> a
+    showAnn  :: a -> ByteString
+
+mkParseAnn :: (ByteString -> a -> ByteString) -> [(ByteString, a)]
+           -> (ByteString -> a) -> ByteString -> a
+mkParseAnn modify anns mkOther =
+    let annots = M.fromList anns
+    in \feat -> let featMod = modify feat (error "mkParseAnn: never here")
+                in fromMaybe (mkOther feat) $ M.lookup featMod annots
+
+mkShowAnn :: Ord a => (ByteString -> a -> ByteString) -> [(ByteString, a)]
+          -> (a -> Maybe ByteString) -> a -> ByteString
+mkShowAnn modify anns fromOther =
+    let annots = M.fromList [(a,b) | (b,a) <- anns]
+    in \ann -> fromMaybe (error "mkShowAnn: never here 2") $
+               fromOther ann `mplus` (mod' <$> M.lookup ann annots)
+             where mod' = flip modify (error "mkShowAnn: never here 1")
+
+instance IsAnnotation SequenceAnnotation where
+    parseAnn = mkParseAnn const seqAnns S_Other
+    showAnn  = mkShowAnn  const seqAnns f
+        where f (S_Other o) = Just o
+              f _           = Nothing
+
+seqAnns :: [(ByteString, SequenceAnnotation)]
+seqAnns = [("LO",LO), ("OC",OC), ("OS",OS),
+           ("AC",S_AC), ("DE",S_DE), ("DR",S_DR)]
+
+
+instance IsAnnotation FileAnnotation where
+    parseAnn = mkParseAnn const fileAnns F_Other
+    showAnn  = mkShowAnn  const fileAnns f
+        where f (F_Other o) = Just o
+              f _           = Nothing
+
+fileAnns :: [(ByteString, FileAnnotation)]
+fileAnns = [("AC",AC), ("AM",AM), ("AU",AU), ("CC",CC),
+            ("DC",DC), ("DE",DE), ("DR",DR), ("GA",GA),
+            ("ID",ID), ("KW",KW), ("NC",NC), ("NE",NE),
+            ("NL",NL), ("PI",PI), ("RA",RA), ("RC",RC),
+            ("RL",RL), ("RM",RM), ("RN",RN), ("RT",RT),
+            ("SE",SE), ("SQ",SQ), ("TC",TC), ("TP",TP)]
+
+
+instance ClmnAnnLoc a => IsAnnotation (ColumnAnnotation a) where
+    parseAnn = mkParseAnn removeSuffix clmnAnns C_Other
+        where
+          removeSuffix feat phantom =
+              let suffix = clmnAnnSuffix phantom
+                  (f, s) = B.splitAt (B.length feat - B.length suffix) feat
+              in if suffix == s then f else ""
+    showAnn = mkShowAnn addSuffix clmnAnns f
+        where
+          f (C_Other o) = Just o
+          f _           = Nothing
+          addSuffix feat phantom = feat `B.append` clmnAnnSuffix phantom
+
+clmnAnns :: [(ByteString, ColumnAnnotation a)]
+clmnAnns = [("AS",AS), ("IN",IN), ("LI",LI), ("PAS",PAS), ("PP",PP),
+            ("SA",SA), ("SAS",SAS), ("SS",SS), ("TM",TM)]
+
+class ClmnAnnLoc a where
+    clmnAnnSuffix :: b a -> ByteString
+instance ClmnAnnLoc InSeq where
+    clmnAnnSuffix _ = ""
+instance ClmnAnnLoc InFile where
+    clmnAnnSuffix _ = "_cons"
+
+parseAnn' :: IsAnnotation a => ByteString -> ByteString -> Ann a
+parseAnn' = Ann . parseAnn
+
+
+
+
+#ifdef TEST
+test_parseAnnots :: Specs
+test_parseAnnots =
+    describe "parse*" $ do
+      it "1"  $ parseAnn "AC" @?= AC
+      it "2"  $ parseAnn "SQ" @?= SQ
+      it "3a" $ parseAnn "SS" @?= (SS :: ColumnAnnotation InSeq)
+      it "3b" $ parseAnn "SS" @?= (C_Other "SS" :: ColumnAnnotation InFile)
+      it "4a" $ parseAnn "SS_cons" @?= (SS :: ColumnAnnotation InFile)
+      it "4b" $ parseAnn "SS_cons" @?= (C_Other "SS_cons" :: ColumnAnnotation InSeq)
+      it "5a" $ parseAnn "SS_CONS" @?= (C_Other "SS_CONS" :: ColumnAnnotation InSeq)
+      it "5b" $ parseAnn "SS_CONS" @?= (C_Other "SS_CONS" :: ColumnAnnotation InFile)
+      it "6"  $ parseAnn "LO" @?= LO
+#endif
+
+
+-- | Find an annotation.  For example, you may use @'findAnn' 'SS'@
+-- to find the secondary of an Stockholm file.
+findAnn :: Eq d => d -> [Ann d] -> Maybe ByteString
+findAnn x = fmap text . find ((== x) . feature)
+
+
+-- | Exceptions that may happen while parsing a Stockholm file.
+class StockholmExc e where
+    -- | File is empty.
+    emptyFileExc :: e
+
+    -- | Header is missing.
+    headerExc :: e
+
+    -- | Malformed annotation.  The line is passed as argument.
+    malformedAnnExc :: ByteString -> e
+
+    -- | Unknown annotation type.
+    unknownAnnTypeExc :: Char -> e
+
+    -- | Malformed sequence line. The line is passed as argument.
+    malformedSeqDataExc :: ByteString -> e
+
+instance StockholmExc () where
+    emptyFileExc          = ()
+    headerExc             = ()
+    malformedAnnExc     _ = ()
+    unknownAnnTypeExc   _ = ()
+    malformedSeqDataExc _ = ()
+
+instance StockholmExc ByteString where
+    emptyFileExc = "parseStockholm: empty file."
+    headerExc = "parseStockholm: header is missing."
+    malformedAnnExc line =
+        B.concat ["parseStockholm: malformed annotation '", line, "'."]
+    unknownAnnTypeExc typ =
+        B.concat ["parseStockholm: unknown annotation type '", B.pack [typ], "'."]
+    malformedSeqDataExc line =
+        B.concat ["parseStockholm: malformed sequence data line '", line, "'."]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+-- | Type (F, C, S or R), corresponding sequence, name and data.
+type ParseAnnRet = ((Char, Maybe SeqLabel, ByteString), ByteString)
+
+-- | @parseAnnotation line@ tries to parse a line as an annotation.
+parseAnnotation :: (StockholmExc e) => ByteString -> Exceptional e ParseAnnRet
+parseAnnotation line
+    | not (B.isPrefixOf "#=G" line) || B.length line < 5 =
+        throw (malformedAnnExc line)
+parseAnnotation line =
+  let Just (typ, rest) = B.uncons $ B.drop 3 line
+      (word1, text1) = second dropSpace . B.break isSpace $ dropSpace rest
+      (word2, text2) = second dropSpace . B.break isSpace $ text1
+      global = ((typ, Nothing,               word1), text1)
+      seqspe = ((typ, Just (SeqLabel word1), word2), text2)
+  in case typ of
+       'F' -> return global
+       'C' -> return global
+       'S' -> return seqspe
+       'R' -> return seqspe
+       _ -> throw (unknownAnnTypeExc typ)
+
+dropSpace :: ByteString -> ByteString
+dropSpace = B.dropWhile isSpace
+
+
+-- | @parseSeqData line@ tries to parse a line as some data of a sequence.
+parseSeqData :: (StockholmExc e) => ByteString
+             -> Exceptional e (SeqLabel, SeqData)
+parseSeqData str = case B.words str of
+                     [ident, sq] -> return (SeqLabel ident, SeqData sq)
+                     _ -> throw (malformedSeqDataExc str)
+
+
+-- | @parseStockholm@ parses a file in Stockholm 1.0 format.
+--
+--   Each file must be completely read before it is used because
+--   the Stockholm format allows information to be given in any
+--   part of the file.  However, there may be multiple
+--   \"Stockholm files\" concatenated in a single \"filesystem
+--   file\".  These multiple files are read independently, which
+--   is why we return a list of 'Exceptional'@s@.
+--
+--   If you prefer to read the whole file in one go, use
+--   @'sequence' (parseStockholm input)@, which will fail if any
+--   family fails.
+parseStockholm :: (StockholmExc e) => ByteString
+               -> [Exceptional e Stockholm]
+parseStockholm = map parseStockholm' . split .
+                 filter (not . B.all isSpace) . B.lines
+    where
+      split [] = []
+      split xs = case break (B.isPrefixOf "//") xs of
+                   (y, ys) -> y : split (tail ys)
+
+parseStockholm' :: (StockholmExc e) => [ByteString]
+                -> Exceptional e Stockholm
+parseStockholm' = header . filter (not . B.null)
+    where
+      -- Find
+      header (h:hs)
+          | h == stockholm = do (annots,seqs) <- go initial hs
+                                return (makeStockholm annots seqs)
+          | otherwise      = throw headerExc
+          where stockholm = "# STOCKHOLM 1.0"
+                initial   = (M.empty, M.empty)
+      header [] = throw emptyFileExc
+
+      -- End of file
+      go acc [] = return acc
+
+      -- Annotation
+      go (annots,seqs) (line:ls) | B.take 2 line == "#=" = do
+        annot <- parseAnnotation line
+        go (insertDM annot annots, seqs) ls
+
+      -- Comment
+      go (annots,seqs) (l:ls) | B.head l == '#' =
+        go (annots,seqs) ls
+
+      -- Otherwise a sequence
+      go (annots,seqs) (line:ls) = do
+        seqData <- parseSeqData line
+        go (annots, insertDM seqData seqs) ls
+
+
+type DiffMap a b = M.Map a ([b] -> [b])
+
+insertDM :: Ord a => (a,b) -> DiffMap a b -> DiffMap a b
+insertDM (key,val) = M.insertWith (flip (.)) key (val:)
+
+finishDM :: (b -> ByteString) -> DiffMap a b -> M.Map a ByteString
+finishDM f = fmap (B.concat . map f . ($ []))
+
+-- | Glue everything in place, as the Stockholm format lets
+--   everything be everywhere and split in any number of parts.
+makeStockholm :: DiffMap (Char, Maybe SeqLabel, ByteString) ByteString
+              -> DiffMap SeqLabel SeqData -> Stockholm
+makeStockholm annotsDM seqsDM =
+    let annots = finishDM id   annotsDM
+        seqs   = finishDM unSD seqsDM
+
+        -- Separate the global from the sequence annotations
+        go ('F', Nothing, feat) txt (f,c,r) = (parseAnn' feat txt:f,c,r)
+        go ('C', Nothing, feat) txt (f,c,r) = (f,parseAnn' feat txt:c,r)
+        go (typ, Just sq, feat) txt (f,c,r) = (f,c,(typ,sq,feat,txt):r)
+        go _ _ _ = error "makeStockholm: not here, ever"
+
+        -- Separate the sequence annotations to each annotation
+        add ('S',sq,feat,txt) = flip M.adjust sq $ \(StSeq sl sd sa ca) ->
+                                  StSeq sl sd (parseAnn' feat txt : sa) ca
+        add ('R',sq,feat,txt) = flip M.adjust sq $ \(StSeq sl sd sa ca) ->
+                                  StSeq sl sd sa (parseAnn' feat txt : ca)
+        add _ = error "makeStockholm: not here either"
+
+        -- Glue everything
+        (file, clmn, rest) = M.foldWithKey go ([],[],[]) annots
+        plainseqs = M.mapWithKey (\k s -> StSeq k (SeqData s) [] []) seqs
+    in Stockholm file clmn (M.elems $ foldl' (flip add) plainseqs rest)
+
+
+
+
+-- | Pretty-prints an Stockholm file.  We follow Rfam preferences
+-- and do not wrap lines.
+prettyPrintStockholm :: Stockholm -> B.ByteString
+prettyPrintStockholm (Stockholm file clmn seqs) =
+    let showAnnF :: IsAnnotation a => Char -> Ann a -> (ByteString, ByteString)
+        showAnnF t ann = (B.concat [B.pack ("#=G" ++ t : " "),
+                                    showAnn (feature ann)], text ann)
+        showAnnS :: IsAnnotation a => ByteString -> Char -> Ann a -> (ByteString, ByteString)
+        showAnnS s t ann = (B.unwords [B.pack ("#=G" ++ [t]), s,
+                                       showAnn (feature ann)], text ann)
+
+        fileLines = map (showAnnF 'F') file
+        clmnLines = map (showAnnF 'C') clmn
+        sequences = do
+          StSeq (SeqLabel name) (SeqData seqd) sa ca <- seqs
+          (name, seqd) : map (showAnnS name 'R') ca
+                      ++ map (showAnnS name 'S') sa
+
+        allLines    = fileLines ++ sequences ++ clmnLines
+        firstColLen = maximum $ map (B.length . fst) allLines
+        mkLine (col1, col2) = B.concat [col1, B.replicate n ' ', col2]
+            where n = 1 + firstColLen - B.length col1
+    in B.unlines ("# STOCKHOLM 1.0" : map mkLine allLines ++ ["//"])
+
+
+
+#ifdef TEST
+stockFile :: B.ByteString
+stockFile = B.unlines [
+  "# STOCKHOLM 1.0",
+  "#=GF AU Infernal 1.0",
+  "",
+  "#=GS Purine1 DE Number 1 :)",
+  "Purine1      AAAAUUGAAUAUCGUUUUACUUGUUUAUGUC-GUGAAU-UGGCAC-GACG",
+  "Purine2      AAAAUUUAAUAA-GAAGCACUCAUAUAAUCCCGAGAAUAUGGCUCGGGAG",
+  "Purine3      UGGCAGUAACUAGCGUCACUUCGUAUAACCCCAGUGAUAUGGAUUGGGGG",
+  "#=GC SS_cons :::::::::::::::::((((((((,,,<<<-<<<_______>>>->>>,",
+  "",
+  "# We may have comments =)",
+  "",
+  "Purine1      UUUCUACAAGGUG-CCGGAA--CACCUAACAAUAAGUAAGUCAGCAGUGA",
+  "Purine2      UCUCUACCGAACAACCGUAAAUUGUUCGACUAUGAGUGAAAGUGUACCUA",
+  "Purine3      UCUCUACCAGGAACCAAUAA--AUCCUGAUUACGAAGAGUUUAGUGCUUU",
+  "#=GC SS_cons ,,,,,,,<<<<<<_________>>>>>>,,))))))))::::::::::::",
+  "",
+  "Purine1      GAU",
+  "Purine2      GGG",
+  "Purine3      AGU",
+  "#=GC SS_cons :::",
+  "// "]
+
+purine1, purine2, purine3 :: SeqData
+ss_cons :: ByteString
+purine1 = SeqData "AAAAUUGAAUAUCGUUUUACUUGUUUAUGUC-GUGAAU-UGGCAC-GACGUUUCUACAAGGUG-CCGGAA--CACCUAACAAUAAGUAAGUCAGCAGUGAGAU"
+purine2 = SeqData "AAAAUUUAAUAA-GAAGCACUCAUAUAAUCCCGAGAAUAUGGCUCGGGAGUCUCUACCGAACAACCGUAAAUUGUUCGACUAUGAGUGAAAGUGUACCUAGGG"
+purine3 = SeqData "UGGCAGUAACUAGCGUCACUUCGUAUAACCCCAGUGAUAUGGAUUGGGGGUCUCUACCAGGAACCAAUAA--AUCCUGAUUACGAAGAGUUUAGUGCUUUAGU"
+ss_cons =         ":::::::::::::::::((((((((,,,<<<-<<<_______>>>->>>,,,,,,,,<<<<<<_________>>>>>>,,)))))))):::::::::::::::"
+
+result :: [Stockholm]
+result = [Stockholm file clmn seqs]
+    where
+      file = [Ann AU "Infernal 1.0"]
+      clmn = [Ann SS ss_cons]
+      seqs = [mkStock "Purine1" purine1 [Ann S_DE "Number 1 :)"],
+              mkStock "Purine2" purine2 [],
+              mkStock "Purine3" purine3 []]
+      mkStock name data_ sa = StSeq name data_ sa []
+
+stockFile2 :: B.ByteString
+stockFile2 = B.unlines [stockFile, stockFile]
+
+result2 :: [Stockholm]
+result2 = result ++ result
+
+returnExc :: a -> Exceptional B.ByteString a
+returnExc = return
+
+test_parseStockholm :: Specs
+test_parseStockholm =
+    describe "parseStockholm" $ do
+      it "correctly parses test file 1" $ parseStockholm stockFile  @?= returnExc result
+      it "correctly parses test file 2" $ parseStockholm stockFile2 @?= returnExc result2
+
+test_prettyPrintStockholm :: Specs
+test_prettyPrintStockholm =
+    describe "parseStockholm/prettyPrintStockholm" $ do
+      it "parses printed test file 1" $ parseStockholm (func result)  @?= returnExc result
+      it "parses printed test file 2" $ parseStockholm (func result2) @?= returnExc result2
+    where func = B.unlines . map prettyPrintStockholm
+#endif
+
+
+#ifdef TEST
+test_Stockholm :: Specs
+test_Stockholm = describe "Bio.Sequence.Stockholm" $ do
+                   test_parseAnnots
+                   test_parseStockholm
+                   test_prettyPrintStockholm
+#endif
