{- 
Beispielprogramm Haskell File-I/O, "FileIO.hs"

Das Programm liest und vergleicht zwei Dateien und schreibt 
die unterschiedlichen Zeilen in eine dritte oder auf die Konsole.

Autor: Jost Berthold
-}

module Main where

import System(getArgs, getProgName)
import Monad(when)
import IO -- alle Operationen mit Handles

---------------------------------------------------------------------------
-- Gebrauchsanweisung
usage :: String -> String
usage name = "Beispielprogramm zur Datei- Ein- und Ausgabe\n" ++
	     " Benutzung: " ++ name ++ " <Datei 1> <Datei 2> [Ausgabedatei]" 

-- main Funktion
main :: IO()
main = do 
   args <- getArgs
   if length args < 2 
      then do -- Gebrauchsanweisung ausgeben
	    name <- getProgName
	    putStrLn (usage name)
      else -- Dateien öffnen (Handles), Abbruch falls Fehler:
           do 
	    maybef1 <- openCatchNonExist (args!!0)
	    if maybef1 == Nothing 
	      then putStrLn "Programm abgebrochen"
	      else do
		    let (Just f1) = maybef1
		    maybef2 <- openCatchNonExist (args!!1)
		    if maybef2 == Nothing 
		      then do 
			    hClose f1
			    putStrLn "Programm abgebrochen"
		      else do
			    let (Just f2) = maybef2
			    outHdl <- if length args == 2 
		 	                 then return stdout -- vordefiniert, bereits offen
	       				 else -- Datei zum Schreiben öffnen
					      openFile (args!!2) WriteMode 
			    -- Header schreiben:
			    hPutStrLn outHdl ("\t\tUnterschiedliche Zeilen\n" ++ 
					   "\t<: in "++ (args!!0) ++ "\n\t>: in " ++ (args!!1))
                            -- ganze Datei 1 lesen, hGetContents :: Handle -> IO String
			    str1 <- hGetContents f1
			    cmpFiles (lines str1) f2 outHdl

-- rekursiv: vergleiche Zeile aus Argument 1 mit Zeile, die aus handle gelesen wird.
cmpFiles :: [String] -> Handle -> Handle -> IO ()
-- Datei 1 komplett gelesen:
cmpFiles [] f2 out = do 
		      ende <- hIsEOF f2
		      if ende -- Datei 2 auch am Ende
			 then return () -- fertig
			 else do -- sonst: schreibe eine Zeile in <out>
				l <- hGetLine f2
				hPutStrLn out (">: " ++ l)
				cmpFiles [] f2 out
-- noch Zeilen aus Datei 1 übrig:
cmpFiles (l:ls) f2 out = do 
			  ende <- hIsEOF f2 
			  if ende -- Datei 2 am Ende? 
			     then -- Rest von Datei 1 in <out> schreiben
			      -- do 
                              --  hPutStrLn out l
			      --  cmpFiles ls f2 out
                              -- oder schneller: kein weiterer Test mehr
				 mapM_ (hPutStrLn out) 
					(map ("<: " ++ )  (l:ls))
			     else -- Datei 2 noch nicht am Ende: 
			          do
			      -- Zeile aus Datei 2 lesen und mit 1 vergleichen
			      l2 <- hGetLine f2
			      when (l /= l2) -- nur ausführen, falls Test True ergibt
				   ( do 
				       hPutStrLn out ("<: " ++ l)
				       hPutStrLn out (">: " ++ l2)
				     )
			      cmpFiles ls f2 out

openCatchNonExist :: FilePath ->  IO (Maybe Handle)
openCatchNonExist file = catch (openFile file ReadMode >>= \h -> return (Just h))
                                        (\e -> if isDoesNotExistError e 
                                                  then 
                                                    putStrLn (file ++ ": File does not exist.") 
                                                        >> return Nothing
                                                  else ioError e)

