diff --git a/hadolint.cabal b/hadolint.cabal
--- a/hadolint.cabal
+++ b/hadolint.cabal
@@ -1,11 +1,11 @@
--- This file has been generated from package.yaml by hpack version 0.21.2.
+-- This file has been generated from package.yaml by hpack version 0.28.2.
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: b523045510203dbac76d6488afec9838250f551597ef35d8b888adcbdc39e619
+-- hash: 913a53c45d2193a9fb13a8dfb47bf647d985298fc2522ce1e369ab2ed1d1a311
 
 name:           hadolint
-version:        1.6.5
+version:        1.6.6
 synopsis:       Dockerfile Linter JavaScript API
 description:    A smarter Dockerfile linter that helps you build best practice Docker images.
 category:       Development
@@ -17,7 +17,6 @@
 license-file:   LICENSE
 build-type:     Simple
 cabal-version:  >= 1.10
-
 extra-source-files:
     README.md
 
@@ -34,7 +33,7 @@
     , aeson
     , base >=4.8 && <5
     , bytestring
-    , dlist
+    , containers
     , language-docker >=5.0.0 && <6
     , parsec >=3.1
     , split >=0.2
diff --git a/src/Hadolint/Formatter/Checkstyle.hs b/src/Hadolint/Formatter/Checkstyle.hs
--- a/src/Hadolint/Formatter/Checkstyle.hs
+++ b/src/Hadolint/Formatter/Checkstyle.hs
@@ -9,7 +9,7 @@
 import qualified Data.ByteString.Builder as Builder
 import qualified Data.ByteString.Lazy.Char8 as B
 import Data.Char
-import Data.DList (toList)
+import Data.Foldable (toList)
 import Data.List (groupBy)
 import Data.Monoid ((<>), mconcat)
 import Hadolint.Formatter.Format
@@ -79,7 +79,7 @@
         if isOk c
             then [c]
             else "&#" ++ show (ord c) ++ ";"
-    isOk x = any ($x) [isAsciiUpper, isAsciiLower, isDigit, (`elem` [' ', '.', '/'])]
+    isOk x = any (\check -> check x) [isAsciiUpper, isAsciiLower, isDigit, (`elem` [' ', '.', '/'])]
 
 formatResult :: Result -> Builder.Builder
 formatResult (Result errors checks) =
diff --git a/src/Hadolint/Formatter/Codeclimate.hs b/src/Hadolint/Formatter/Codeclimate.hs
--- a/src/Hadolint/Formatter/Codeclimate.hs
+++ b/src/Hadolint/Formatter/Codeclimate.hs
@@ -9,8 +9,8 @@
 
 import Data.Aeson hiding (Result)
 import qualified Data.ByteString.Lazy as B
-import Data.DList (DList)
 import Data.Monoid ((<>))
+import Data.Sequence (Seq)
 import GHC.Generics
 import Hadolint.Formatter.Format (Result(..), formatErrorReason)
 import Hadolint.Rules (Metadata(..), RuleCheck(..))
@@ -59,7 +59,7 @@
     Issue
     { checkName = "DL1000"
     , description = formatErrorReason err
-    , location = LocPos (sourceName pos) Pos{..}
+    , location = LocPos (sourceName pos) Pos {..}
     , impact = severityText ErrorC
     }
   where
@@ -67,7 +67,6 @@
     line = sourceLine pos
     column = sourceColumn pos
 
-
 checkToIssue :: RuleCheck -> Issue
 checkToIssue RuleCheck {..} =
     Issue
@@ -85,7 +84,7 @@
         InfoC -> "info"
         StyleC -> "minor"
 
-formatResult :: Result -> DList Issue
+formatResult :: Result -> Seq Issue
 formatResult (Result errors checks) = allIssues
   where
     allIssues = errorMessages <> checkMessages
diff --git a/src/Hadolint/Formatter/Format.hs b/src/Hadolint/Formatter/Format.hs
--- a/src/Hadolint/Formatter/Format.hs
+++ b/src/Hadolint/Formatter/Format.hs
@@ -6,18 +6,18 @@
     , toResult
     ) where
 
-import Data.DList (DList, fromList, singleton)
 import Data.List (sort)
 import Data.Monoid (Monoid)
 import Data.Semigroup
+import Data.Sequence (Seq, fromList, singleton)
 import Hadolint.Rules
 import ShellCheck.Interface
 import Text.Parsec.Error
        (ParseError, errorMessages, showErrorMessages)
 
 data Result = Result
-    { errors :: DList ParseError
-    , checks :: DList RuleCheck
+    { errors :: Seq ParseError
+    , checks :: Seq RuleCheck
     } deriving (Eq)
 
 instance Semigroup Result where
diff --git a/src/Hadolint/Formatter/TTY.hs b/src/Hadolint/Formatter/TTY.hs
--- a/src/Hadolint/Formatter/TTY.hs
+++ b/src/Hadolint/Formatter/TTY.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE NamedFieldPuns #-}
+
 module Hadolint.Formatter.TTY
     ( printResult
     , formatError
@@ -26,13 +27,10 @@
         formatPos source line ++ code meta ++ " " ++ message meta
 
 formatPos :: Filename -> Linenumber -> String
-formatPos source line =
-    if line >= 0
-        then source ++ ":" ++ show line ++ " "
-        else source ++ " "
+formatPos source line = source ++ ":" ++ show line ++ " "
 
 printResult :: Result -> IO ()
-printResult Result{errors, checks} = printErrors >> printChecks
+printResult Result {errors, checks} = printErrors >> printChecks
   where
     printErrors = mapM_ putStrLn (formatErrors errors)
     printChecks = mapM_ putStrLn (formatChecks checks)
diff --git a/src/Hadolint/Rules.hs b/src/Hadolint/Rules.hs
--- a/src/Hadolint/Rules.hs
+++ b/src/Hadolint/Rules.hs
@@ -10,6 +10,7 @@
 import Hadolint.Bash
 import Language.Docker.Syntax
 
+import qualified Data.Set as Set
 import qualified ShellCheck.Interface
 import ShellCheck.Interface (Severity(..))
 import qualified Text.Parsec as Parsec
@@ -76,19 +77,8 @@
     -> Rule
 instructionRuleState code severity message = mapInstructions (Metadata code severity message)
 
-dockerfileRule ::
-       String -- ^ The rule code name
-    -> ShellCheck.Interface.Severity -- ^ The rule severity
-    -> String -- ^ The error message associated with the rule
-    -> ([Instruction] -> Bool) -- ^ The function that will check the rule
-    -> Rule
-dockerfileRule code severity message f = rule
-  where
-    rule dockerfile =
-        [RuleCheck metadata (filename dockerfile) (-1) (f (map instruction dockerfile))]
-    metadata = Metadata code severity message
-    filename (inst:_) = sourcename inst
-    filename [] = ""
+withState :: a -> b -> (a, b)
+withState st res = (st, res)
 
 -- Enforce rules on a dockerfile and return failed checks
 analyze :: [Rule] -> Dockerfile -> [RuleCheck]
@@ -225,54 +215,55 @@
     check _ = True
 
 hasNoMaintainer :: Rule
-hasNoMaintainer = dockerfileRule code severity message check
+hasNoMaintainer = instructionRule code severity message check
   where
     code = "DL4000"
     severity = ErrorC
     message = "MAINTAINER is deprecated"
-    check dockerfile = not $ any isMaintainer dockerfile
-    isMaintainer (Maintainer _) = True
-    isMaintainer _ = False
+    check (Maintainer _) = False
+    check _ = True
 
 -- Check if a command contains a program call in the Run instruction
 usingProgram :: String -> [String] -> Bool
 usingProgram prog args = or [True | cmd:_ <- bashCommands args, cmd == prog]
 
 multipleCmds :: Rule
-multipleCmds = dockerfileRule code severity message check
+multipleCmds = instructionRuleState code severity message check Nothing
   where
     code = "DL4003"
     severity = WarningC
     message =
         "Multiple `CMD` instructions found. If you list more than one `CMD` then only the last \
         \`CMD` will take effect"
-    check dockerfile = 1 >= length (filter (True ==) $ map isCmd dockerfile)
-    isCmd (Cmd _) = True
-    isCmd _ = False
+    check Nothing line (Cmd _) = withState (Just line) True -- Remember the first CMD found
+    check (Just l) _ (Cmd _) = withState (Just l) False -- Fail the rule, CMD is duplicated
+    check st _ _ = withState st True
 
 multipleEntrypoints :: Rule
-multipleEntrypoints = dockerfileRule code severity message check
+multipleEntrypoints = instructionRuleState code severity message check Nothing
   where
     code = "DL4004"
     severity = ErrorC
     message =
         "Multiple `ENTRYPOINT` instructions found. If you list more than one `ENTRYPOINT` then \
         \only the last `ENTRYPOINT` will take effect"
-    check dockerfile = 1 >= length (filter (True ==) $ map isEntrypoint dockerfile)
-    isEntrypoint (Entrypoint _) = True
-    isEntrypoint _ = False
+    check Nothing line (Entrypoint _) = withState (Just line) True -- Remember the first ENTRYPOINT found
+    check (Just l) _ (Entrypoint _) = withState (Just l) False -- Fail the rule, ENTRYPOINT is duplicated
+    check st _ _ = withState st True
 
 wgetOrCurl :: Rule
-wgetOrCurl = dockerfileRule code severity message check
+wgetOrCurl = instructionRuleState code severity message check Set.empty
   where
     code = "DL4001"
     severity = WarningC
     message = "Either use Wget or Curl but not both"
-    check dockerfile = not $ anyCurl dockerfile && anyWget dockerfile
-    anyCurl = any $ usingCmd "curl"
-    anyWget = any $ usingCmd "wget"
-    usingCmd cmd (Run (Arguments args)) = cmd `elem` args
-    usingCmd _ _ = False
+    check state _ (Run (Arguments args)) = detectDoubleUsage state args
+    check state _ _ = withState state True
+    detectDoubleUsage state args =
+        let newArgs = extractCommands args
+            newState = Set.union state newArgs
+        in withState newState (Set.size newState < 2)
+    extractCommands args = Set.fromList [w | w <- args, w == "curl" || w == "wget"]
 
 invalidCmd :: Rule
 invalidCmd = instructionRule code severity message check
@@ -375,7 +366,6 @@
     code = "DL3009"
     severity = InfoC
     message = "Delete the apt-get lists after installing something"
-    withState st res = (st, res)
     -- | 'check' returns a tuple (state, check_result)
     --   The state in this case is the FROM instruction where the current instruction we are
     --   inspecting is nested in.
@@ -653,7 +643,6 @@
     code = "DL3023"
     severity = ErrorC
     message = "COPY --from should reference a previously defined FROM alias"
-    withState st res = (st, res)
     -- | 'check' returns a tuple (state, check_result)
     --   The state in this case is the FROM instruction where the current instruction we are
     --   inspecting is nested in.
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -453,19 +453,21 @@
                   ruleCatchesNot copyFromAnother $ unlines dockerFile
                   ruleCatchesNot copyEndingSlash $ unlines dockerFile
 
-assertChecks :: Rule -> String -> ([RuleCheck] -> IO a) -> IO a
-assertChecks rule s f =
+assertChecks :: HasCallStack => Rule -> String -> ([RuleCheck] -> IO a) -> IO a
+assertChecks rule s makeAssertions =
     case parseString (s ++ "\n") of
         Left err -> assertFailure $ show err
-        Right dockerFile -> f $ analyze [rule] dockerFile
+        Right dockerFile -> makeAssertions $ analyze [rule] dockerFile
 
 -- Assert a failed check exists for rule
-ruleCatches :: Rule -> String -> Assertion
+ruleCatches :: HasCallStack => Rule -> String -> Assertion
 ruleCatches rule s = assertChecks rule s f
   where
-    f checks = assertEqual "No check for rule found" 1 $ length checks
+    f checks = do
+      assertEqual "No check for rule found" 1 $ length checks
+      assertBool "Incorrect line number for result" $ null [c | c <- checks, linenumber c <= 0]
 
-ruleCatchesNot :: Rule -> String -> Assertion
+ruleCatchesNot :: HasCallStack => Rule -> String -> Assertion
 ruleCatchesNot rule s = assertChecks rule s f
   where
     f checks = assertEqual "Found check of rule" 0 $ length checks
