diff --git a/scan.cabal b/scan.cabal
--- a/scan.cabal
+++ b/scan.cabal
@@ -1,7 +1,7 @@
-cabal-version:      >= 1.6
+cabal-version:      >= 1.4
 build-type:         Simple
 name:               scan
-version:            0.1.0.2
+version:            0.1.0.3
 license:            BSD3
 license-file:       doc/LICENSE
 category:           Development
@@ -17,7 +17,7 @@
 stability:          experimental
 
 executable scan
-    build-depends:      base < 5, parsec < 3 || >= 3
+    build-depends:      base < 5, parsec < 3
     hs-source-dirs:     src
     main-is:            scan.hs
     other-modules:      Language.Haskell.Scanner
diff --git a/src/Language/Haskell/Scanner.hs b/src/Language/Haskell/Scanner.hs
--- a/src/Language/Haskell/Scanner.hs
+++ b/src/Language/Haskell/Scanner.hs
@@ -14,11 +14,9 @@
 -}
 
 module Language.Haskell.Scanner
-  ( splitLines
-  , splitBy
+  ( scan
   , showScan
   , processScan
-  , scan
   , PosTok
   , Diag (..)
   , showDiag
@@ -174,27 +172,29 @@
 infixOp :: Parser String
 infixOp = enclosedBy (fmap showQualName qId) $ char '`'
 
--- | the separators are newline, comma, semicolon and three kinds of parens
+-- | the separators are comma, semicolon and three kinds of parens
 seps :: String
-seps = "[({,;})]\n"
+seps = "[({,;})]"
 
 -- | beside names and separators we have a couple of more token kinds
-data TokenKind = LineComment | BlockComment | Literal | Infix
+data TokenKind =
+    LineComment  -- ^ a line comment
+  | BlockComment -- ^ single- or multiline (nested) block comment
+  | Literal      -- ^ number, character, or string
+  | Infix        -- ^ something in back quotes
+  | Sep          -- ^ a separator
+  | Indent       -- ^ a newline or nothing at the very beginning
 
 -- | the data type for tokens
 data Token
   = QualName QualName
-  | Sep Char
   | Token TokenKind String
-  | Start -- ^ the void token at the very beginning
 
 -- | renders the original string without kind information
 showToken :: Token -> String
 showToken t = case t of
     QualName q -> showQualName q
-    Sep c -> [c]
     Token _ s -> s
-    Start -> ""
 
 -- | all white spaces except newline
 isWhite :: Char -> Bool
@@ -215,7 +215,8 @@
 tok = fmap (Token BlockComment) nestComment
   <|> fmap (Token LineComment) lineComment
   <|> fmap QualName qId
-  <|> fmap Sep (oneOf seps)
+  <|> fmap (Token Sep) (single $ oneOf seps)
+  <|> fmap (Token Indent) (string "\n")
   <|> fmap (Token Literal) literal
   <|> fmap (Token Infix) infixOp
 
@@ -239,32 +240,12 @@
 
 -- | the initial 'white' stuff
 startTok :: Parser PosTok
-startTok = fmap (PosTok startPos Start) white
+startTok = fmap (PosTok startPos $ Token Indent "") white
 
 -- | the final scanner
 scan :: Parser [PosTok]
 scan = startTok <:> many posTok << eof
 
--- * splitting and removing
-
-{- | a generic splitting function that keeps the separator as first element
-except in the first list. -}
-splitBy :: (a -> Bool) -> [a] -> [[a]]
-splitBy p l = let (fr, rt) = break p l in fr : case rt of
-  [] -> []
-  d : tl -> let hd : tll = splitBy p tl in (d : hd) : tll
-
--- | removing more than two consecutive lists fulfilling the predicate
-removeBlankLines :: Int -> ([a] -> Bool) -> [[a]] -> [[a]]
-removeBlankLines c p l = case l of
-  [] -> []
-  x : r ->
-    if p x
-    then if c > 1
-         then removeBlankLines c p r
-         else x : removeBlankLines (c + 1) p r
-    else x : removeBlankLines 0 p r
-
 -- * message data type
 
 -- | messages with positions
@@ -300,14 +281,13 @@
 
 isSepIn :: String -> Token -> Bool
 isSepIn cs t = case t of
-  Sep c -> elem c cs
+  Token Sep [c] -> elem c cs
   _ -> False
 
 isIndent :: Token -> Bool
-isIndent = isSepIn "\n"
-
-isLineBreak :: PosTok -> Bool
-isLineBreak (PosTok _ t _) = isIndent t
+isIndent t = case t of
+  Token Indent _ -> True
+  _ -> False
 
 isOpPar :: Token -> Bool
 isOpPar = isSepIn "[({"
@@ -329,34 +309,27 @@
   QualName (Name _ k _) -> case k of
     Sym -> False
     _ -> True
-  Sep _ -> isClPar t
   Token k _ -> case k of
     Literal -> True
     Infix -> True
+    Sep -> isClPar t
     _ -> False
-  Start -> False
 
+isSymb :: [String] -> Token -> Bool
+isSymb s t = case t of
+  QualName (Name False Sym r) -> elem r s
+  _ -> False
+
 noSpaceNeededBefore :: Token -> Bool
 noSpaceNeededBefore t =
-  isSepIn ",;})]" t || showToken t == "@"
+  isSepIn ",;})]" t || isIndent t || isSymb ["-", "@"] t
 
 noSpaceNeededAfter :: Token -> Bool
 noSpaceNeededAfter t =
-  isOpPar t || elem (showToken t) (map (: []) "!#-@~")
+  isOpPar t || isIndent t || isSymb (map (: []) "!#-@~") t
 
 -- * adjusting tokens
 
--- | replace all white spaces by blanks
-untabify :: SourcePos -> String -> String
-untabify p s =
-  let p2 = updatePosString p s
-      bs = sourceColumn p2 - sourceColumn p
-  in replicate bs ' '
-
-untab :: PosTok -> PosTok
-untab (PosTok p t w) =
-  PosTok p t $ untabify (updatePosString p $ showToken t) w
-
 -- | remove trailing spaces
 rmSp :: PosTok -> PosTok
 rmSp (PosTok p t _) = PosTok p t ""
@@ -366,16 +339,17 @@
 blank (PosTok p t _) = PosTok p t " "
 
 multipleBlanks :: PosTok -> [Diag]
-multipleBlanks (PosTok p t w) = let n = length w in
-  [ Diag (updatePosString p $ showToken t)
-    $ "multiple (" ++ show n ++ ") blanks"
-  | n > 1 ]
+multipleBlanks (PosTok p t w) =
+  let q = updatePosString p $ showToken t
+      n = length w
+  in [ Diag q $ "up to column " ++ show (sourceColumn q + n)
+       ++ " multiple (" ++ show n ++ ") blanks" | n > 1 ]
 
 -- | tidy up line comments
 adjustLineComment :: String -> String
-adjustLineComment = ("--" ++)
-  . reverse . dropWhile isSpace . reverse
-  . (' ' :) . dropWhile isSpace . drop 2 -- cut of initial line comment marker
+adjustLineComment s = -- cut of initial line comment marker and spaces
+  let r = dropWhile isSpace $ drop 2 s in
+  "--" ++ if null r then r else ' ' : r
 
 -- | check if prefix extended by one char from next string is still a prefix
 hasLongerPrefix :: String -> String -> String -> Bool
@@ -397,10 +371,7 @@
 adjustBothEnds :: String -> String -> String -> String -> String
 adjustBothEnds p q n s =
     if hasLongerPrefix p n s then s else
-    concatMap (reverse . dropWhile isWhite . reverse)
-    $ removeBlankLines 0 (all isSpace)
-    $ splitBy (== '\n')
-    $ reverse $ adjustPrefix (reverse q) n $ reverse $ adjustPrefix p n s
+    reverse $ adjustPrefix (reverse q) n $ reverse $ adjustPrefix p n s
 
 -- | adjust a block comment
 adjustComment :: String -> String
@@ -410,12 +381,6 @@
 
 -- * analyse and adjust lines
 
--- | utility to combine results
-(<+>) :: (PosTok, [Diag]) -> ([PosTok], [Diag]) -> ([PosTok], [Diag])
-(t, ds) <+> (ts, es) = (t : ts, ds ++ es)
-
-infixr 4 <+>
-
 anaPosTok :: PosTok -> (PosTok, [Diag])
 anaPosTok t@(PosTok p u w) = case u of
   Token BlockComment s -> let
@@ -427,106 +392,86 @@
        [ Diag p $ "non-conventional comment start: " ++ s5 | s5 /= take 5 n ]
        ++ [ Diag (updatePosString p s) $ "non-conventional comment end: "
             ++ reverse sr | sr /= nr ])
-  _ | showToken u == ";" -> (t, [Diag p "use layout instead of ;"])
-  _ -> (t, [])
+  Token LineComment s -> let
+    n = adjustLineComment s
+    s4 = take 4 s
+    sr = takeWhile isPrint $ dropWhile isSpace $ drop 2 s4
+    q = incSourceColumn p 2
+    in (PosTok p (Token LineComment n) "",
+        [ Diag q $ "put " ++ (if null sr then "only " else "")
+          ++ "one blank after --"
+          ++ if null sr then "" else " in --" ++ sr
+        | s4 /= take 4 n ]
+        ++ [ Diag q "missing comment after --" | n == "--" ])
+  _ -> (t, [ Diag p "use layout instead of ;" | isSepIn ";" u ])
 
 anaLine :: [PosTok] -> ([PosTok], [Diag])
 anaLine l = case l of
   [] -> ([], [])
   t0 : r1 -> let (t1@(PosTok p1 u1 w1), cs) = anaPosTok t0 in case r1 of
-    [] -> case u1 of
-      Token LineComment s -> let
-        n = adjustLineComment s
-        s4 = take 4 s
-        in ([PosTok p1 (Token LineComment n) ""], cs ++
-           [Diag p1 $ "leave a single blank after line comment sign: " ++ s4
-           | s4 /= take 4 n])
-      _ -> ([rmSp t1], cs ++ [Diag p1 "trailing white space" | not (null w1)])
-    t2@(PosTok p2 u2 w2) : r2 -> case u1 of
-      Start -> (rmSp t1, cs ++ [Diag p1 "leading white space" | not (null w1)])
-        <+> anaLine r1
-      _ | isIndent u1 -> let (ft, rt) = span (== ' ') w1 in
-           (untab t1, cs ++
-           [ Diag (updatePosString p1 ft) "use only blanks for indentation"
-           | not (null rt) ]) <+> anaLine r1
-      _ -> let
+    [] -> ([t1], cs)
+    t2@(PosTok p2 u2 w2) : r2 -> let
+        (ar1, rds) = anaLine r1
         s1 = showToken u1
         s2 = showToken u2
+        i1 = isInfixOp u1
+        i2 = isInfixOp u2
         n1 = length s1
         n2 = length s2
         lt = n1 <= n2
         both = s1 ++ s2
+        nw1 = null w1
+        bt1 = blank t1
+        m1 = multipleBlanks t1
         after = case () of
           _ | isNonPar u1 -> True
             | isOpPar u2 -> False
-            | s1 == "\\" -> True
-            | isInfixOp u1 -> if isInfixOp u2 then lt else True
-            | isInfixOp u2 || s2 == ".." -> False
+            | isSymb ["\\"] u1 -> True
+            | i1 -> if i2 then lt else True
+            | i2 || isSymb [".."] u2 -> False
           _ -> lt
         parsOfBoth = filter (`elem` "[({})]") both
-        pos = case both of
+        pos = case parsOfBoth of
              _ : _ : _ -> "between"
              _ | isOpPar u1 -> "after"
                | isClPar u2 -> "before"
              _ -> "here"
         omitSpace = isOpParOrInfix u1 && isClParOrInfix u2
-          && not (isInfixOp u1 && isInfixOp u2)
-          && (s1 /= ".." || s2 /= "]")
+          && not (i1 && i2)
+          && (not (isSymb [".."] u1) || not (isSepIn "]" u2))
         addSpace = not (noSpaceNeededAfter u1)
           && not (noSpaceNeededBefore u2)
-        (newT1, ds) =
-          if null w1
-          then if addSpace
-            then (blank t1, Diag p2
-                  ("leave space " ++
-                   if after then "after " ++ s1 else "before " ++ s2)
-               : [ Diag p1 $ "may be template haskell " ++ both
-                 | s1 == "$" ])
-            else (t1, [])
-          else if omitSpace
-            then (rmSp t1,
-               [Diag p2 $ "no space needed " ++ pos ++ " " ++ parsOfBoth])
-            else if isComment u2
-              then (untab t1, [])
-              else (blank t1, multipleBlanks t1)
-        pt0 = (newT1, cs ++ ds)
-        in case r2 of
-          [] -> pt0 <+> anaLine r1
-          PosTok _ u3 _ : _ -> let
-            s3 = showToken u3
-            ms = [Diag p2 "put spaces around infix -" | null w1 || null w2 ]
-                 ++ multipleBlanks t1 ++ multipleBlanks t2
-            in if s2 == "-" && not (noSpaceNeededBefore u3)
-                  && isFstInfixMinusArg u1
-               then (blank t1, cs ++ ms) <+> anaLine (blank t2 : r2)
-               else (if elem s2 ["do", "of"] && s3 /= "{" && noLineComment u3
-                 then (newT1,
-                   cs ++ Diag p2 ("break line after " ++ show s2) : ds)
-                 else pt0) <+> anaLine r1
-
--- * ensure final newline
-
-isBlankLine :: [PosTok] -> Bool
-isBlankLine x = case x of
-  [PosTok _ Start _] -> True
-  [t] -> isLineBreak t
-  _ -> False
-
-removeFinalBlankLines :: [[PosTok]] -> [[PosTok]]
-removeFinalBlankLines ll = reverse $ [PosTok startPos (Sep '\n') ""]
-  : dropWhile isBlankLine (reverse ll)
+        in case u1 of
+      Token BlockComment s | isIndent u2 -> (t1 : ar1, cs ++
+          [ Diag p1 "could be a line comment"
+          | isPrefixOf "{- " s && not (any (== '\n') s) ] ++ rds)
+      _ | nw1 && addSpace -> (bt1 : ar1, cs ++
+            Diag p2 ("put blank " ++
+                    if after then "after " ++ s1 else "before " ++ s2)
+            : [ Diag p1 $ "may be template haskell " ++ both
+              | isSymb ["$"] u1 ] ++ rds)
+      _ | isIndent u1 || isComment u2 -> (t1 : ar1, cs ++ rds)
+      _ | not nw1 && omitSpace -> (rmSp t1 : ar1, cs ++
+            Diag p2 ("no space needed " ++ pos ++ " " ++ parsOfBoth) : rds)
+      _ -> case r2 of
+        PosTok _ u3 _ : _
+          | isSymb ["-"] u2 && not (noSpaceNeededBefore u3)
+            && isFstInfixMinusArg u1 ->
+            (bt1 : blank t2 : tail ar1, cs ++ m1 ++
+             [ Diag p2 "put blanks around infix -" | nw1 || null w2 ]
+             ++ multipleBlanks t2 ++ rds)
+          | elem s2 ["do", "of"] && not (isSepIn "{" u3)
+            && noLineComment u3 && not (isIndent u3) ->
+            (bt1 : ar1, cs ++ m1 ++
+            Diag p2 ("break line after " ++ show s2) : rds)
+        _ -> ((if nw1 then t1 else bt1) : ar1, cs ++ m1 ++ rds)
 
 -- * main functions
 
--- | split lines at newline tokens
-splitLines :: [PosTok] -> [[PosTok]]
-splitLines = splitBy isLineBreak
-
 -- | create adjusted source file
-processScan :: [[PosTok]] -> String
-processScan = concatMap (concatMap showPosTok . fst . anaLine)
-  . removeBlankLines 1 isBlankLine . removeFinalBlankLines
+processScan :: [PosTok] -> String
+processScan = concatMap showPosTok . fst . anaLine
 
 -- | list all diagnostics
-showScan :: [[PosTok]] -> [Diag]
-showScan = concatMap (snd . anaLine)
+showScan :: [PosTok] -> [Diag]
+showScan = snd . anaLine
diff --git a/src/scan.hs b/src/scan.hs
--- a/src/scan.hs
+++ b/src/scan.hs
@@ -13,8 +13,6 @@
 
 module Main () where
 
-import Control.Monad
-
 import Data.Char
 import Data.List
 
@@ -32,7 +30,7 @@
 
 -- | the hard-coded version string.
 version :: String
-version = "scan-0.1.0.2 http://projects.haskell.org/style-scanner/"
+version = "scan-0.1.0.3 http://projects.haskell.org/style-scanner/"
 
 -- | the usage string
 usage :: String
@@ -46,7 +44,8 @@
 main = do
   args <- getArgs
   let (optsOrFiles, files1) = span (/= "--") args
-      (opts, files2) = partition (isPrefixOf "-") optsOrFiles
+      (opts, files2) = partition
+        (\ arg -> isPrefixOf "-" arg || all isDigit arg) optsOrFiles
       files = files2 ++ drop 1 files1
   case opts of
     [] -> case files of
@@ -64,15 +63,25 @@
 process b f = do
   str <- readFile f
   let ls = lines str
-      cs = concatMap (checkLine f) (zip [1 ..] ls)
-        ++ checkBlankLines f 1 1 ls
-        ++ [ Diag (diagLinePos f (length ls)) "missing final newline"
-           | not $ isSuffixOf "\n" str ]
+      wcsr = map (checkLine f) (zip [1 ..] ls)
+      wcs = map fst wcsr
+      wfile = [ Diag (diagLinePos f 2) "windows (CRLF) file" ]
+      noNL = not $ isSuffixOf "\n" str
+      cs = case group $ map fst wcs of
+             [True : _, [False]] | noNL -> wfile -- with missing newline
+             x : _ : _ ->
+               [ Diag (diagLinePos f (length x + 1))
+                 "inconsistent unix (LF) or windows (CRLF) file" ]
+             [True : _] -> wfile
+             _ -> []
+        ++ concatMap snd wcs ++ checkBlankLines f 0 0 ls
+      newStr = unlines $ map snd wcsr
+      fs = [ Diag (diagLinePos f $ length ls) "missing final newline" | noNL ]
       prDiags = mapM_ (putStrLn . showDiag)
-  case parse scan f str of
-    Right ts -> let x = splitLines ts in
-      if b then let ds = showScan x in prDiags $ cs ++ ds else
-      let rstr = processScan x in
+  case parse scan f newStr of
+    Right x ->
+      if b then let ds = showScan x in prDiags $ cs ++ ds ++ fs else
+      let rstr = unlines . removeBlankLines 0 null . lines $ processScan x in
       if rstr == str then putStrLn $ "no changes in \"" ++ f ++ "\"" else do
       writeFile (f ++ ".bak") str
       writeFile f rstr
@@ -90,34 +99,52 @@
          (errorMessages err)
 
 -- | check for more than two consecutive lines
-checkBlankLines :: FilePath -> Int -> Int -> [String] -> [Diag]
-checkBlankLines f c n l = let p = diagLinePos f in case l of
-  [] -> [Diag (p (n - 1)) "trailing blank lines" | c > 0]
+checkBlankLines :: FilePath
+  -> Int -- ^ current number of blank lines
+  -> Int -- ^ current line number
+  -> [String] -> [Diag]
+checkBlankLines f c n l = let p = diagLinePos f n in case l of
+  [] -> [Diag p $ "trailing (" ++ show c ++ ") blank lines" | c > 0]
   s : r -> let n1 = n + 1 in
-    if null $ filter (not . isSpace) s then
-      if c >= 2 then
-        Diag (p n) "too many consecutive blank lines"
-        : checkBlankLines f (- 20) n1 r
-      else checkBlankLines f (c + 1) n1 r
-    else checkBlankLines f 0 n1 r
+    if null $ filter (not . isSpace) s then checkBlankLines f (c + 1) n1 r
+    else [ Diag p $ "too many (" ++ show c ++ ") consecutive blank lines"
+         | c > 2 ] ++ checkBlankLines f 0 n1 r
 
+-- | removing more than two consecutive lists fulfilling the predicate
+removeBlankLines :: Int -> ([a] -> Bool) -> [[a]] -> [[a]]
+removeBlankLines c p l = case l of
+  [] -> []
+  x : r ->
+    if p x
+    then removeBlankLines (c + 1) p r
+    else replicate (min 2 c) [] ++ x : removeBlankLines 0 p r
+
 -- | create a position from a file and a line number
 diagLinePos :: FilePath -> Int -> SourcePos
 diagLinePos = setSourceLine . initialPos
 
 -- | check length, chars and end of a line
-checkLine :: FilePath -> (Int, String) -> [Diag]
+checkLine :: FilePath -> (Int, String) -> ((Bool, [Diag]), String)
 checkLine f (n, s) =
   let r = reverse s
-      rt = dropWhile isSpace r
-      l = length rt
-      trailBSlash = takeWhile (== '\\') rt
+      (sps, rt) = span isSpace r
+      (w, ws) = case sps of
+         '\r' : rs -> (True, rs)
+         _ -> (False, sps)
+      t = reverse rt
       p = diagLinePos f n
-  in [Diag p $ "too long line (" ++ show l ++ " chars)" | l > 80]
-  ++ badChars p s
-  ++ [Diag (updatePosString p $ init s)
+      v = untabify p t
+      l = length v
+      trailBSlash = takeWhile (== '\\') rt
+  in ((w,
+     [ Diag p $ "too long line (" ++ show l ++ " chars)" | l > 80 ]
+  ++ badChars p t
+  ++ [ Diag (setSourceColumn p l)
        "back slash at line end (may disturb cpp)"
-     | not (null trailBSlash)]
+     | not (null trailBSlash) ]
+  ++ [ Diag (setSourceColumn p $ l + 1)
+       $ "trailing (" ++ show (length ws) ++ ") white space"
+     | not (null ws) ]), v)
 
 -- | create diagnostics for bad characters in a line
 badChars :: SourcePos -> String -> [Diag]
@@ -125,5 +152,21 @@
   let h : r = splitBy (\ c -> not $ isAscii c && isPrint c) s
   in snd $ mapAccumL (\ q t@(f : _) ->
             (updatePosString q t, Diag (updatePosChar q f)
-            $ "undesirable character: " ++ show f))
+            $ "undesirable character " ++ show f))
          (updatePosString p h) r
+
+{- | a generic splitting function that keeps the separator as first element
+except in the first list. -}
+splitBy :: (a -> Bool) -> [a] -> [[a]]
+splitBy p l = let (fr, rt) = break p l in fr : case rt of
+  [] -> []
+  d : tl -> let hd : tll = splitBy p tl in (d : hd) : tll
+
+-- | replace all tabs by blanks in a string
+untabify :: SourcePos -> String -> String
+untabify p s = case s of
+  "" -> ""
+  c : r -> let q = updatePosChar p c in case c of
+      '\t' -> replicate (sourceColumn q - sourceColumn p) ' '
+      _ -> [c]
+    ++ untabify q r
