diff --git a/Ast.hs b/Ast.hs
new file mode 100644
--- /dev/null
+++ b/Ast.hs
@@ -0,0 +1,90 @@
+-- |
+-- Module      :  Ast
+-- Copyright   :  (c) Vitaliy Rkavishnikov
+-- License     :  BSD-style (see the file LICENSE)
+-- 
+-- Maintainer  :  virukav@gmail.com
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-- The main types used in the program
+
+module Ast where 
+
+import SedRegex (Pattern)
+import Data.ByteString.Char8 (ByteString)
+
+-- | Editing commands
+data SedCmd = SedCmd Address SedFun  deriving Show
+
+-- | Functions represents a single-character command verb
+data SedFun = 
+              Group [SedCmd]         -- ^ { - group of the sed commands
+            | LineNum                -- ^ = - write to standard output the current line number
+            | Append Text            -- ^ a - append text following each line matched by address 
+            | Branch (Maybe Label)   -- ^ b - transfer control to Label
+            | Change Text            -- ^ c - replace the lines selected by the address with Text
+            | Delete                 -- ^ d - delete line(s) from pattern space
+            | DeletePat              -- ^ D - delete (up to newline) of multiline pattern space
+            | ReplacePat             -- ^ g - copy hold space into the pattern space
+            | AppendPat              -- ^ G - add newline followed by hold space into the pattern space
+            | ReplaceHold            -- ^ h - copy pattern space into hold space
+            | AppendHold             -- ^ H - add newline followed by pattern space into the hold space
+            | Insert Text            -- ^ i - insert Text before each line matched by address 
+            | List                   -- ^ l - list the pattern space, showing non-printing chars in ASCII
+            | Next                   -- ^ n - read next line of input into pattern space
+            | AppendLinePat          -- ^ N - add next input line and newline into pattern space
+            | PrintPat               -- ^ p - print the addressed line(s)
+            | WriteUpPat             -- ^ P - print (up to newline) of multiline pattern space   
+            | Quit                   -- ^ q - quit when address is encounterd
+            | ReadFile FilePath      -- ^ r - add contents of file to the pattern space
+            | Substitute  Pattern Replacement Flags  -- ^ s - substitute Replacement for Pattern
+            | Test (Maybe Label)     -- ^ t - branch to line marked by :label if substitution was made 
+            | WriteFile FilePath     -- ^ w - write the line to file if a replacement was done
+            | Exchange               -- ^ x - exchange pattern space with hold space
+            | Transform Text Text    -- ^ y - transform each char by position in Text to Text
+            | Label Label            -- ^ : - label a line in the scipt for transfering by b or t.
+            | Comment                -- ^ # - ignore a line in the script except "#n" in the first line 
+            | EmptyCmd               -- ^   - ignore spaces
+    deriving Show
+
+-- | The work buffer to keep the selected line(s)
+--data PatternSpace = PatternSpace [ByteString] deriving Show
+
+-- | The work buffer to keep the line(s) temporarily
+--data HoldSpace = HoldSpace [ByteString] deriving Show
+
+-- | An address is either a decimal number that counts input lines cumulatively across files, 
+--   a '$' character that addresses the last line of input, or a context address as BRE 
+data Addr = LineNumber Int
+          | LastLine
+          | Pat Pattern
+    deriving Show
+
+-- | A permissable address is representing by zero, one or two addresses
+data Address = Address (Maybe Addr) (Maybe Addr) Invert
+    deriving Show
+
+-- | Used in the replacement string. An appersand ('&') will be replaced by the
+--   string matched the BRE. The characters "\n", where n is a digit will be
+--   replaced by the corresponding back-reference expression.
+data Occurrence = Replace Int | ReplaceAll 
+    deriving Show
+
+-- | The flag to control the pattern space output in the substitute function
+type OutputPat = Bool
+
+-- | The allowed sequence of the Occurrence and OutputPat flags in the substitute
+--   function
+data OccurrencePrint = OccurrencePrint (Maybe Occurrence) OutputPat |
+                       PrintOccurrence OutputPat (Maybe Occurrence)
+    deriving Show
+
+-- | Flags used in the substitute command
+data Flags = Flags (Maybe OccurrencePrint) (Maybe FilePath) 
+    deriving Show
+
+type Replacement = ByteString
+type Invert = Bool
+type Text = ByteString
+type Label = ByteString
diff --git a/Hsed.cabal b/Hsed.cabal
new file mode 100644
--- /dev/null
+++ b/Hsed.cabal
@@ -0,0 +1,55 @@
+Name:                Hsed
+Version:             0.1
+Description:         Haskell Stream Editor
+License:             BSD3
+License-File:        LICENSE
+Author:              Vitaliy Rukavishnikov
+Maintainer:          virukav@gmail.com
+Homepage:            http://github.com/rukav/Hsed
+Bug-Reports:         mailto:virukav@gmail.com
+Build-Type:          Simple
+Tested-with:	     GHC==6.12.3
+Category:            Editor
+Synopsis:            Stream Editor in Haskell
+Data-Dir:            tests
+Data-Files:	     Append.in,Append.ok,Append.sed,Branch.in,Branch.ok,Branch.sed,
+                     BranchTag.ok,BranchTag.in,BranchTag.ok,BranchTag.sed,Change.in,
+                     Change.ok,Change.sed,ChangeAddr.in,ChangeAddr.ok,ChangeAddr.sed,
+                     Comment.in,Comment.ok,Comment.sed,Delete.in,Delete.ok,Delete.sed,
+                     DeleteMultiline.in,DeleteMultiline.ok,DeleteMultiline.sed,Empty.in,
+                     Empty.ok,Empty.sed,Hold.in,Hold.ok,Hold.sed,HoldTransform.in,
+                     HoldTransform.ok,HoldTransform.sed,Insert.in,Insert.ok,Insert.sed,
+                     LineNum.in,LineNum.ok,LineNum.sed,LineNumAddr.in,LineNumAddr.ok,
+                     LineNumAddr.sed,LineNumInvert.in,LineNumInvert.ok,LineNumInvert.sed,
+                     LineNumMany.in,LineNumMany.ok,LineNumMany.sed,LineNumPrint.in,
+                     LineNumPrint.ok,LineNumPrint.sed,List.in,List.ok,List.sed,Next.in,
+                     Next.ok,Next.sed,NextMultiline.in,NextMultiline.ok,NextMultiline.sed,
+                     NextMultiline2.in,NextMultiline2.ok,NextMultiline2.sed,NextSimple.in,
+                     NextSimple.ok,NextSimple.sed,Print.in,Print.ok,Print.sed,PrintMultiline.in,
+                     PrintMultiline.ok,PrintMultiline.sed,Quit.in,Quit.ok,Quit.sed,ReadFile.in,
+                     ReadFile.ok,ReadFile.sed,SubstituteGlobal.in,SubstituteGlobal.ok,
+                     SubstituteGlobal.sed,SubstituteOccur.in,SubstituteOccur.ok,
+                     SubstituteOccur.sed,SubstituteOccurEol.in,SubstituteOccurEol.ok,
+                     SubstituteOccurEol.sed,SubstituteRef.in,SubstituteRef.ok,SubstituteRef.sed,
+                     Transform.in,Transform.ok,Transform.sed,WriteFile.in,WriteFile.ok,
+                     WriteFile.sed,company.lst
+Cabal-Version:       >=1.2
+
+Executable Hsed
+    Main-is:             Hsed.hs
+    Other-modules:  	 Ast, SedRegex, Parsec, SedState, StreamEd, TestSuite
+    Build-Depends:       base >= 3.0.3.2 && < 5, 
+                         Glob >= 0.5.1, 
+                         cmdargs >= 0.3, 
+                         data-accessor >= 0.2.1.4,
+                         data-accessor-template >= 0.2.1.5, 
+                         data-accessor-transformers >= 0.2.1.2, 
+                         parsec, 
+                         bytestring, 
+                         regex-compat, 
+                         regex-base,
+                         regex-posix, 
+                         array, 
+                         filepath, 
+                         mtl, 
+                         haskell98
diff --git a/Hsed.hs b/Hsed.hs
new file mode 100644
--- /dev/null
+++ b/Hsed.hs
@@ -0,0 +1,33 @@
+-- |
+-- Module      :  Main
+-- Copyright   :  (c) Vitaliy Rkavishnikov
+-- License     :  BSD-style (see the file LICENSE)
+-- 
+-- Maintainer  :  virukav@gmail.com
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-- See "The Open Group Base Specifications Issue 7" for the requirements
+-- This version of the Haskell Sed uses regex-posix package to parse all regular 
+-- expressions. At the moment it doesn't supports the back-references in the RE.
+
+module Main where
+
+import System (getArgs)
+import StreamEd (run)
+
+main :: IO ()
+main = do
+    args <- getArgs
+    if null args || head args == "--help" then do
+      putStrLn usage
+      return ()
+     else run args
+
+usage :: String
+usage = unlines help 
+  where help = ["usage: Hsed [-n] script [file...]",
+                "       Hsed [-n] -e script [-e script]... [-f script_file]... [file...]",
+                "       Hsed [-n] [-e script]... -f script_file [-f script_file]... [file...]"
+               ]
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2010 Vitaliy Rukavishnikov
+
+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.
+ 
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "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 HOLDERS 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/Parsec.hs b/Parsec.hs
new file mode 100644
--- /dev/null
+++ b/Parsec.hs
@@ -0,0 +1,235 @@
+-- |
+-- Module      :  Parsec
+-- Copyright   :  (c) Vitaliy Rkavishnikov
+-- License     :  BSD-style (see the file LICENSE)
+-- 
+-- Maintainer  :  virukav@gmail.com
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-- Sed commands parser. See "The Open Group Base Specifications Issue 7" for
+-- parsing requirements. The current version of the Haskell Sed doesn't supports
+-- the back-references in the RE.
+
+module Parsec where
+
+import Prelude hiding (readFile, writeFile)
+import Text.ParserCombinators.Parsec hiding (label)
+import qualified Data.ByteString.Char8 as B
+import Ast
+import SedRegex
+
+-- | If an RE is empty last RE used in the last command applied 
+data ParserState = ParserState {
+    lastRE :: Pattern
+} 
+
+emptyState = ParserState { 
+    lastRE = B.pack ""   
+}
+
+type SedParser = GenParser Char ParserState
+type Stream = String
+
+eol = oneOf "\n\r" >> return ()
+eoleof = choice [eol, eof]
+slash = char '/'
+comma = char ','
+semi = char ';'
+backslash = char '\\'
+number = many1 digit >>= \n -> return $ read n
+invert = (spaces >> char '!' >> return True) <|> return False
+
+parseSed :: SedParser a -> Stream -> Either ParseError a
+parseSed p = runParser p emptyState ""
+
+parseRE :: String -> SedParser Pattern
+parseRE pat = do
+    let patB = B.pack pat
+    updateState (\(ParserState _)  -> ParserState patB)
+    return patB
+
+pattern open close val = do
+    pat <- between open close val
+    if null pat then do
+        s <- getState
+        return $ lastRE s
+     else parseRE (unesc pat)
+
+addr = 
+    fmap (Just . LineNumber) number <|>
+    (char '$' >> return (Just LastLine)) <|>
+    (pattern slash slash val >>= \pat -> return $ Just (Pat pat))
+    where val = many (noneOf "/")
+
+addr1 = do
+    a1 <- addr
+    spaces
+    b <- invert
+    return $ Address a1 Nothing b
+
+addr2 = do
+    a1 <- addr
+    comma <?> ","
+    a2 <- addr <?> "bad address" 
+    spaces
+    b <- invert
+    return $ Address a1 a2 b
+
+address :: SedParser Address
+address = 
+    try addr2 <|> try addr1 <|> 
+    (invert >>= \b -> return $ Address Nothing Nothing b) 
+
+sedCmds :: SedParser [SedCmd]
+sedCmds = 
+    many1 $ try (space >> return emptyCmd) <|>
+            (do { x <- sedCmd; endCmd; return x })
+    where
+    endCmd = choice [eol, eof, semiend, comm, spaces >> return ()]
+       where
+       semiend = try (spaces >> semi >> spaces >> return ())
+       comm = lookAhead (char '#') >> return ()
+
+sedCmd :: SedParser SedCmd
+sedCmd = do
+    a <- address
+    fun <- sedFun
+    return $ SedCmd a fun
+
+sedFun :: SedParser SedFun
+sedFun = choice functions >>= \f -> return f
+
+functions = 
+    [substitute, group, append, change, insert, lineNum, delete, deletePat, replacePat, 
+     appendPat, replaceHold, appendHold, list, next, appendLinePat, printPat,
+     writeUpPat, quit, exchange, comment, branch, test, readFile, writeFile,
+     label, transform
+    ]
+
+append        = textFun 'a' Append
+change        = textFun 'c' Change 
+insert        = textFun 'i' Insert
+
+readFile      = fileFun 'r' ReadFile
+writeFile     = fileFun 'w' WriteFile
+label         = argFun ':' Label
+
+lineNum       = bareFun '=' LineNum
+delete        = bareFun 'd' Delete
+deletePat     = bareFun 'D' DeletePat
+replacePat    = bareFun 'g' ReplacePat
+appendPat     = bareFun 'G' AppendPat
+replaceHold   = bareFun 'h' ReplaceHold
+appendHold    = bareFun 'H' AppendHold
+list          = bareFun 'l' List
+next          = bareFun 'n' Next
+appendLinePat = bareFun 'N' AppendLinePat
+printPat      = bareFun 'p' PrintPat
+writeUpPat    = bareFun 'P' WriteUpPat
+quit          = bareFun 'q' Quit
+exchange      = bareFun 'x' Exchange
+
+branch        = gotoFun 'b' Branch
+test          = gotoFun 't' Test
+
+bareFun :: Char -> SedFun -> SedParser SedFun
+bareFun c f = char c >> return f
+
+textFun :: Char -> (Text -> SedFun) -> SedParser SedFun
+textFun c f = do
+    char c
+    backslash <?> "backslash"
+    eol <?> "end of line"
+    parts <- lines
+    return $ f (B.pack (init $ unlines parts))
+    where
+       lines = do {x <- line; try eoleof; return x}
+       line = sepBy part (backslash >> eol)
+       part = many (noneOf "\\\n")
+
+fileFun :: Char -> (FilePath -> SedFun) -> SedParser SedFun
+fileFun c f = 
+    char c >> spaces >> 
+    manyTill anyChar (lookAhead eoleof) >>= \l -> return $ f l
+
+argFun :: Char -> (B.ByteString -> SedFun) -> SedParser SedFun
+argFun c f = 
+    char c >> spaces >> 
+    manyTill anyChar (lookAhead eoleof) >>= \l -> return $ f (B.pack l)
+
+gotoFun :: Char -> (Maybe Label -> SedFun) -> SedParser SedFun
+gotoFun c f = do
+    char c
+    many $ choice[char ' ', char '\t']
+    label <- manyTill anyChar (lookAhead eoleof)
+    if null label then return $ f Nothing
+      else return $ f (Just $ B.pack label)
+
+group = do
+    char '{'
+    cmds <- sedCmds
+    spaces  
+    char '}' <?> "}"
+    return $ Group cmds
+
+comment = do
+    char '#'
+    manyTill anyChar (lookAhead eoleof)
+    return Comment
+
+transform = do
+    char 'y'
+    slash <?> "/"
+    str1 <- manyTill anyChar slash
+    str2 <- manyTill anyChar slash
+    return $ Transform (B.pack str1) (B.pack str2)
+
+substitute = do
+    char 's'
+    delim <- lookAhead anyChar
+    let val = many $ noneOf [delim]
+    pat <- pattern (char delim) (char delim) val
+    repl <- rhs delim
+    fs <- flags 
+    return $ Substitute (B.pack $ unesc (B.unpack pat)) (B.pack $ esc repl) fs
+    where
+      esc [] = []
+      esc [x] | x == '&' = "\\0"
+              | otherwise = [x]
+      esc (x:y:ys) | [x,y] == "\\n" = '\n':esc ys
+                   | [x,y] == "\\\n" = esc (y:ys)
+                   | [x,y] == "\\&" = '&':esc ys
+                   | x     == '&' = "\\0" ++ esc (y:ys)
+                   | otherwise = x:esc(y:ys)           
+
+      rhs delim = manyTill anyChar (char delim)
+
+flags = do
+    op <- occur
+    out <- outFile
+    return $ Flags op out
+    where
+      occur = occurPrint <|> return Nothing
+      outFile = 
+          (char 'w' >> spaces >> 
+          manyTill anyChar (lookAhead eoleof) >>= \f -> return $ Just f) <|>
+          return Nothing
+      occurPrint = 
+          occurrence >>= \o -> prn >>= \p ->
+          return $ Just $ OccurrencePrint o p
+      occurrence = 
+          (char 'g' >> return (Just ReplaceAll)) <|>
+          (number >>= \n -> return $ Just $ Replace n) <|>
+          return Nothing
+      prn = 
+          (char 'p' >> return True) <|>
+          return False
+
+unesc [] = []
+unesc [x] = [x]
+unesc (x:y:xs) | x:[y] == "\\t" = '\t':unesc xs
+               | x:[y] == "\\n" = '\n':unesc xs
+               | otherwise = x : unesc (y:xs)
+
+emptyCmd = SedCmd (Address Nothing Nothing False) EmptyCmd
diff --git a/SedRegex.hs b/SedRegex.hs
new file mode 100644
--- /dev/null
+++ b/SedRegex.hs
@@ -0,0 +1,74 @@
+-- |
+-- Module      :  SedRegex
+
+-- Maintainer  :  virukav@gmail.com
+-- Stability   :  experimental
+-- Portability :  non-portable-- |
+
+-- The Sed regular expression implementation based on the regex-posix package.   
+
+module SedRegex where
+
+import Data.Array ((!))
+import Text.Regex.Posix
+--import Text.Regex.TDFA
+import Text.Regex
+import qualified Data.ByteString.Char8 as B
+
+type Pattern = B.ByteString
+
+-- | Replaces every occurance of the given regexp with the replacement string. 
+--   Modification of the subRegex function from regex-posix package.
+sedSubRegex :: B.ByteString         -- ^ Search pattern
+            -> B.ByteString         -- ^ Input string
+            -> B.ByteString         -- ^ Replacement text
+            -> Int                  -- ^ Occurrence
+            -> (B.ByteString, Bool) -- ^ (Output string, Replacement occurs)
+--sedSubRegex _ "" _ _ = ("", True)
+sedSubRegex pat inp repl n =
+  let regexp = makeRegexOpts compExtended defaultExecOpt pat
+  --let regexp = mkRegex pat -- for TDFA
+      compile _i str [] = \ _m -> B.append str
+      compile i str ((xstr,(off,len)):rest) =
+        let i' = off+len
+            pre = B.take (off-i) str
+            str' = B.drop (i'-i) str
+            slashEsc = B.pack "\\"
+            app m = if xstr == slashEsc then B.append slashEsc
+                     else let x = read (B.unpack xstr) :: Int in 
+                            B.append (fst (m!x))
+        in if B.null str' then \ m -> B.append pre . app m
+             else \ m -> B.append pre . app m . compile i' str' rest m
+
+      compiled :: MatchText B.ByteString -> B.ByteString -> B.ByteString
+      compiled = compile 0 repl findrefs where
+        -- bre matches a backslash then capture either a backslash or some digits
+        bre = mkRegex "\\\\(\\\\|[0-9]+)"
+        findrefs = map (\m -> (fst (m!1),snd (m!0))) (matchAllText bre repl)
+      go _i str [] = str
+      go i str (m:ms) =
+        let (_,(off,len)) = m!0
+            i' = off+len
+            pre = B.take (off-i) str
+            str' = B.drop (i'-i) str
+        in if B.null str' then B.concat [pre, compiled m B.empty]
+             else B.concat [pre, compiled m (go i' str' ms)]
+   
+      occur regexp inp repl n pre =
+        if inp == B.empty then (B.empty, False)
+         else
+          case matchOnceText regexp inp of
+           Nothing -> (inp, False)
+           Just (p, m, r) -> 
+              if n > 1 then 
+                 let acc = B.concat [pre, p, fst (m!0)] in
+                     occur regexp r repl (n-1) acc
+               else
+                  (B.concat [pre, go 0 inp [m]], True)
+  in if n == 0 then 
+       let ms = matchAllText regexp inp in
+         (go 0 inp ms, (not . null) ms)
+      else occur regexp inp repl n B.empty
+
+-- | Match the regular expression against text
+matchRE pat str = str =~ pat :: Bool
diff --git a/SedState.hs b/SedState.hs
new file mode 100644
--- /dev/null
+++ b/SedState.hs
@@ -0,0 +1,67 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+-- |
+-- Module      :  SedState
+-- Copyright   :  (c) Vitaliy Rkavishnikov
+-- License     :  BSD-style (see the file LICENSE)
+-- 
+-- Maintainer  :  virukav@gmail.com
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-- The state of the program 
+
+module SedState where
+
+import qualified Control.Monad.State as S
+import qualified Data.Accessor.Basic as A
+import qualified Data.ByteString.Char8 as B
+import Data.Accessor.Template (deriveAccessors)
+import System.IO
+import Ast
+
+data Env = Env {
+  ast_ :: [SedCmd],                 -- ^ Parsed Sed commands
+  defOutput_ :: !Bool,              -- ^ Suppress the default output
+  lastLine_ :: !Int,                -- ^ The last line index
+  curLine_ :: !Int,                 -- ^ The current line index
+  inRange_ :: !Bool,                -- ^ Is pattern space matches the address range
+  patternSpace_ :: !B.ByteString,   -- ^ The buffer to keep the selected line(s)
+  holdSpace_ :: !B.ByteString,      -- ^ The buffer to keep the line(s) temporarily
+  appendSpace_ :: [B.ByteString],   -- ^ The buffer to keep the append lines
+  memorySpace_ :: !B.ByteString,    -- ^ Store the output in the memory
+  useMemSpace_ :: !Bool,            -- ^ If True the Sed output is stored in the memory buffer
+  exit_ :: !Bool,                   -- ^ Exit the stream editor
+  fileout_ :: [(FilePath, Handle)], -- ^ Write (w command) files handles 
+  subst_ :: !Bool                   -- ^ The result of the last substitution
+} deriving (Show)
+
+$( deriveAccessors ''Env )
+
+type SedState = S.StateT Env IO
+
+initEnv :: Env
+initEnv = Env {
+  ast_ = [],
+  defOutput_ = True,
+  lastLine_ = 0,
+  curLine_ = 0,
+  inRange_ = False,
+  patternSpace_ = B.empty,
+  holdSpace_ = B.empty,
+  appendSpace_ = [],
+  memorySpace_ = B.empty,
+  useMemSpace_ = False,
+  exit_  = False,
+  fileout_ = [],
+  subst_ = False
+}
+
+set :: A.T Env a -> a -> SedState ()
+set f x = S.modify (A.set f x)
+
+get :: A.T Env a -> SedState a
+get f = S.gets (A.get f)
+
+modify :: A.T Env a -> (a -> a) -> SedState ()
+modify f g = S.modify (A.modify f g)
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/StreamEd.hs b/StreamEd.hs
new file mode 100644
--- /dev/null
+++ b/StreamEd.hs
@@ -0,0 +1,410 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+
+-- |
+-- Module      :  StreamEd
+-- Copyright   :  (c) Vitaliy Rkavishnikov
+-- License     :  BSD-style (see the file LICENSE)
+-- 
+-- Maintainer  :  virukav@gmail.com
+-- Stability   :  experimental
+-- Portability :  non-portable
+--
+-- The Sed runtime engine. The execution sequence includes the parseArgs (parse the Sed options/args),
+-- compile (parse) Sed commands, execute Sed commands.
+
+module StreamEd where
+
+import System.IO 
+import System.FilePath (splitFileName)
+import Control.Monad (unless, when)
+import qualified Control.Monad.State as S
+import qualified Control.Exception as E
+import qualified System.FilePath.Glob as G (compile, globDir1) 
+import Data.List (isPrefixOf)
+import Data.Char (isPrint)
+import Data.Maybe (fromMaybe)
+import qualified Data.ByteString.Char8 as B
+import Text.Printf (printf)
+import Parsec (parseSed, sedCmds)
+import Ast
+import SedRegex
+import SedState
+
+data FileStatus = EOF | Cont 
+   deriving (Eq, Show)
+
+data CmdSchedule = None | NextCmd | Break | Continue | Jump (Maybe B.ByteString) | Exit
+   deriving (Eq, Show)
+
+-- | Run Sed program
+run :: [String] -> IO ()
+run args = 
+    S.execStateT (do 
+           (files, cmds) <- parseArgs args
+           compile cmds
+           execute files
+         ) initEnv >> return ()
+
+-- | Parse Sed program's arguments
+parseArgs :: [String] -> SedState ([FilePath], String)
+parseArgs xs = parseArgs' xs ([],[]) where
+    parseArgs' [] fs = return fs
+    parseArgs' [x] fs 
+       | x == "-n" = set defOutput False >> return fs
+       | otherwise = parseCmds x fs
+    parseArgs' (x:y:ys) fs
+       | x == "-e" = parseArgs' ys (addCmd fs y)
+       | x == "-f" = do
+            sed <- S.lift $ System.IO.readFile y `catch` openFileError y
+            when ("#n" `isPrefixOf` sed) $ set defOutput False 
+            parseArgs' ys (addCmd fs sed)
+       | x == "-n" = set defOutput False >> parseArgs' (y:ys) fs
+       | otherwise = parseCmds x fs >>= \fs' -> parseArgs' (y:ys) fs'
+       where
+         addCmd (files, cmds) x' = (files, cmds ++ ('\n':x'))
+
+openFileError :: String -> E.IOException -> IO [a]
+openFileError f e = putStr ("Error: Couldn't open " ++ f ++ ": " ++ 
+                    show (e :: E.IOException)) >> return []
+
+-- | Parse Sed program's embedded commands and the input files arguments
+parseCmds :: String -> ([FilePath], String) -> SedState ([FilePath], String)
+parseCmds x (files, cmds) = 
+    if null cmds then return (files, x) 
+     else do 
+       let (dir, fp) = splitFileName x
+       fs <- S.lift $ G.globDir1 (G.compile fp) dir
+       if null fs then error $ fp ++ ": No such file or directory"
+        else return (files ++ fs, cmds)
+
+-- | Parse the Sed commands
+compile :: String -> SedState ()
+compile cmds = do
+  case parseSed sedCmds cmds of
+     Right x -> set ast x
+     Left e  -> error $ show e ++ " in " ++ cmds
+  return ()
+
+-- | Execute the parsed Sed commands against input data
+execute :: [FilePath] -> SedState ()
+execute cont = 
+   if null cont then processStdin
+    else do 
+     let (files,len) = (cont, length cont)
+     let fs = zipWith (\x y -> (x, y == len)) files [1..len]
+     processFiles fs
+     fout <- get fileout
+     S.lift $ S.mapM_ hClose (map snd fout)
+     return ()
+
+-- | The standard input will be used if no file operands are specified
+processStdin :: SedState ()
+processStdin = do
+    e <- get exit
+    unless e $ do
+      a <- get ast
+      loop stdin True a
+
+-- | Process the input text files
+processFiles :: [(FilePath, Bool)] -> SedState ()
+processFiles fs = 
+    S.forM_ fs $ \(file, lastFile) -> do
+        e <- get exit
+        unless e $ do
+           h <- S.lift $ openFile file ReadMode
+           a <- get ast
+           loop h lastFile a
+
+-- | Cyclically append a line of input without newline into the pattern space
+loop :: Handle -> Bool -> [SedCmd] -> SedState ()
+loop h b cs = do
+      (res, str) <- line h b   
+      case res of
+        EOF -> S.lift $ hClose h >> return ()
+        _   -> do
+          set patternSpace str
+          set appendSpace []
+          sch <- eval h b cs
+          case sch of 
+            Exit -> return ()
+            _    -> loop h b cs
+
+-- | Read an input line and handle the EOF status
+line :: Handle -> Bool -> SedState (FileStatus, B.ByteString)
+line h b = do
+   p <- S.lift $ hIsEOF h
+   if p then return (EOF,B.empty)
+     else do 
+       str <- S.lift $ B.hGetLine h
+       modify curLine (+1)
+       isLast <- if h == stdin then return False 
+                   else S.lift (hIsEOF h) >>= \eof -> return eof
+
+       if isLast && b then                
+           get curLine >>= \l -> set lastLine l >> return (Cont, str)
+        else return (Cont, str)
+
+-- | Manage the flow control of the Sed commands
+eval :: Handle -> Bool -> [SedCmd] -> SedState CmdSchedule
+eval h b cs = do
+    sch <- execCmds cs (line h b)
+    case sch of
+       Jump lbl -> get ast >>= \as -> eval h b (goto as lbl)
+       Exit -> printPatSpace >> get appendSpace >>= \a -> 
+               mapM_ prnStr a >> set exit True >> return Exit
+       _ -> get appendSpace >>= \a -> mapM_ prnStr a >> return sch
+
+-- | Transfer control to the command marked with the label
+goto :: [SedCmd] -> Maybe Label -> [SedCmd]      
+goto cmds = maybe [] (go cmds)
+  where 
+        go [] _ = []
+        go (SedCmd _ fun:cs) str = case fun of
+           Group cs' -> go cs' str
+           Label x -> if x == str then cs
+                       else go cs str
+           _ -> go cs str   
+
+-- | Execute the specified Sed commands
+execCmds :: [SedCmd] -> SedState (FileStatus,B.ByteString) -> SedState CmdSchedule
+execCmds [] _ = printPatSpace >> return None
+execCmds (x:xs) line' = do
+    sch <- execCmd x line'
+    if sch `elem` [NextCmd, None] then execCmds xs line'
+     else if sch == Continue then do
+       cs <- get ast
+       execCmds cs line'
+     else return sch
+
+-- | Execute the Sed command if the address interval of the command is matched
+execCmd :: SedCmd -> SedState (FileStatus, B.ByteString) -> SedState CmdSchedule
+execCmd (SedCmd a fun) line' = do
+     b <- matchAddress a
+     if b then runCmd fun line' >>= \sch -> return sch
+      else return NextCmd
+
+-- | Check if the address interval is matched  
+matchAddress :: Address -> SedState Bool
+matchAddress (Address addr1 addr2 invert) = 
+    case (addr1,addr2) of
+      (Just x, Nothing) -> matchAddr x x >>= \b -> return $ b /= invert
+      (Just x, Just y)  -> matchAddr x y >>= \b -> return $ b /= invert
+      _                 -> return $ not invert
+    where
+      matchAddr :: Addr -> Addr -> SedState Bool
+      matchAddr a1 a2 = do 
+         lineNum <- get curLine
+         patSpace <- get patternSpace
+         lastLineNum <- get lastLine 
+         case (a1,a2) of
+           (LineNumber x, LineNumber y) -> matchRange (x == lineNum) (y == lineNum)
+           (LineNumber x, Pat y) -> matchRange (x == lineNum) (matchRE y patSpace)
+           (LineNumber x, LastLine) -> matchRange (x == lineNum) (lineNum == lastLineNum)
+           (LastLine, _) -> return $ lineNum == lastLineNum
+           (Pat x, Pat y) -> matchRange (matchRE x patSpace) (matchRE y patSpace)
+           (Pat x, LineNumber y) -> matchRange (matchRE x patSpace) (y == lineNum) 
+           (Pat x, LastLine) -> matchRange (matchRE x patSpace) (lineNum == lastLineNum)       
+      matchRange :: Bool -> Bool -> SedState Bool
+      matchRange b1 b2 = do
+         let setRange = set inRange
+         range <- get inRange           
+         if not range then 
+            if b1 && b2 then return True
+             else if b1 then setRange True >> return True
+                   else return False
+          else if b2 then setRange False >> return True
+                else return True
+
+-- | Execute the Sed function
+runCmd :: SedFun -> SedState (FileStatus,B.ByteString) -> SedState CmdSchedule
+runCmd cmd line' = 
+    case cmd of
+      Group cs -> group cs line'
+      LineNum -> get curLine >>= (prnStrLn . B.pack . show) >> return NextCmd
+      Append txt -> modify appendSpace (++ [txt]) >> return NextCmd
+      Branch lbl -> return (Jump lbl)
+      Change txt -> change txt
+      Delete -> set patternSpace B.empty >> return Break
+      DeletePat -> deletePat
+      ReplacePat -> get holdSpace >>= \h -> set patternSpace h >> return NextCmd
+      AppendPat -> get holdSpace >>= \h -> modify patternSpace (`B.append` B.cons '\n' h) 
+                   >> return NextCmd
+      ReplaceHold -> get patternSpace >>= \p -> set holdSpace p >> return NextCmd
+      AppendHold -> get patternSpace >>= \p -> modify holdSpace (`B.append` B.cons '\n' p) 
+                    >> return NextCmd
+      Insert txt -> prnStrLn txt >> return NextCmd
+      List -> list
+      Next -> next line'
+      AppendLinePat -> appendLinePat line'
+      PrintPat -> get patternSpace >>= \p -> prnStrLn p >> return NextCmd
+      WriteUpPat -> get patternSpace >>= (prnStrLn . B.takeWhile (/='\n')) >> return NextCmd
+      Quit -> return Exit
+      ReadFile file -> readF file
+      Substitute pat repl fs -> substitute pat repl fs
+      Test lbl -> get subst >>= \s -> if s then return $ Jump lbl else return NextCmd
+      WriteFile file -> writeF file
+      Exchange -> exchange
+      Transform t1 t2 -> transform t1 t2
+      Label _ -> return NextCmd
+      Comment -> return NextCmd
+      EmptyCmd -> return None
+
+-- | Execute 'group' Sed function 
+group :: [SedCmd] -> SedState (FileStatus, B.ByteString) -> SedState CmdSchedule  
+group [] _ = return NextCmd
+group (cmd:xs) line' = do
+    sch <- execCmd cmd line'
+    if sch `elem` [NextCmd, None] then
+       group xs line'
+     else return sch
+
+-- | Execute 'delete' from multiline pattern space Sed function
+deletePat :: SedState CmdSchedule
+deletePat = do
+    p <- get patternSpace
+    set patternSpace (B.drop 1 $ B.dropWhile (/='\n') p)
+    return Continue
+
+-- | Execute 'substitute' Sed function
+substitute :: B.ByteString -> B.ByteString -> Flags -> SedState CmdSchedule
+substitute pat repl fs = do
+  let (gn, p, w) = getFlags fs 
+  patSpace <- get patternSpace
+  let (repl', b) = sedSubRegex pat patSpace repl gn
+  set subst b
+  if b then do
+     set patternSpace repl'
+     _ <- if p then get patternSpace >>= \ps -> prnStrLn ps >> return NextCmd 
+           else return NextCmd
+     if (not.null) w then writeF w >> return NextCmd else return NextCmd
+   else return NextCmd
+   where
+     getFlags :: Flags -> (Int, Bool, FilePath)
+     getFlags (Flags o f) = (occurr, printPat, file) where
+        (occurr, printPat) = case o of
+           Nothing -> (1, False)
+           Just (OccurrencePrint x y) -> (occ x, y)
+           Just (PrintOccurrence x y) -> (occ y, x)
+        occ x = case x of
+           Nothing -> 1
+           Just ReplaceAll -> 0
+           Just (Replace n) -> n        
+        file = fromMaybe "" f
+
+-- | Execute 'change' Sed function
+change :: B.ByteString -> SedState CmdSchedule
+change txt = do
+    range <- get inRange
+    if not range then do
+       prnStrLn txt
+       return Break
+     else return Break
+
+-- | Execute 'next' Sed function
+next :: SedState (FileStatus, B.ByteString) -> SedState CmdSchedule
+next line' = do
+    printPatSpace
+    (res,str) <- line'
+    set patternSpace str
+    if res == EOF then return Break else return NextCmd
+
+-- | Execute 'list' Sed function
+list :: SedState CmdSchedule
+list = do
+    patSpace <- get patternSpace
+    S.forM_ (B.unpack patSpace) $ \ch ->
+      if isPrint ch then prnChar ch
+       else case lookup ch esc of
+             Nothing -> do 
+                 prnChar '\\'
+                 prnPrintf ch
+             Just x -> prnStr (B.pack x)
+    prnChar '\n'
+    return NextCmd
+    where esc = zip "\\\a\b\f\r\t\v"
+                    ["\\\\","\\a", "\\b", "\\f", "\\r", "\\t", "\\v"]
+
+-- | Execute 'exchange' Sed function
+exchange :: SedState CmdSchedule
+exchange = do
+    hold <- get holdSpace
+    pat  <- get patternSpace
+    set holdSpace pat
+    set patternSpace hold
+    return NextCmd
+
+-- | Execute 'appendLinePat' Sed function
+appendLinePat :: SedState (FileStatus, B.ByteString) -> SedState CmdSchedule
+appendLinePat line' = do
+    (res,ln) <- line'
+    if res == EOF then return Break
+      else do
+       let suffix = B.append (B.pack "\n") ln
+       modify patternSpace (`B.append` suffix)
+       return None
+
+-- | Execute 'transform' Sed function
+transform :: B.ByteString -> B.ByteString -> SedState CmdSchedule
+transform t1 t2 = do
+    when (B.length t1 /= B.length t2) $ 
+      error "Transform strings are not the same length"
+    patSpace <- get patternSpace
+    let tr = B.map go patSpace
+    set patternSpace tr 
+    return NextCmd
+    where go ch = fromMaybe ch (lookup ch (B.zip t1 t2))
+
+-- | Write contents of the pattern space to the file
+writeF :: FilePath -> SedState CmdSchedule
+writeF file = do
+    fout <- get fileout
+    patSpace <- get patternSpace
+    let printFileout h = S.lift $ B.hPutStrLn h patSpace
+    case lookup file fout of
+       Nothing -> do
+          h <- S.lift $ openFile file WriteMode
+          modify fileout (++ [(file,h)])
+          printFileout h
+       Just h -> printFileout h
+    return NextCmd
+
+-- | Add contents of the input file to the append space
+readF :: FilePath -> SedState CmdSchedule
+readF file = do
+    cont <- S.lift $ B.readFile file `catch` \_ -> return B.empty
+    modify appendSpace (++ [cont])
+    return NextCmd
+
+-- | Print the pattern space to the standard output
+printPatSpace :: SedState ()
+printPatSpace = do
+   out <- get defOutput
+   when out $ get patternSpace >>= \p -> prnStrLn p
+
+-- | Check if the current line in the pattern space is the last line
+isLastLine :: SedState Bool
+isLastLine = do
+    l <- get lastLine
+    cur <- get curLine
+    return $ l == cur
+
+-- | Writes the string to the standard output or save the string in the memory buffer
+prnStr :: B.ByteString -> SedState ()
+prnStr str = do
+   useMem <- get useMemSpace
+   if useMem then modify memorySpace (`B.append` str) 
+     else S.lift $ B.putStr str
+
+-- | The same as prnStr, but adds a newline character
+prnStrLn :: B.ByteString -> SedState ()
+prnStrLn str = prnStr $ B.snoc str '\n'
+
+-- | The same as prnStr, but for char
+prnChar :: Char -> SedState ()
+prnChar c = prnStr $ B.singleton c
+
+-- | Print the character as three-digit octal number
+prnPrintf :: Char -> SedState ()
+prnPrintf c = do
+    let str = printf "%03o" c :: String 
+    prnStr $ B.pack str
diff --git a/TestSuite.hs b/TestSuite.hs
new file mode 100644
--- /dev/null
+++ b/TestSuite.hs
@@ -0,0 +1,187 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+
+import System.Directory (getDirectoryContents)
+import System.FilePath (replaceExtension, takeExtension, (</>))
+import Control.Monad (forM_, when)
+import qualified Control.Exception as E
+import qualified Control.Monad.State as S
+import System.Console.CmdArgs
+import Data.List (isPrefixOf)
+import qualified Data.ByteString.Char8 as B
+import Parsec (parseSed, sedCmds)
+import StreamEd
+import SedState
+
+data Config = Config {
+  dir :: FilePath,
+  inp :: String,
+  ok :: String,
+  file :: FilePath
+} deriving (Show, Data, Typeable)
+
+config :: Mode (CmdArgs Config)
+config = cmdArgsMode $ Config
+   {dir = def &= typFile &= help "Directory with the test cases"
+   ,inp = def &= help "Input file extension"
+   ,ok  = def &= help "Output file extension"
+   ,file= def &= typFile &= help "Sed script to test"
+   } &=
+   program "TestSuite" &=
+   summary "TestSuite 0.1" &=
+   help "" &=
+   details []
+
+data Result = OK | Diff String | Error String 
+  deriving (Show, Eq, Typeable)
+
+main :: IO ()
+main = do
+  cnf <- cmdArgsRun config
+  let script = file cnf
+  testParse
+  if (not.null) script then
+    testFile script (inp cnf) (ok cnf)
+   else
+    testDir (dir cnf) (inp cnf) (ok cnf) 
+
+passed :: String -> String
+passed script = result script " is passed"
+
+failed :: String -> String
+failed script = result script " is failed"
+
+result :: String -> String -> String
+result script str = "Test " ++ script ++ str
+
+parseFile :: FilePath -> String -> SedState ([FilePath],String)
+parseFile script inpfile = do
+    sed <- S.lift $ readFile script `catch` openFileError script
+    when ("#n" `isPrefixOf` sed) $ set defOutput False
+    set useMemSpace True
+    return ([inpfile], sed)
+
+testParse :: IO ()
+testParse = do
+   putStrLn "<-- Sed parser tests started"
+   forM_ ptests $ \(str, etalon) -> 
+      case (parseSed sedCmds str) of
+        Left x -> putStrLn  (failed str ++ " -> ") >> print x
+        Right y -> if show y == etalon then putStrLn $ passed (show str)
+                    else do 
+                     putStrLn $ failed str
+                     print etalon
+                     print "====>"
+                     print y
+
+   putStrLn "--> Sed parser tests ended\n"
+   
+testFile :: FilePath -> String -> String -> IO ()
+testFile script inext okext = do
+    let inpfile = replaceExtension script inext
+    let okfile = replaceExtension script okext    
+    env <- S.execStateT (do
+                (files, cmds) <- parseFile script inpfile
+                compile cmds
+                execute files
+           ) initEnv
+    okf <- B.readFile okfile `catch` openFileErrorB okfile 
+    let res = memorySpace_ env
+    if res == okf then 
+      putStrLn $ passed script
+     else do
+      putStrLn $ failed script  
+      mapM_ print (B.lines okf)
+      print "===>"
+      mapM_ print (B.lines (memorySpace_ env))
+    where
+      openFileErrorB f e = putStr ("Error: Couldn't open " ++ f ++ 
+           ": " ++ show (e :: E.IOException)) >> return B.empty
+
+testDir :: FilePath -> String -> String -> IO ()
+testDir path inpext okext = do
+    putStrLn "<-- Sed script tests started"
+    names <- getDirectoryContents path
+    forM_ names $ \n -> do
+       let filePath = path </> n
+       when (takeExtension filePath == ".sed") $
+           testFile filePath inpext okext
+    putStrLn "--> Sed script tests ended"
+
+ptests :: [(String, String)]
+ptests = [
+ ("/123/=",
+  "[SedCmd (Address (Just (Pat \"123\")) Nothing False) LineNum]"),
+ ("/123/{\n=\n}",
+  "[SedCmd (Address (Just (Pat \"123\")) Nothing False) (Group [SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) LineNum])]"),
+ ("a\\\n123\\\n456",
+  "[SedCmd (Address Nothing Nothing False) (Append \"123\\n456\")]"),
+ ("a\\\n~",
+  "[SedCmd (Address Nothing Nothing False) (Append \"~\")]"),
+ ("/123/,/456/b", 
+  "[SedCmd (Address (Just (Pat \"123\")) (Just (Pat \"456\")) False) (Branch Nothing)]"),
+ ("y/aaa/bbb/",
+  "[SedCmd (Address Nothing Nothing False) (Transform \"aaa\" \"bbb\")]"),
+ ("s/aaa/bbb/g",
+  "[SedCmd (Address Nothing Nothing False) (Substitute \"aaa\" \"bbb\" (Flags (Just (OccurrencePrint (Just ReplaceAll) False)) Nothing))]"),
+ ("s'aaa'b&bb'1pw 123",
+  "[SedCmd (Address Nothing Nothing False) (Substitute \"aaa\" \"b\\\\0bb\" (Flags (Just (OccurrencePrint (Just (Replace 1)) True)) (Just \"123\")))]"),
+ ("s/aaa/aa\1bb/g",
+  "[SedCmd (Address Nothing Nothing False) (Substitute \"aaa\" \"aa\\SOHbb\" (Flags (Just (OccurrencePrint (Just ReplaceAll) False)) Nothing))]"),
+ ("s/aaa/\3/",
+  "[SedCmd (Address Nothing Nothing False) (Substitute \"aaa\" \"\\ETX\" (Flags (Just (OccurrencePrint Nothing False)) Nothing))]"),
+ ("r ReadFile.cmd",
+  "[SedCmd (Address Nothing Nothing False) (ReadFile \"ReadFile.cmd\")]"),
+ ("w WriteFile.cmd",
+  "[SedCmd (Address Nothing Nothing False) (WriteFile \"WriteFile.cmd\")]"),
+ ("{\n=\n=\n}",
+  "[SedCmd (Address Nothing Nothing False) (Group [SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) LineNum,SedCmd (Address Nothing Nothing False) LineNum])]"),
+ ("=\n=\n",
+  "[SedCmd (Address Nothing Nothing False) LineNum,SedCmd (Address Nothing Nothing False) LineNum]"),
+ (" #=",
+  "[SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) Comment]"),
+ ("#n Print line before and after changes.\n{\n=\n}",
+  "[SedCmd (Address Nothing Nothing False) Comment,SedCmd (Address Nothing Nothing False) (Group [SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) LineNum])]"),
+ ("r ReadFile.cmd\nr ReadFile.cmd",
+  "[SedCmd (Address Nothing Nothing False) (ReadFile \"ReadFile.cmd\"),SedCmd (Address Nothing Nothing False) (ReadFile \"ReadFile.cmd\")]"),
+ ("/^\\.H1/n\n/^$/d",
+  "[SedCmd (Address (Just (Pat \"^\\\\.H1\")) Nothing False) Next,SedCmd (Address (Just (Pat \"^$\")) Nothing False) Delete]"),
+ ("s/      /\\n/2",
+  "[SedCmd (Address Nothing Nothing False) (Substitute \"      \" \"\\n\" (Flags (Just (OccurrencePrint (Just (Replace 2)) False)) Nothing))]"),
+ ("s/Owner and Operator\\nGuide/aaa/g",
+  "[SedCmd (Address Nothing Nothing False) (Substitute \"Owner and Operator\\nGuide\" \"aaa\" (Flags (Just (OccurrencePrint (Just ReplaceAll) False)) Nothing))]"),
+ ("/Operator$/{\nN\ns/Owner and Operator\\nGuide/Installation Guide/\n}\n",
+  "[SedCmd (Address (Just (Pat \"Operator$\")) Nothing False) (Group [SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) AppendLinePat,SedCmd (Address Nothing Nothing False) (Substitute \"Owner and Operator\\nGuide\" \"Installation Guide\" (Flags (Just (OccurrencePrint Nothing False)) Nothing))])]"),
+ ("s/Owner and Operator Guide/Installation Guide/\n/Owner/{\nN\ns/ *\n/ /\ns/Owner andOperator Guide */Installation Guide\\n/\n}",
+  "[SedCmd (Address Nothing Nothing False) (Substitute \"Owner and Operator Guide\" \"Installation Guide\" (Flags (Just (OccurrencePrint Nothing False)) Nothing)),SedCmd (Address (Just (Pat \"Owner\")) Nothing False) (Group [SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) AppendLinePat,SedCmd (Address Nothing Nothing False) (Substitute \" *\\n\" \" \" (Flags (Just (OccurrencePrint Nothing False)) Nothing)),SedCmd (Address Nothing Nothing False) (Substitute \"Owner andOperator Guide *\" \"Installation Guide\\n\" (Flags (Just (OccurrencePrint Nothing False)) Nothing))])]"),
+ ("    /^\n$/D",
+  "[SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address (Just (Pat \"^\\n$\")) Nothing False) DeletePat]"),
+ ("{\n{\n}\n}",
+  "[SedCmd (Address Nothing Nothing False) (Group [SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) (Group [SedCmd (Address Nothing Nothing False) EmptyCmd])])]"),
+ ("{\n{\nN\n}\n}",
+  "[SedCmd (Address Nothing Nothing False) (Group [SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) (Group [SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) AppendLinePat])])]"),
+ ("/UNIX/{\n  N\n  /\nSystem/{\n  s// Operating &/\n  P\n  D\n  }\n}",
+  "[SedCmd (Address (Just (Pat \"UNIX\")) Nothing False) (Group [SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) AppendLinePat,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address (Just (Pat \"\\nSystem\")) Nothing False) (Group [SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) (Substitute \"\\nSystem\" \" Operating \\\\0\" (Flags (Just (OccurrencePrint Nothing False)) Nothing)),SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) WriteUpPat,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) DeletePat,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd])])]"),
+ ("=\n=\n  #\n",
+  "[SedCmd (Address Nothing Nothing False) LineNum,SedCmd (Address Nothing Nothing False) LineNum,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) Comment]"),
+ ("  \n =\n \n =\n",
+  "[SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) LineNum,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) LineNum]"),
+ ("  \n \n \n \n",
+  "[SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd]"),
+ ("{N\nN\n{=\n  }\n}",
+  "[SedCmd (Address Nothing Nothing False) (Group [SedCmd (Address Nothing Nothing False) AppendLinePat,SedCmd (Address Nothing Nothing False) AppendLinePat,SedCmd (Address Nothing Nothing False) (Group [SedCmd (Address Nothing Nothing False) LineNum,SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd])])]"),
+ ("{\n }",
+  "[SedCmd (Address Nothing Nothing False) (Group [SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) EmptyCmd])]"),
+ ("{\nN\n}\n#/Owner/{}",
+  "[SedCmd (Address Nothing Nothing False) (Group [SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) AppendLinePat]),SedCmd (Address Nothing Nothing False) Comment]"),
+ ("{\n}\nP",
+  "[SedCmd (Address Nothing Nothing False) (Group [SedCmd (Address Nothing Nothing False) EmptyCmd]),SedCmd (Address Nothing Nothing False) WriteUpPat]"),
+ ("N\nP",
+  "[SedCmd (Address Nothing Nothing False) AppendLinePat,SedCmd (Address Nothing Nothing False) WriteUpPat]"),
+ ("1!H;1!{ x; p; x \n};d",
+  "[SedCmd (Address (Just (LineNumber 1)) Nothing True) AppendHold,SedCmd (Address (Just (LineNumber 1)) Nothing True) (Group [SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) Exchange,SedCmd (Address Nothing Nothing False) PrintPat,SedCmd (Address Nothing Nothing False) Exchange]),SedCmd (Address Nothing Nothing False) Delete]"),
+ ("/^0$/{\nc\\\nyes\n}",
+  "[SedCmd (Address (Just (Pat \"^0$\")) Nothing False) (Group [SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) (Change \"yes\")])]"),
+ ("2,5 {\ns/[\t ]//g\n}",
+  "[SedCmd (Address (Just (LineNumber 2)) (Just (LineNumber 5)) False) (Group [SedCmd (Address Nothing Nothing False) EmptyCmd,SedCmd (Address Nothing Nothing False) (Substitute \"[\\t ]\" \"\" (Flags (Just (OccurrencePrint (Just ReplaceAll) False)) Nothing))])]")
+--}
+ ]
diff --git a/tests/Append.in b/tests/Append.in
new file mode 100644
--- /dev/null
+++ b/tests/Append.in
@@ -0,0 +1,2 @@
+789
+456
diff --git a/tests/Append.ok b/tests/Append.ok
new file mode 100644
--- /dev/null
+++ b/tests/Append.ok
@@ -0,0 +1,5 @@
+789
+~
+123456
+~
+123
diff --git a/tests/Append.sed b/tests/Append.sed
new file mode 100644
--- /dev/null
+++ b/tests/Append.sed
@@ -0,0 +1,3 @@
+a\
+~\
+123
diff --git a/tests/Branch.in b/tests/Branch.in
new file mode 100644
--- /dev/null
+++ b/tests/Branch.in
@@ -0,0 +1,11 @@
+Linux
+   Administration
+   Scripting
+     Tips and Tricks
+Windows
+   Administration
+Database
+   Administration of Oracle
+   Administration of Mysql
+
+
diff --git a/tests/Branch.ok b/tests/Branch.ok
new file mode 100644
--- /dev/null
+++ b/tests/Branch.ok
@@ -0,0 +1,11 @@
+Linux
+   Supervision
+   Scripting
+     Tips and Tricks
+Windows
+   Administration
+Database
+   Administration of Oracle
+   Administration of Mysql
+
+
diff --git a/tests/Branch.sed b/tests/Branch.sed
new file mode 100644
--- /dev/null
+++ b/tests/Branch.sed
@@ -0,0 +1,7 @@
+# Replace the first occurence of a pattern in a whole file
+/Administration/{
+s/Administration/Supervision/
+:loop
+n
+b loop
+}
diff --git a/tests/BranchTag.in b/tests/BranchTag.in
new file mode 100644
--- /dev/null
+++ b/tests/BranchTag.in
@@ -0,0 +1,8 @@
+<html><body>
+<table
+border=2><tr><td valign=top
+align=right>1.</td>
+<td>Line 1 Column 2</
+td>
+</table>
+</body></html>
diff --git a/tests/BranchTag.ok b/tests/BranchTag.ok
new file mode 100644
--- /dev/null
+++ b/tests/BranchTag.ok
@@ -0,0 +1,5 @@
+
+1.
+Line 1 Column 2
+
+
diff --git a/tests/BranchTag.sed b/tests/BranchTag.sed
new file mode 100644
--- /dev/null
+++ b/tests/BranchTag.sed
@@ -0,0 +1,9 @@
+# Remove the HTML tags
+/</{
+:loop
+s/<[^<]*>//g
+/</{
+N
+b loop
+}
+}
diff --git a/tests/Change.in b/tests/Change.in
new file mode 100644
--- /dev/null
+++ b/tests/Change.in
@@ -0,0 +1,2 @@
+Text to be changed 1
+Text to be changed 2
diff --git a/tests/Change.ok b/tests/Change.ok
new file mode 100644
--- /dev/null
+++ b/tests/Change.ok
@@ -0,0 +1,2 @@
+Changed text
+Changed text
diff --git a/tests/Change.sed b/tests/Change.sed
new file mode 100644
--- /dev/null
+++ b/tests/Change.sed
@@ -0,0 +1,2 @@
+c\
+Changed text
diff --git a/tests/ChangeAddr.in b/tests/ChangeAddr.in
new file mode 100644
--- /dev/null
+++ b/tests/ChangeAddr.in
@@ -0,0 +1,11 @@
+Return-Path: [fake@address.com]
+Received: from server.mymailhost.com (mail.mymailhost.com [126.43.75.123])
+        by pilot01.cl.msu.edu (8.10.2/8.10.2) with ESMTP id NAA23597;
+        Fri, 12 Jul 2002 16:11:20 -0400 (EDT)
+Received: from aol.com (127-34-56-98.dsl.mybigisp.com [127.34.56.98])
+        by server.mymailhost.com; Fri, 12 Jul 2002 13:09:38 -0700 (PDT)
+Date: Fri, 12 Jul 2002 13:09:38 -0700 (PDT)
+From: Hot Summer Deals <hot_deals@aol.com>
+To: My.Friends@pilot.msu.edu
+Subject: Just what you've been waiting for!!
+
diff --git a/tests/ChangeAddr.ok b/tests/ChangeAddr.ok
new file mode 100644
--- /dev/null
+++ b/tests/ChangeAddr.ok
@@ -0,0 +1,8 @@
+Return-Path: [fake@address.com]
+Received: from server.mymailhost.com (mail.mymailhost.com [126.43.75.123])
+        by pilot01.cl.msu.edu (8.10.2/8.10.2) with ESMTP id NAA23597;
+        Fri, 12 Jul 2002 16:11:20 -0400 (EDT)
+Received: from aol.com (127-34-56-98.dsl.mybigisp.com [127.34.56.98])
+        by server.mymailhost.com; Fri, 12 Jul 2002 13:09:38 -0700 (PDT)
+Date: Fri, 12 Jul 2002 13:09:38 -0700 (PDT)
+<Mail Header Removed>
diff --git a/tests/ChangeAddr.sed b/tests/ChangeAddr.sed
new file mode 100644
--- /dev/null
+++ b/tests/ChangeAddr.sed
@@ -0,0 +1,2 @@
+/^From:/,/^$/c\
+<Mail Header Removed>
diff --git a/tests/Comment.in b/tests/Comment.in
new file mode 100644
--- /dev/null
+++ b/tests/Comment.in
@@ -0,0 +1,3 @@
+text A
+text B
+text C
diff --git a/tests/Comment.ok b/tests/Comment.ok
new file mode 100644
--- /dev/null
+++ b/tests/Comment.ok
@@ -0,0 +1,3 @@
+text A
+text B
+text C
diff --git a/tests/Comment.sed b/tests/Comment.sed
new file mode 100644
--- /dev/null
+++ b/tests/Comment.sed
@@ -0,0 +1,1 @@
+# =
diff --git a/tests/Delete.in b/tests/Delete.in
new file mode 100644
--- /dev/null
+++ b/tests/Delete.in
@@ -0,0 +1,5 @@
+Column 1
+
+Column 2
+
+Column 3
diff --git a/tests/Delete.ok b/tests/Delete.ok
new file mode 100644
--- /dev/null
+++ b/tests/Delete.ok
@@ -0,0 +1,3 @@
+Column 1
+Column 2
+Column 3
diff --git a/tests/Delete.sed b/tests/Delete.sed
new file mode 100644
--- /dev/null
+++ b/tests/Delete.sed
@@ -0,0 +1,1 @@
+/^$/d
diff --git a/tests/DeleteMultiline.in b/tests/DeleteMultiline.in
new file mode 100644
--- /dev/null
+++ b/tests/DeleteMultiline.in
@@ -0,0 +1,15 @@
+This line is followed by 1 blank line.
+
+This line is followed by 2 blank lines.
+
+
+This line is followed by 3 blank lines.
+
+
+
+This line is followed by 4 blank lines.
+
+
+
+
+This is the end.
diff --git a/tests/DeleteMultiline.ok b/tests/DeleteMultiline.ok
new file mode 100644
--- /dev/null
+++ b/tests/DeleteMultiline.ok
@@ -0,0 +1,9 @@
+This line is followed by 1 blank line.
+
+This line is followed by 2 blank lines.
+
+This line is followed by 3 blank lines.
+
+This line is followed by 4 blank lines.
+
+This is the end.
diff --git a/tests/DeleteMultiline.sed b/tests/DeleteMultiline.sed
new file mode 100644
--- /dev/null
+++ b/tests/DeleteMultiline.sed
@@ -0,0 +1,5 @@
+# reduce multiple blank lines to one; version using d command
+/^$/{
+    N
+    /^\n$/D
+}
diff --git a/tests/Empty.in b/tests/Empty.in
new file mode 100644
--- /dev/null
+++ b/tests/Empty.in
@@ -0,0 +1,2 @@
+line 1
+line 2
diff --git a/tests/Empty.ok b/tests/Empty.ok
new file mode 100644
--- /dev/null
+++ b/tests/Empty.ok
@@ -0,0 +1,2 @@
+line 1
+line 2
diff --git a/tests/Empty.sed b/tests/Empty.sed
new file mode 100644
--- /dev/null
+++ b/tests/Empty.sed
@@ -0,0 +1,3 @@
+
+ 
+
diff --git a/tests/Hold.in b/tests/Hold.in
new file mode 100644
--- /dev/null
+++ b/tests/Hold.in
@@ -0,0 +1,7 @@
+1
+2
+11
+22
+111
+222
+
diff --git a/tests/Hold.ok b/tests/Hold.ok
new file mode 100644
--- /dev/null
+++ b/tests/Hold.ok
@@ -0,0 +1,7 @@
+2
+1
+22
+11
+222
+111
+
diff --git a/tests/Hold.sed b/tests/Hold.sed
new file mode 100644
--- /dev/null
+++ b/tests/Hold.sed
@@ -0,0 +1,8 @@
+# Reverse flip
+/1/{
+h
+d
+}
+/2/{
+G
+}
diff --git a/tests/HoldTransform.in b/tests/HoldTransform.in
new file mode 100644
--- /dev/null
+++ b/tests/HoldTransform.in
@@ -0,0 +1,3 @@
+find the Match statement
+Consult the Get statement.
+using the Read statement to retrieve data
diff --git a/tests/HoldTransform.ok b/tests/HoldTransform.ok
new file mode 100644
--- /dev/null
+++ b/tests/HoldTransform.ok
@@ -0,0 +1,3 @@
+find the MATCH statement
+Consult the GET statement.
+using the READ statement to retrieve data
diff --git a/tests/HoldTransform.sed b/tests/HoldTransform.sed
new file mode 100644
--- /dev/null
+++ b/tests/HoldTransform.sed
@@ -0,0 +1,16 @@
+# capitalize statement names
+#/the .* statement/{
+#h
+#s/.*the \(.*\) statement.*/\1/
+#y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
+#G
+#s/\(.*\)\n\(.*the \).*\( statement.*\)/\2\1\3/
+#}
+
+/the .* statement/{
+h
+s/.*the (.*) statement.*/\1/
+y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
+G
+s/(.*)\n(.*the ).*( statement.*)/\2\1\3/
+}
diff --git a/tests/Insert.in b/tests/Insert.in
new file mode 100644
--- /dev/null
+++ b/tests/Insert.in
@@ -0,0 +1,3 @@
+<John's Address>
+----------------
+<Larry's Address>
diff --git a/tests/Insert.ok b/tests/Insert.ok
new file mode 100644
--- /dev/null
+++ b/tests/Insert.ok
@@ -0,0 +1,5 @@
+<John's Address>
+----------------
+4700 Cross Court
+French Lick, IN
+<Larry's Address>
diff --git a/tests/Insert.sed b/tests/Insert.sed
new file mode 100644
--- /dev/null
+++ b/tests/Insert.sed
@@ -0,0 +1,3 @@
+/<Larry's Address>/i\
+4700 Cross Court\
+French Lick, IN
diff --git a/tests/LineNum.in b/tests/LineNum.in
new file mode 100644
--- /dev/null
+++ b/tests/LineNum.in
@@ -0,0 +1,3 @@
+text A
+text B
+text C
diff --git a/tests/LineNum.ok b/tests/LineNum.ok
new file mode 100644
--- /dev/null
+++ b/tests/LineNum.ok
@@ -0,0 +1,6 @@
+1
+text A
+2
+text B
+3
+text C
diff --git a/tests/LineNum.sed b/tests/LineNum.sed
new file mode 100644
--- /dev/null
+++ b/tests/LineNum.sed
@@ -0,0 +1,1 @@
+=
diff --git a/tests/LineNumAddr.in b/tests/LineNumAddr.in
new file mode 100644
--- /dev/null
+++ b/tests/LineNumAddr.in
@@ -0,0 +1,3 @@
+text A
+text B
+text C
diff --git a/tests/LineNumAddr.ok b/tests/LineNumAddr.ok
new file mode 100644
--- /dev/null
+++ b/tests/LineNumAddr.ok
@@ -0,0 +1,4 @@
+text A
+text B
+3
+text C
diff --git a/tests/LineNumAddr.sed b/tests/LineNumAddr.sed
new file mode 100644
--- /dev/null
+++ b/tests/LineNumAddr.sed
@@ -0,0 +1,1 @@
+3=
diff --git a/tests/LineNumInvert.in b/tests/LineNumInvert.in
new file mode 100644
--- /dev/null
+++ b/tests/LineNumInvert.in
@@ -0,0 +1,3 @@
+text A
+text B
+text C
diff --git a/tests/LineNumInvert.ok b/tests/LineNumInvert.ok
new file mode 100644
--- /dev/null
+++ b/tests/LineNumInvert.ok
@@ -0,0 +1,5 @@
+1
+text A
+2
+text B
+text C
diff --git a/tests/LineNumInvert.sed b/tests/LineNumInvert.sed
new file mode 100644
--- /dev/null
+++ b/tests/LineNumInvert.sed
@@ -0,0 +1,1 @@
+3!=
diff --git a/tests/LineNumMany.in b/tests/LineNumMany.in
new file mode 100644
--- /dev/null
+++ b/tests/LineNumMany.in
@@ -0,0 +1,3 @@
+text A
+text B
+text C
diff --git a/tests/LineNumMany.ok b/tests/LineNumMany.ok
new file mode 100644
--- /dev/null
+++ b/tests/LineNumMany.ok
@@ -0,0 +1,9 @@
+1
+1
+text A
+2
+2
+text B
+3
+3
+text C
diff --git a/tests/LineNumMany.sed b/tests/LineNumMany.sed
new file mode 100644
--- /dev/null
+++ b/tests/LineNumMany.sed
@@ -0,0 +1,2 @@
+=
+=
diff --git a/tests/LineNumPrint.in b/tests/LineNumPrint.in
new file mode 100644
--- /dev/null
+++ b/tests/LineNumPrint.in
@@ -0,0 +1,502 @@
+/*
+ * Copyright (c) 1983, 1993
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)random.c    8.2 (Berkeley) 5/19/95";
+#endif /* LIBC_SCCS and not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "namespace.h"
+#include <sys/time.h>          /* for srandomdev() */
+#include <fcntl.h>             /* for srandomdev() */
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>            /* for srandomdev() */
+#include "un-namespace.h"
+
+/*
+ * random.c:
+ *
+ * An improved random number generation package.  In addition to the standard
+ * rand()/srand() like interface, this package also has a special state info
+ * interface.  The initstate() routine is called with a seed, an array of
+ * bytes, and a count of how many bytes are being passed in; this array is
+ * then initialized to contain information for random number generation with
+ * that much state information.  Good sizes for the amount of state
+ * information are 32, 64, 128, and 256 bytes.  The state can be switched by
+ * calling the setstate() routine with the same array as was initiallized
+ * with initstate().  By default, the package runs with 128 bytes of state
+ * information and generates far better random numbers than a linear
+ * congruential generator.  If the amount of state information is less than
+ * 32 bytes, a simple linear congruential R.N.G. is used.
+ *
+ * Internally, the state information is treated as an array of uint32_t's; the
+ * zeroeth element of the array is the type of R.N.G. being used (small
+ * integer); the remainder of the array is the state information for the
+ * R.N.G.  Thus, 32 bytes of state information will give 7 ints worth of
+ * state information, which will allow a degree seven polynomial.  (Note:
+ * the zeroeth word of state information also has some other information
+ * stored in it -- see setstate() for details).
+ *
+ * The random number generation technique is a linear feedback shift register
+ * approach, employing trinomials (since there are fewer terms to sum up that
+ * way).  In this approach, the least significant bit of all the numbers in
+ * the state table will act as a linear feedback shift register, and will
+ * have period 2^deg - 1 (where deg is the degree of the polynomial being
+ * used, assuming that the polynomial is irreducible and primitive).  The
+ * higher order bits will have longer periods, since their values are also
+ * influenced by pseudo-random carries out of the lower bits.  The total
+ * period of the generator is approximately deg*(2**deg - 1); thus doubling
+ * the amount of state information has a vast influence on the period of the
+ * generator.  Note: the deg*(2**deg - 1) is an approximation only good for
+ * large deg, when the period of the shift is the dominant factor.
+ * With deg equal to seven, the period is actually much longer than the
+ * 7*(2**7 - 1) predicted by this formula.
+ *
+ * Modified 28 December 1994 by Jacob S. Rosenberg.
+ * The following changes have been made:
+ * All references to the type u_int have been changed to unsigned long.
+ * All references to type int have been changed to type long.  Other
+ * cleanups have been made as well.  A warning for both initstate and
+ * setstate has been inserted to the effect that on Sparc platforms
+ * the 'arg_state' variable must be forced to begin on word boundaries.
+ * This can be easily done by casting a long integer array to char *.
+ * The overall logic has been left STRICTLY alone.  This software was
+ * tested on both a VAX and Sun SpacsStation with exactly the same
+ * results.  The new version and the original give IDENTICAL results.
+ * The new version is somewhat faster than the original.  As the
+ * documentation says:  "By default, the package runs with 128 bytes of
+ * state information and generates far better random numbers than a linear
+ * congruential generator.  If the amount of state information is less than
+ * 32 bytes, a simple linear congruential R.N.G. is used."  For a buffer of
+ * 128 bytes, this new version runs about 19 percent faster and for a 16
+ * byte buffer it is about 5 percent faster.
+ */
+
+/*
+ * For each of the currently supported random number generators, we have a
+ * break value on the amount of state information (you need at least this
+ * many bytes of state info to support this random number generator), a degree
+ * for the polynomial (actually a trinomial) that the R.N.G. is based on, and
+ * the separation between the two lower order coefficients of the trinomial.
+ */
+#define TYPE_0          0               /* linear congruential */
+#define BREAK_0         8
+#define DEG_0           0
+#define SEP_0           0
+
+#define TYPE_1          1               /* x**7 + x**3 + 1 */
+#define BREAK_1         32
+#define DEG_1           7
+#define SEP_1           3
+
+#define TYPE_2          2               /* x**15 + x + 1 */
+#define BREAK_2         64
+#define DEG_2           15
+#define SEP_2           1
+
+#define TYPE_3          3               /* x**31 + x**3 + 1 */
+#define BREAK_3         128
+#define DEG_3           31
+#define SEP_3           3
+
+#define TYPE_4          4               /* x**63 + x + 1 */
+#define BREAK_4         256
+#define DEG_4           63
+#define SEP_4           1
+
+/*
+ * Array versions of the above information to make code run faster --
+ * relies on fact that TYPE_i == i.
+ */
+#define MAX_TYPES       5               /* max number of types above */
+
+#ifdef  USE_WEAK_SEEDING
+#define NSHUFF 0
+#else   /* !USE_WEAK_SEEDING */
+#define NSHUFF 50       /* to drop some "seed -> 1st value" linearity */
+#endif  /* !USE_WEAK_SEEDING */
+
+static const int degrees[MAX_TYPES] =   { DEG_0, DEG_1, DEG_2, DEG_3, DEG_4 };
+static const int seps [MAX_TYPES] =     { SEP_0, SEP_1, SEP_2, SEP_3, SEP_4 };
+
+/*
+ * Initially, everything is set up as if from:
+ *
+ *      initstate(1, randtbl, 128);
+ *
+ * Note that this initialization takes advantage of the fact that srandom()
+ * advances the front and rear pointers 10*rand_deg times, and hence the
+ * rear pointer which starts at 0 will also end up at zero; thus the zeroeth
+ * element of the state information, which contains info about the current
+ * position of the rear pointer is just
+ *
+ *      MAX_TYPES * (rptr - state) + TYPE_3 == TYPE_3.
+ */
+
+static uint32_t randtbl[DEG_3 + 1] = {
+        TYPE_3,
+#ifdef  USE_WEAK_SEEDING
+/* Historic implementation compatibility */
+/* The random sequences do not vary much with the seed */
+        0x9a319039, 0x32d9c024, 0x9b663182, 0x5da1f342, 0xde3b81e0, 0xdf0a6fb5,
+        0xf103bc02, 0x48f340fb, 0x7449e56b, 0xbeb1dbb0, 0xab5c5918, 0x946554fd,
+        0x8c2e680f, 0xeb3d799f, 0xb11ee0b7, 0x2d436b86, 0xda672e2a, 0x1588ca88,
+        0xe369735d, 0x904f35f7, 0xd7158fd6, 0x6fa6f051, 0x616e6b96, 0xac94efdc,
+        0x36413f93, 0xc622c298, 0xf5a42ab8, 0x8a88d77b, 0xf5ad9d0e, 0x8999220b,
+        0x27fb47b9,
+#else   /* !USE_WEAK_SEEDING */
+        0x991539b1, 0x16a5bce3, 0x6774a4cd, 0x3e01511e, 0x4e508aaa, 0x61048c05,
+        0xf5500617, 0x846b7115, 0x6a19892c, 0x896a97af, 0xdb48f936, 0x14898454,
+        0x37ffd106, 0xb58bff9c, 0x59e17104, 0xcf918a49, 0x09378c83, 0x52c7a471,
+        0x8d293ea9, 0x1f4fc301, 0xc3db71be, 0x39b44e1c, 0xf8a44ef9, 0x4c8b80b1,
+        0x19edc328, 0x87bf4bdd, 0xc9b240e5, 0xe9ee4b1b, 0x4382aee7, 0x535b6b41,
+        0xf3bec5da
+#endif  /* !USE_WEAK_SEEDING */
+};
+
+/*
+ * fptr and rptr are two pointers into the state info, a front and a rear
+ * pointer.  These two pointers are always rand_sep places aparts, as they
+ * cycle cyclically through the state information.  (Yes, this does mean we
+ * could get away with just one pointer, but the code for random() is more
+ * efficient this way).  The pointers are left positioned as they would be
+ * from the call
+ *
+ *      initstate(1, randtbl, 128);
+ *
+ * (The position of the rear pointer, rptr, is really 0 (as explained above
+ * in the initialization of randtbl) because the state table pointer is set
+ * to point to randtbl[1] (as explained below).
+ */
+static uint32_t *fptr = &randtbl[SEP_3 + 1];
+static uint32_t *rptr = &randtbl[1];
+
+/*
+ * The following things are the pointer to the state information table, the
+ * type of the current generator, the degree of the current polynomial being
+ * used, and the separation between the two pointers.  Note that for efficiency
+ * of random(), we remember the first location of the state information, not
+ * the zeroeth.  Hence it is valid to access state[-1], which is used to
+ * store the type of the R.N.G.  Also, we remember the last location, since
+ * this is more efficient than indexing every time to find the address of
+ * the last element to see if the front and rear pointers have wrapped.
+ */
+static uint32_t *state = &randtbl[1];
+static int rand_type = TYPE_3;
+static int rand_deg = DEG_3;
+static int rand_sep = SEP_3;
+static uint32_t *end_ptr = &randtbl[DEG_3 + 1];
+
+static inline uint32_t good_rand(int32_t);
+
+static inline uint32_t good_rand (x)
+        int32_t x;
+{
+#ifdef  USE_WEAK_SEEDING
+/*
+ * Historic implementation compatibility.
+ * The random sequences do not vary much with the seed,
+ * even with overflowing.
+ */
+        return (1103515245 * x + 12345);
+#else   /* !USE_WEAK_SEEDING */
+/*
+ * Compute x = (7^5 * x) mod (2^31 - 1)
+ * wihout overflowing 31 bits:
+ *      (2^31 - 1) = 127773 * (7^5) + 2836
+ * From "Random number generators: good ones are hard to find",
+ * Park and Miller, Communications of the ACM, vol. 31, no. 10,
+ * October 1988, p. 1195.
+ */
+        int32_t hi, lo;
+
+        /* Can't be initialized with 0, so use another value. */
+        if (x == 0)
+                x = 123459876;
+        hi = x / 127773;
+        lo = x % 127773;
+        x = 16807 * lo - 2836 * hi;
+        if (x < 0)
+                x += 0x7fffffff;
+        return (x);
+#endif  /* !USE_WEAK_SEEDING */
+}
+
+/*
+ * srandom:
+ *
+ * Initialize the random number generator based on the given seed.  If the
+ * type is the trivial no-state-information type, just remember the seed.
+ * Otherwise, initializes state[] based on the given "seed" via a linear
+ * congruential generator.  Then, the pointers are set to known locations
+ * that are exactly rand_sep places apart.  Lastly, it cycles the state
+ * information a given number of times to get rid of any initial dependencies
+ * introduced by the L.C.R.N.G.  Note that the initialization of randtbl[]
+ * for default usage relies on values produced by this routine.
+ */
+void
+srandom(x)
+        unsigned long x;
+{
+        int i, lim;
+
+        state[0] = (uint32_t)x;
+        if (rand_type == TYPE_0)
+                lim = NSHUFF;
+        else {
+                for (i = 1; i < rand_deg; i++)
+                        state[i] = good_rand(state[i - 1]);
+                fptr = &state[rand_sep];
+                rptr = &state[0];
+                lim = 10 * rand_deg;
+        }
+        for (i = 0; i < lim; i++)
+                (void)random();
+}
+
+/*
+ * srandomdev:
+ *
+ * Many programs choose the seed value in a totally predictable manner.
+ * This often causes problems.  We seed the generator using the much more
+ * secure random(4) interface.  Note that this particular seeding
+ * procedure can generate states which are impossible to reproduce by
+ * calling srandom() with any value, since the succeeding terms in the
+ * state buffer are no longer derived from the LC algorithm applied to
+ * a fixed seed.
+ */
+void
+srandomdev()
+{
+        int fd, done;
+        size_t len;
+
+        if (rand_type == TYPE_0)
+                len = sizeof state[0];
+        else
+                len = rand_deg * sizeof state[0];
+
+        done = 0;
+        fd = _open("/dev/random", O_RDONLY, 0);
+        if (fd >= 0) {
+                if (_read(fd, (void *) state, len) == (ssize_t) len)
+                        done = 1;
+                _close(fd);
+        }
+
+        if (!done) {
+                struct timeval tv;
+                unsigned long junk;
+
+                gettimeofday(&tv, NULL);
+                srandom((getpid() << 16) ^ tv.tv_sec ^ tv.tv_usec ^ junk);
+                return;
+        }
+
+        if (rand_type != TYPE_0) {
+                fptr = &state[rand_sep];
+                rptr = &state[0];
+        }
+}
+
+/*
+ * initstate:
+ *
+ * Initialize the state information in the given array of n bytes for future
+ * random number generation.  Based on the number of bytes we are given, and
+ * the break values for the different R.N.G.'s, we choose the best (largest)
+ * one we can and set things up for it.  srandom() is then called to
+ * initialize the state information.
+ *
+ * Note that on return from srandom(), we set state[-1] to be the type
+ * multiplexed with the current value of the rear pointer; this is so
+ * successive calls to initstate() won't lose this information and will be
+ * able to restart with setstate().
+ *
+ * Note: the first thing we do is save the current state, if any, just like
+ * setstate() so that it doesn't matter when initstate is called.
+ *
+ * Returns a pointer to the old state.
+ *
+ * Note: The Sparc platform requires that arg_state begin on an int
+ * word boundary; otherwise a bus error will occur. Even so, lint will
+ * complain about mis-alignment, but you should disregard these messages.
+ */
+char *
+initstate(seed, arg_state, n)
+        unsigned long seed;             /* seed for R.N.G. */
+        char *arg_state;                /* pointer to state array */
+        long n;                         /* # bytes of state info */
+{
+        char *ostate = (char *)(&state[-1]);
+        uint32_t *int_arg_state = (uint32_t *)arg_state;
+
+        if (rand_type == TYPE_0)
+                state[-1] = rand_type;
+        else
+                state[-1] = MAX_TYPES * (rptr - state) + rand_type;
+        if (n < BREAK_0) {
+                (void)fprintf(stderr,
+                    "random: not enough state (%ld bytes); ignored.\n", n);
+                return(0);
+        }
+        if (n < BREAK_1) {
+                rand_type = TYPE_0;
+                rand_deg = DEG_0;
+                rand_sep = SEP_0;
+        } else if (n < BREAK_2) {
+                rand_type = TYPE_1;
+                rand_deg = DEG_1;
+                rand_sep = SEP_1;
+        } else if (n < BREAK_3) {
+                rand_type = TYPE_2;
+                rand_deg = DEG_2;
+                rand_sep = SEP_2;
+        } else if (n < BREAK_4) {
+                rand_type = TYPE_3;
+                rand_deg = DEG_3;
+                rand_sep = SEP_3;
+        } else {
+                rand_type = TYPE_4;
+                rand_deg = DEG_4;
+                rand_sep = SEP_4;
+        }
+        state = int_arg_state + 1; /* first location */
+        end_ptr = &state[rand_deg];     /* must set end_ptr before srandom */
+        srandom(seed);
+        if (rand_type == TYPE_0)
+                int_arg_state[0] = rand_type;
+        else
+                int_arg_state[0] = MAX_TYPES * (rptr - state) + rand_type;
+        return(ostate);
+}
+
+/*
+ * setstate:
+ *
+ * Restore the state from the given state array.
+ *
+ * Note: it is important that we also remember the locations of the pointers
+ * in the current state information, and restore the locations of the pointers
+ * from the old state information.  This is done by multiplexing the pointer
+ * location into the zeroeth word of the state information.
+ *
+ * Note that due to the order in which things are done, it is OK to call
+ * setstate() with the same state as the current state.
+ *
+ * Returns a pointer to the old state information.
+ *
+ * Note: The Sparc platform requires that arg_state begin on an int
+ * word boundary; otherwise a bus error will occur. Even so, lint will
+ * complain about mis-alignment, but you should disregard these messages.
+ */
+char *
+setstate(arg_state)
+        char *arg_state;                /* pointer to state array */
+{
+        uint32_t *new_state = (uint32_t *)arg_state;
+        uint32_t type = new_state[0] % MAX_TYPES;
+        uint32_t rear = new_state[0] / MAX_TYPES;
+        char *ostate = (char *)(&state[-1]);
+
+        if (rand_type == TYPE_0)
+                state[-1] = rand_type;
+        else
+                state[-1] = MAX_TYPES * (rptr - state) + rand_type;
+        switch(type) {
+        case TYPE_0:
+        case TYPE_1:
+        case TYPE_2:
+        case TYPE_3:
+        case TYPE_4:
+                rand_type = type;
+                rand_deg = degrees[type];
+                rand_sep = seps[type];
+                break;
+        default:
+                (void)fprintf(stderr,
+                    "random: state info corrupted; not changed.\n");
+        }
+        state = new_state + 1;
+        if (rand_type != TYPE_0) {
+                rptr = &state[rear];
+                fptr = &state[(rear + rand_sep) % rand_deg];
+        }
+        end_ptr = &state[rand_deg];             /* set end_ptr too */
+        return(ostate);
+}
+
+/*
+ * random:
+ *
+ * If we are using the trivial TYPE_0 R.N.G., just do the old linear
+ * congruential bit.  Otherwise, we do our fancy trinomial stuff, which is
+ * the same in all the other cases due to all the global variables that have
+ * been set up.  The basic operation is to add the number at the rear pointer
+ * into the one at the front pointer.  Then both pointers are advanced to
+ * the next location cyclically in the table.  The value returned is the sum
+ * generated, reduced to 31 bits by throwing away the "least random" low bit.
+ *
+ * Note: the code takes advantage of the fact that both the front and
+ * rear pointers can't wrap on the same call by not testing the rear
+ * pointer if the front one has wrapped.
+ *
+ * Returns a 31-bit random number.
+ */
+long
+random()
+{
+        uint32_t i;
+        uint32_t *f, *r;
+
+        if (rand_type == TYPE_0) {
+                i = state[0];
+                state[0] = i = (good_rand(i)) & 0x7fffffff;
+        } else {
+                /*
+                 * Use local variables rather than static variables for speed.
+                 */
+                f = fptr; r = rptr;
+                *f += *r;
+                i = (*f >> 1) & 0x7fffffff;     /* chucking least random bit */
+                if (++f >= end_ptr) {
+                        f = state;
+                        ++r;
+                }
+                else if (++r >= end_ptr) {
+                        r = state;
+                }
+
+                fptr = f; rptr = r;
+        }
+        return((long)i);
+}
diff --git a/tests/LineNumPrint.ok b/tests/LineNumPrint.ok
new file mode 100644
--- /dev/null
+++ b/tests/LineNumPrint.ok
@@ -0,0 +1,32 @@
+243
+        if (x == 0)
+248
+        if (x < 0)
+273
+        if (rand_type == TYPE_0)
+303
+        if (rand_type == TYPE_0)
+310
+        if (fd >= 0) {
+311
+                if (_read(fd, (void *) state, len) == (ssize_t) len)
+316
+        if (!done) {
+325
+        if (rand_type != TYPE_0) {
+363
+        if (rand_type == TYPE_0)
+367
+        if (n < BREAK_0) {
+372
+        if (n < BREAK_1) {
+396
+        if (rand_type == TYPE_0)
+431
+        if (rand_type == TYPE_0)
+450
+        if (rand_type != TYPE_0) {
+481
+        if (rand_type == TYPE_0) {
+491
+                if (++f >= end_ptr) {
diff --git a/tests/LineNumPrint.sed b/tests/LineNumPrint.sed
new file mode 100644
--- /dev/null
+++ b/tests/LineNumPrint.sed
@@ -0,0 +1,5 @@
+#n print line number and line with if statement
+/        if/{
+=
+p
+}
diff --git a/tests/List.in b/tests/List.in
new file mode 100644
--- /dev/null
+++ b/tests/List.in
@@ -0,0 +1,1 @@
+Here is a string of special characters:    
diff --git a/tests/List.ok b/tests/List.ok
new file mode 100644
--- /dev/null
+++ b/tests/List.ok
@@ -0,0 +1,2 @@
+Here is a string of special characters: \001 \002 \r \r
+Here is a string of special characters:    
diff --git a/tests/List.sed b/tests/List.sed
new file mode 100644
--- /dev/null
+++ b/tests/List.sed
@@ -0,0 +1,1 @@
+l
diff --git a/tests/Next.in b/tests/Next.in
new file mode 100644
--- /dev/null
+++ b/tests/Next.in
@@ -0,0 +1,4 @@
+.H1 "On Egypt"
+
+Napoleon, pointing to the Pyramids, said to his troops:
+"Soldiers, forty centuries have their eyes upon you."
diff --git a/tests/Next.ok b/tests/Next.ok
new file mode 100644
--- /dev/null
+++ b/tests/Next.ok
@@ -0,0 +1,3 @@
+.H1 "On Egypt"
+Napoleon, pointing to the Pyramids, said to his troops:
+"Soldiers, forty centuries have their eyes upon you."
diff --git a/tests/Next.sed b/tests/Next.sed
new file mode 100644
--- /dev/null
+++ b/tests/Next.sed
@@ -0,0 +1,2 @@
+/^\.H1/n
+/^$/d
diff --git a/tests/NextMultiline.in b/tests/NextMultiline.in
new file mode 100644
--- /dev/null
+++ b/tests/NextMultiline.in
@@ -0,0 +1,3 @@
+Consult Section 3.1 in the Owner and Operator
+Guide for a description of the tape drives
+available on your system.
diff --git a/tests/NextMultiline.ok b/tests/NextMultiline.ok
new file mode 100644
--- /dev/null
+++ b/tests/NextMultiline.ok
@@ -0,0 +1,2 @@
+Consult Section 3.1 in the Installation Guide for a description of the tape drives
+available on your system.
diff --git a/tests/NextMultiline.sed b/tests/NextMultiline.sed
new file mode 100644
--- /dev/null
+++ b/tests/NextMultiline.sed
@@ -0,0 +1,4 @@
+/Operator$/{
+N
+s/Owner and Operator\nGuide/Installation Guide/
+}
diff --git a/tests/NextMultiline2.in b/tests/NextMultiline2.in
new file mode 100644
--- /dev/null
+++ b/tests/NextMultiline2.in
@@ -0,0 +1,4 @@
+Consult Section 3.1 in the Owner and Operator Guide for a description of the tape drives available on your system.
+Look in the Owner and Operator Guide shipped with your system.
+Two manuals are provided including the Owner and Operator Guide and the User Guide.
+The Owner and Operator Guide is shipped with your system.
diff --git a/tests/NextMultiline2.ok b/tests/NextMultiline2.ok
new file mode 100644
--- /dev/null
+++ b/tests/NextMultiline2.ok
@@ -0,0 +1,4 @@
+Consult Section 3.1 in the Installation Guide for a description of the tape drives available on your system.
+Look in the Installation Guide shipped with your system.
+Two manuals are provided including the Installation Guide and the User Guide.
+The Installation Guide is shipped with your system.
diff --git a/tests/NextMultiline2.sed b/tests/NextMultiline2.sed
new file mode 100644
--- /dev/null
+++ b/tests/NextMultiline2.sed
@@ -0,0 +1,11 @@
+s/Owner and Operator Guide/Installation Guide/
+/Owner/{
+N
+}
+
+#/Owner/{ 
+#N
+#s/ *\n/ / 
+#s/Owner and Operator Guide */Installation Guide\ 
+#/ 
+#}
diff --git a/tests/NextSimple.in b/tests/NextSimple.in
new file mode 100644
--- /dev/null
+++ b/tests/NextSimple.in
@@ -0,0 +1,3 @@
+1
+2
+3
diff --git a/tests/NextSimple.ok b/tests/NextSimple.ok
new file mode 100644
--- /dev/null
+++ b/tests/NextSimple.ok
@@ -0,0 +1,3 @@
+1
+2
+3
diff --git a/tests/NextSimple.sed b/tests/NextSimple.sed
new file mode 100644
--- /dev/null
+++ b/tests/NextSimple.sed
@@ -0,0 +1,1 @@
+n
diff --git a/tests/Print.in b/tests/Print.in
new file mode 100644
--- /dev/null
+++ b/tests/Print.in
@@ -0,0 +1,5 @@
+.Ah "Comment"
+.Ah "Substitution"
+.Ah "Delete"
+.Ah "Append, Insert and Change"
+.Ah "List"
diff --git a/tests/Print.ok b/tests/Print.ok
new file mode 100644
--- /dev/null
+++ b/tests/Print.ok
@@ -0,0 +1,10 @@
+.Ah "Comment"
+"Comment"
+.Ah "Substitution"
+"Substitution"
+.Ah "Delete"
+"Delete"
+.Ah "Append, Insert and Change"
+"Append, Insert and Change"
+.Ah "List"
+"List"
diff --git a/tests/Print.sed b/tests/Print.sed
new file mode 100644
--- /dev/null
+++ b/tests/Print.sed
@@ -0,0 +1,5 @@
+#n Print line before and after changes.
+/^\.Ah/{
+p
+s/^\.Ah //p
+}
diff --git a/tests/PrintMultiline.in b/tests/PrintMultiline.in
new file mode 100644
--- /dev/null
+++ b/tests/PrintMultiline.in
@@ -0,0 +1,4 @@
+Here are examples of the UNIX
+System. Where UNIX
+System appears, it should be the UNIX
+Operating System.
diff --git a/tests/PrintMultiline.ok b/tests/PrintMultiline.ok
new file mode 100644
--- /dev/null
+++ b/tests/PrintMultiline.ok
@@ -0,0 +1,4 @@
+Here are examples of the UNIX Operating 
+System. Where UNIX Operating 
+System appears, it should be the UNIX
+Operating System.
diff --git a/tests/PrintMultiline.sed b/tests/PrintMultiline.sed
new file mode 100644
--- /dev/null
+++ b/tests/PrintMultiline.sed
@@ -0,0 +1,8 @@
+/UNIX$/{
+  N
+  /\nSystem/{
+  s// Operating &/
+  P
+  D
+  }
+}
diff --git a/tests/Quit.in b/tests/Quit.in
new file mode 100644
--- /dev/null
+++ b/tests/Quit.in
@@ -0,0 +1,10 @@
+Line 1
+Line 2
+Line 3
+Line 4
+Line 5
+Line 6
+Line 7
+Line 8
+Line 9
+Line 10
diff --git a/tests/Quit.ok b/tests/Quit.ok
new file mode 100644
--- /dev/null
+++ b/tests/Quit.ok
@@ -0,0 +1,3 @@
+Line 1
+Line 2
+Line 3
diff --git a/tests/Quit.sed b/tests/Quit.sed
new file mode 100644
--- /dev/null
+++ b/tests/Quit.sed
@@ -0,0 +1,1 @@
+3q
diff --git a/tests/ReadFile.in b/tests/ReadFile.in
new file mode 100644
--- /dev/null
+++ b/tests/ReadFile.in
@@ -0,0 +1,3 @@
+Employee-list
+Company-list
+Client-list
diff --git a/tests/ReadFile.ok b/tests/ReadFile.ok
new file mode 100644
--- /dev/null
+++ b/tests/ReadFile.ok
@@ -0,0 +1,7 @@
+Employee-list
+Company-list
+  Bremer
+  IBM
+  Sybase
+  Apple
+Client-list
diff --git a/tests/ReadFile.sed b/tests/ReadFile.sed
new file mode 100644
--- /dev/null
+++ b/tests/ReadFile.sed
@@ -0,0 +1,1 @@
+/^Company-list/r tests/company.lst
diff --git a/tests/SubstituteGlobal.in b/tests/SubstituteGlobal.in
new file mode 100644
--- /dev/null
+++ b/tests/SubstituteGlobal.in
@@ -0,0 +1,10 @@
+9	who(1) who(1) 
+9 NNNNAAAAMMMMEEEE
+	who - who is on the system? 
+SSSSYYYYNNNNOOOOPPPPSSSSII
+	who [-a] [-b] [-d] [-H] [-l] [-p] [-q] [-r] [-s] [-t] [-T]
+	[-u] [_f_i_l_e] who am i
+		who am I 
+DDDDEEEESSSSCCCCRRRRIIIIPP
+	time,
+	the ...
diff --git a/tests/SubstituteGlobal.ok b/tests/SubstituteGlobal.ok
new file mode 100644
--- /dev/null
+++ b/tests/SubstituteGlobal.ok
@@ -0,0 +1,10 @@
+who(1) who(1) 
+NAME
+who - who is on the system? 
+SYNOPSI
+who [-a] [-b] [-d] [-H] [-l] [-p] [-q] [-r] [-s] [-t] [-T]
+[-u] [file] who am i
+who am I 
+DESCRIP
+time,
+the ...
diff --git a/tests/SubstituteGlobal.sed b/tests/SubstituteGlobal.sed
new file mode 100644
--- /dev/null
+++ b/tests/SubstituteGlobal.sed
@@ -0,0 +1,5 @@
+# sedman - deformat nroff-formatted manpage
+s/.//g
+s/9//g
+s/^[ 	]*//g
+s/	/ /g
diff --git a/tests/SubstituteOccur.in b/tests/SubstituteOccur.in
new file mode 100644
--- /dev/null
+++ b/tests/SubstituteOccur.in
@@ -0,0 +1,1 @@
+Column1	Column2	Column3	Column4
diff --git a/tests/SubstituteOccur.ok b/tests/SubstituteOccur.ok
new file mode 100644
--- /dev/null
+++ b/tests/SubstituteOccur.ok
@@ -0,0 +1,1 @@
+Column1	Column2>Column3	Column4
diff --git a/tests/SubstituteOccur.sed b/tests/SubstituteOccur.sed
new file mode 100644
--- /dev/null
+++ b/tests/SubstituteOccur.sed
@@ -0,0 +1,1 @@
+s/	/>/2
diff --git a/tests/SubstituteOccurEol.in b/tests/SubstituteOccurEol.in
new file mode 100644
--- /dev/null
+++ b/tests/SubstituteOccurEol.in
@@ -0,0 +1,1 @@
+Column1	Column2	Column3	Column4
diff --git a/tests/SubstituteOccurEol.ok b/tests/SubstituteOccurEol.ok
new file mode 100644
--- /dev/null
+++ b/tests/SubstituteOccurEol.ok
@@ -0,0 +1,2 @@
+Column1	Column2
+Column3	Column4
diff --git a/tests/SubstituteOccurEol.sed b/tests/SubstituteOccurEol.sed
new file mode 100644
--- /dev/null
+++ b/tests/SubstituteOccurEol.sed
@@ -0,0 +1,2 @@
+s/	/\
+/2
diff --git a/tests/SubstituteRef.in b/tests/SubstituteRef.in
new file mode 100644
--- /dev/null
+++ b/tests/SubstituteRef.in
@@ -0,0 +1,4 @@
+1.10
+5.20
+10.100
+100.200
diff --git a/tests/SubstituteRef.ok b/tests/SubstituteRef.ok
new file mode 100644
--- /dev/null
+++ b/tests/SubstituteRef.ok
@@ -0,0 +1,4 @@
+1-10=1.10
+5-20=5.20
+10-100=10.100
+100-200=100.200
diff --git a/tests/SubstituteRef.sed b/tests/SubstituteRef.sed
new file mode 100644
--- /dev/null
+++ b/tests/SubstituteRef.sed
@@ -0,0 +1,2 @@
+##s/\([0-9][0-9]*\)\.\([0-9][0-9]*\)/\1-\2=&/
+s/([0-9][0-9]*)\.([0-9][0-9]*)/\1-\2=&/
diff --git a/tests/Transform.in b/tests/Transform.in
new file mode 100644
--- /dev/null
+++ b/tests/Transform.in
@@ -0,0 +1,1 @@
+Hello world!
diff --git a/tests/Transform.ok b/tests/Transform.ok
new file mode 100644
--- /dev/null
+++ b/tests/Transform.ok
@@ -0,0 +1,1 @@
+HELLO WORLD!
diff --git a/tests/Transform.sed b/tests/Transform.sed
new file mode 100644
--- /dev/null
+++ b/tests/Transform.sed
@@ -0,0 +1,1 @@
+y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
diff --git a/tests/WriteFile.in b/tests/WriteFile.in
new file mode 100644
--- /dev/null
+++ b/tests/WriteFile.in
@@ -0,0 +1,7 @@
+Adams, Henrietta	Northeast
+Banks, Freda		South
+Dennis, Jim		Midwest
+Garvey, Bill		Northeast
+Jeffries, Jane		West
+Madison, Sylvia		Midwest
+Sommes, Tom		South
diff --git a/tests/WriteFile.ok b/tests/WriteFile.ok
new file mode 100644
--- /dev/null
+++ b/tests/WriteFile.ok
@@ -0,0 +1,7 @@
+Adams, Henrietta	Northeast
+Banks, Freda		South
+Dennis, Jim		Midwest
+Garvey, Bill		Northeast
+Jeffries, Jane		West
+Madison, Sylvia		Midwest
+Sommes, Tom		South
diff --git a/tests/WriteFile.sed b/tests/WriteFile.sed
new file mode 100644
--- /dev/null
+++ b/tests/WriteFile.sed
@@ -0,0 +1,4 @@
+/Northeast$/w region.northeast
+/South$/w region.south
+/Midwest$/w region.midwest
+/West$/w region.west
diff --git a/tests/company.lst b/tests/company.lst
new file mode 100644
--- /dev/null
+++ b/tests/company.lst
@@ -0,0 +1,4 @@
+  Bremer
+  IBM
+  Sybase
+  Apple
