diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2011, Joseph Adams
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Joseph Adams nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/Text/PortableLines.hs b/Text/PortableLines.hs
new file mode 100644
--- /dev/null
+++ b/Text/PortableLines.hs
@@ -0,0 +1,30 @@
+module Text.PortableLines
+    ( lines
+    ) where
+
+import Prelude hiding (lines)
+import qualified Prelude as P
+
+-- | Like the 'P.lines' function from Prelude, but treat the @\"\\r\\n\"@ and
+--   @\"\\r\"@ sequences as newlines too, not just @\"\\n\"@.
+lines :: String -> [String]
+lines []  = []
+lines str = let (a, str') = breakNewline str
+             in a : lines str'
+
+breakNewline :: String -> (String, String)
+breakNewline []       = ([], [])
+breakNewline (x : xs) =
+    case x of
+        '\n' -> ([], xs)
+        '\r' -> ([], case xs of
+                         ('\n' : xs') -> xs'
+                         _            -> xs)
+                -- The reason for the weird case expression instead of just a
+                -- ('\r' : '\n' : s) pattern is for better laziness.
+                -- Otherwise, lines ("hello\r" ++ undefined) would fail to
+                -- completely yield the first line.  If we see a '\r', we know
+                -- the line has ended, so don't force the next character
+                -- immediately.
+        _    -> let (line, rest) = breakNewline xs
+                 in (x : line, rest)
diff --git a/Text/PortableLines/ByteString.hs b/Text/PortableLines/ByteString.hs
new file mode 100644
--- /dev/null
+++ b/Text/PortableLines/ByteString.hs
@@ -0,0 +1,31 @@
+module Text.PortableLines.ByteString
+    ( lines8
+    ) where
+
+import Prelude as P hiding (lines)
+
+import Data.ByteString.Char8 (ByteString)
+import Data.ByteString.Unsafe (unsafeIndex, unsafeTail, unsafeDrop)
+
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as B8
+
+-- | Like the 'B8.lines' function from Data.ByteString.Char8, but treat the
+-- @\"\\r\\n\"@ and @\"\\r\"@ sequences as newlines too, not just @\"\\n\"@.
+--
+-- Input is assumed to be in ASCII or an ASCII-compatible encoding (at least
+-- with respect to newline characters).  For example, UTF-8 is fine, but UTF-16
+-- is not.
+lines8 :: ByteString -> [ByteString]
+lines8 str | B.null str = []
+           | otherwise  = let (line, rest) = breakNewline8 str
+                           in line : lines8 rest
+
+breakNewline8 :: ByteString -> (ByteString, ByteString)
+breakNewline8 str =
+    case B.break (\c -> c == 13 || c == 10) str of
+        (line, rest) | B.null rest                      -> (line, rest)
+                     | B.length rest >= 2 &&
+                       rest `unsafeIndex` 0 == 13 &&
+                       rest `unsafeIndex` 1 == 10       -> (line, unsafeDrop 2 rest)
+                     | otherwise                        -> (line, unsafeTail rest)
diff --git a/Text/PortableLines/ByteString/Lazy.hs b/Text/PortableLines/ByteString/Lazy.hs
new file mode 100644
--- /dev/null
+++ b/Text/PortableLines/ByteString/Lazy.hs
@@ -0,0 +1,30 @@
+module Text.PortableLines.ByteString.Lazy
+    ( lines8
+    ) where
+
+import Prelude as P hiding (lines)
+
+import Data.ByteString.Lazy (ByteString)
+
+import qualified Data.ByteString.Lazy as L
+import qualified Data.ByteString.Lazy.Char8 as L8
+
+-- | Like the 'L8.lines' function from Data.ByteString.Lazy.Char8, but treat the
+-- @\"\\r\\n\"@ and @\"\\r\"@ sequences as newlines too, not just @\"\\n\"@.
+--
+-- Input is assumed to be in ASCII or an ASCII-compatible encoding (at least
+-- with respect to newline characters).  For example, UTF-8 is fine, but UTF-16
+-- is not.
+lines8 :: ByteString -> [ByteString]
+lines8 str | L.null str = []
+           | otherwise  = let (line, rest) = breakNewline8 str
+                           in line : lines8 rest
+
+breakNewline8 :: ByteString -> (ByteString, ByteString)
+breakNewline8 str =
+    case L.break (\c -> c == 13 || c == 10) str of
+        (line, rest) | L.null rest                -> (line, rest)
+                     | L.length rest >= 2 &&
+                       rest `L.index` 0 == 13 &&
+                       rest `L.index` 1 == 10     -> (line, L.drop 2 rest)
+                     | otherwise                  -> (line, L.tail rest)
diff --git a/portable-lines.cabal b/portable-lines.cabal
new file mode 100644
--- /dev/null
+++ b/portable-lines.cabal
@@ -0,0 +1,43 @@
+name:                portable-lines
+version:             0.1
+synopsis:            Alternative 'lines' implementation that understands CR-LF and CR
+description:
+    Provides an alternative implementation of the 'Prelude.lines' function that
+    treats the following sequences as newline characters:
+    .
+    * @\"\\r\\n\"@ (Windows style)
+    .
+    * @\"\\r\"@ (Macintosh style, before OS X)
+    .
+    * @\"\\n\"@ (Unix style)
+    .
+    Haskell's IO system supports both platform-specific line ending conversion
+    (by opening the file in \"text mode\"), and explicit setting of the newline
+    mode for input and output (see 'System.IO.hSetNewlineMode').  However:
+    .
+    * If you don't know the line ending format in advance, you would have to
+      open the file and detect the newline format manually.
+    .
+    * ByteString currently honors neither of these; see
+      <http://stackoverflow.com/questions/6837628>.
+
+license:             BSD3
+license-file:        LICENSE
+author:              Joey Adams
+maintainer:          joeyadams3.14159@gmail.com
+copyright:           Copyright (c) Joseph Adams 2011
+category:            Text
+build-type:          Simple
+cabal-version:       >=1.8
+
+source-repository head
+    type:       git
+    location:   git://github.com/joeyadams/haskell-portable-lines.git
+
+library
+    exposed-modules: Text.PortableLines
+                   , Text.PortableLines.ByteString
+                   , Text.PortableLines.ByteString.Lazy
+    build-depends: base == 4.*
+                 , bytestring
+    ghc-options: -Wall -O2
