diff --git a/.gitignore b/.gitignore
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,5 @@
 # stack build artifacts
 .stack-work/
-stack.yaml.lock
 
 # Generated by hasktags
 tags
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,13 +1,15 @@
-Copyright (c) 2008 Dino Morelli
+Copyright (c) 2008, Dino Morelli <dino@ui3.info>
 
-Permission to use, copy, modify, and/or distribute this software for any purpose
-with or without fee is hereby granted, provided that the above copyright notice
-and this permission notice appear in all copies.
+Permission to use, copy, modify, and/or distribute this software
+for any purpose with or without fee is hereby granted, provided
+that the above copyright notice and this permission notice appear
+in all copies.
 
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
-FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
-OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
-TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
-THIS SOFTWARE.
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
+OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -72,7 +72,7 @@
 Binaries
 
 - epub-tools is available for Arch Linux [from the AUR](http://aur.archlinux.org/packages/epub-tools/)
-- Download [binaries for Windows](https://github.com/dino-/epub-tools/releases/download/2.11/epub-tools-2.11-win.zip)
+- Download binary distributions for generic Linux, OSX and Windows [from this directory](https://github.com/dino-/epub-tools/releases/download/3.1)
 
 
 Getting source for development
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,9 @@
+3.1 (2023-09-05)
+
+  * Adjustments to deployment tooling and layout
+  * Replaced license file with a better-formatted version
+
+
 3.0 (2023-07-09)
 
   * Fixed link to Windows binary dist
diff --git a/epub-tools.cabal b/epub-tools.cabal
--- a/epub-tools.cabal
+++ b/epub-tools.cabal
@@ -1,7 +1,7 @@
 cabal-version: 2.2
 
 name:         epub-tools
-version:      3.0
+version:      3.1
 synopsis:     Command line utilities for working with epub files
 description:  A suite of command-line utilities for creating and manipulating epub book files. Included are: epubmeta, epubname, epubzip
 category:     Application, Console
@@ -21,9 +21,10 @@
   doc/INSTALL
   README.md
   stack.yaml
+  stack.yaml.lock
   TODO.md
   util/all-books.hs
-  util/install.hs
+  util/install.sh
   util/win-dist.sh
 
 source-repository head
diff --git a/stack.yaml.lock b/stack.yaml.lock
new file mode 100644
--- /dev/null
+++ b/stack.yaml.lock
@@ -0,0 +1,19 @@
+# This file was autogenerated by Stack.
+# You should not edit this file by hand.
+# For more information, please see the documentation at:
+#   https://docs.haskellstack.org/en/stable/lock_files
+
+packages:
+- completed:
+    hackage: epub-metadata-5.1@sha256:c04142a99c9c437bcef9b4ac7ad681bdbddd0a555583a68ef2cbf83bd49babe1,3460
+    pantry-tree:
+      sha256: 299c53341b5fd1e81d096bd9c9574a51b616224c43bd4d47d711afb92758c1c1
+      size: 3659
+  original:
+    hackage: epub-metadata-5.1
+snapshots:
+- completed:
+    sha256: af5d667f6096e535b9c725a72cffe0f6c060e0568d9f9eeda04caee70d0d9d2d
+    size: 649133
+    url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/12.yaml
+  original: lts-20.12
diff --git a/util/install.hs b/util/install.hs
deleted file mode 100644
--- a/util/install.hs
+++ /dev/null
@@ -1,328 +0,0 @@
-#! /usr/bin/env stack
-{- stack runghc -}
-
-{-# LANGUAGE ScopedTypeVariables #-}
-
-import Control.Exception
-import Control.Monad
-import Data.List
-import Distribution.Package
-import Distribution.PackageDescription hiding ( error, options )
-import Distribution.PackageDescription.Parse
-import Distribution.Verbosity
-import Distribution.Version
-import System.Console.GetOpt
-import System.Directory
-import System.Environment
-import System.Exit
-import System.FilePath
-import System.Process
-import Text.Printf
-import Text.Read
-
-
-defaultOptions :: Options
-defaultOptions = Options
-   { optClean = False
-   , optDelete = False
-   , optHelp = False
-   , optLink = False
-   , optPrefix = "/opt"
-   , optRsrcCpVerbose = True
-   , optInstType = FHS
-   , optVersion = True
-   }
-
-data InstallType = Bundle | FHS deriving Eq
-
-
-main :: IO ()
-main = do
-   -- Parse args
-   (opts, _) <- parseOpts =<< getArgs
-
-   -- User asked for help
-   when (optHelp opts) $ putStrLn usageText >> exitSuccess
-
-   -- Locate cabal file
-   cabalFiles <- (filter $ isSuffixOf ".cabal") <$> getDirectoryContents "."
-
-   when (null cabalFiles) $ do
-      die "Can't continue because no cabal files were found in ."
-
-   -- Parse the cabal file and extract things we need from it
-   -- then pass a pile of what we know to a function to create the
-   -- installation dirs
-   dirs <- constructDirs opts . package . packageDescription
-      <$> readGenericPackageDescription normal (head cabalFiles)
-
-
-   -- Perform the installation
-
-   -- Remove existing install directory
-   appDirExists <- doesDirectoryExist $ appDir dirs
-   when (optDelete opts && appDirExists) $ do
-      putStrLn $ "Removing existing directory " ++ (appDir dirs)
-      removeDirectoryRecursive $ appDir dirs
-
-   -- Clean before building
-   when (optClean opts) $ system "stack clean" >> return ()
-
-   -- Copy the binaries
-   createDirectoryIfMissing True $ binDir dirs
-   installExitCode <- system $ "stack install --local-bin-path=" ++ (binDir dirs)
-   unless (ok installExitCode) $ die "Can't continue because stack install failed"
-
-   -- Copy additional scripts
-   {-
-   putStrLn "Copying additional scripts"
-   mapM_ (\f -> copyFile ("util" </> f) (binDir dirs </> f))
-      [ "script1.sh", "script2.hs" ]
-   -}
-
-   -- Copy the license
-   putStrLn "\nCopying LICENSE"
-   createDirectoryIfMissing True $ docDir dirs
-   copyFile "LICENSE" (docDir dirs </> "LICENSE")
-
-   -- Copy the resources
-   let rsrcDirSrc = "." </> "resources"
-   rsrcsExist <- doesDirectoryExist rsrcDirSrc
-   when rsrcsExist $ do
-      putStrLn $ "\nCopying resources"
-      copyTree (optRsrcCpVerbose opts) rsrcDirSrc (rsrcDir dirs)
-      return ()
-
-   -- Make the symlink
-   when (optLink opts) $ do
-      if (optInstType opts == FHS) then
-         putStrLn "No link will be made because installation type is fhs"
-      else if (not . optVersion $ opts) then
-         putStrLn "No link will be made because the app dir already has no version part"
-      else do
-         printf "Making symbolic link now %s -> %s\n" (linkPath dirs) (appDir dirs)
-         system $ printf "rm %s" (linkPath dirs)
-         system $ printf "ln -s %s %s" (appDir dirs) (linkPath dirs)
-         return ()
-
-   exitSuccess
-
-
-data Dirs = Dirs
-   { appDir :: FilePath
-   , linkPath :: FilePath
-   , binDir :: FilePath
-   , docDir :: FilePath
-   , rsrcDir :: FilePath
-   }
-
-
-constructDirs :: Options -> PackageId -> Dirs
-constructDirs opts pkgId =
-   Dirs appDir' linkPath' binDir' (appDir' </> "doc") (appDir' </> "resources")
-
-   where
-      project = unPackageName . pkgName $ pkgId
-      version = showVersion . pkgVersion $ pkgId
-      versionPart = if optVersion opts then "-" ++ version else ""
-      appDir' = case (optInstType opts) of
-         Bundle -> optPrefix opts </> (project ++ versionPart)
-         FHS    -> optPrefix opts </> "share" </> (project ++ versionPart)
-      linkPath' = optPrefix opts </> project
-      binDir' = case (optInstType opts) of
-         Bundle -> appDir' </> "bin"
-         FHS    -> optPrefix opts </> "bin"
-
-
-{- Turn an exit code (say, from system) into a Bool
--}
-ok :: ExitCode -> Bool
-ok ExitSuccess = True
-ok _           = False
-
-
-{-
-   Argument parsing code
--}
-
-data Options = Options
-   { optClean :: Bool
-   , optDelete :: Bool
-   , optHelp :: Bool
-   , optLink :: Bool
-   , optPrefix :: FilePath
-   , optRsrcCpVerbose :: Bool
-   , optInstType :: InstallType
-   , optVersion :: Bool
-   }
-
-
-instance Read InstallType where
-   readsPrec _ "bundle" = [(Bundle, "")]
-   readsPrec _ "fhs"    = [(FHS, "")]
-   readsPrec _ _        = []
-
-instance Show InstallType where
-   show Bundle = "bundle"
-   show FHS = "fhs"
-
-
-readInstallType :: String -> InstallType
-readInstallType s =
-   case (readEither s) of
-      Left _ -> error $ printf "Can't continue because %s is not a valid install type\n\n%s" s usageText
-      Right t -> t
-
-
-options :: [OptDescr (Options -> Options)]
-options =
-   [ Option ['c'] ["clean"]
-      (NoArg (\opts -> opts { optClean = True } ))
-      ("Do 'stack clean' first." ++ (defaultText . optClean $ defaultOptions))
-   , Option ['C'] ["no-clean"]
-      (NoArg (\opts -> opts { optClean = False } ))
-      ("Do not 'stack clean' first."
-         ++ (defaultText . not . optClean $ defaultOptions))
-   , Option ['d'] ["delete"]
-      (NoArg (\opts -> opts { optDelete = True } ))
-      ("Delete the app directory before copying files."
-         ++ (defaultText . optDelete $ defaultOptions))
-   , Option ['D'] ["no-delete"]
-      (NoArg (\opts -> opts { optDelete = False } ))
-      ("Do not delete the app directory before copying files."
-         ++ (defaultText . not . optDelete $ defaultOptions))
-   , Option ['h'] ["help"]
-      (NoArg (\opts -> opts { optHelp = True } ))
-      "This help information."
-   , Option ['l'] ["link"]
-      (NoArg (\opts -> opts { optLink = True } ))
-      ("Create symlink PROJECT -> PROJECT-VERSION in PREFIX dir. Only useful for bundle installations. Does not work on Windows."
-         ++ (defaultText . optLink $ defaultOptions))
-   , Option ['L'] ["no-link"]
-      (NoArg (\opts -> opts { optLink = True } ))
-      ("Do not create symlink PROJECT -> PROJECT-VERSION in PREFIX dir."
-         ++ (defaultText . not . optLink $ defaultOptions))
-   , Option ['p'] ["prefix"]
-      (ReqArg (\s opts -> opts { optPrefix = s } ) "PREFIX" )
-      (printf "Install prefix directory. Defaults to %s so what you'll end up with is %s/PROJECT-VERSION"
-         (optPrefix defaultOptions) (optPrefix defaultOptions))
-   , Option ['r'] ["resource-copy-verbose"]
-      (NoArg (\opts -> opts { optRsrcCpVerbose = True } ))
-      ("Be chatty when copying the resources directory."
-         ++ (defaultText . optRsrcCpVerbose $ defaultOptions))
-   , Option ['R'] ["no-resource-copy-verbose"]
-      (NoArg (\opts -> opts { optRsrcCpVerbose = False } ))
-      ("Don't be chatty when copying the resources directory. Useful when there are a LOT of resources."
-         ++ (defaultText . not . optRsrcCpVerbose $ defaultOptions))
-   , Option ['t'] ["type"]
-      (ReqArg (\s opts -> opts { optInstType = readInstallType s } ) "INST_TYPE" )
-      (printf "Installation type, see INSTALLATION TYPE below for details. Default: %s"
-         (show . optInstType $ defaultOptions))
-   , Option ['v'] ["version"]
-      (NoArg (\opts -> opts { optVersion = True } ))
-      (printf "Include version in installation path, meaning: %s/PROJECT-VERSION %s"
-         (optPrefix defaultOptions) (defaultText . optVersion $ defaultOptions))
-   , Option ['V'] ["no-version"]
-      (NoArg (\opts -> opts { optVersion = False } ))
-      (printf "Do not include version in installation path, meaning: %s/PROJECT %s"
-         (optPrefix defaultOptions) (defaultText . not . optVersion $ defaultOptions))
-   ]
-
-
-defaultText :: Bool -> String
-defaultText True  = " Default"
-defaultText False = ""
-
-
-parseOpts :: [String] -> IO (Options, [String])
-parseOpts args =
-   case getOpt Permute options args of
-      (o,n,[]  ) -> return (foldl (flip id) defaultOptions o, n)
-      (_,_,errs) -> ioError $ userError (concat errs ++ usageText)
-
-
-usageText :: String
-usageText = (usageInfo header options) ++ "\n" ++ footer
-   where
-      header = init $ unlines
-         [ "Usage: install.hs [OPTIONS]"
-         , ""
-         , "options:"
-         ]
-      footer = init $ unlines
-         [ "INSTALLATION TYPE"
-         , ""
-         , "This is the topology used when copying files, one of: bundle, fhs"
-         , ""
-         , "bundle is sort-of a self-contained structure like this:"
-         , ""
-         , "  $PREFIX/"
-         , "    $PROJECT -> $PROJECT-$VERSION    <-- if --link was specified"
-         , "    $PROJECT-$VERSION/    <-- this is the \"app directory\""
-         , "      bin/..."
-         , "      doc/LICENSE"
-         , "      resources/..."
-         , ""
-         , "fhs is the more traditional UNIX structure like this:"
-         , ""
-         , "  $PREFIX/"
-         , "    bin/..."
-         , "    share/"
-         , "      $PROJECT-$VERSION/  <-- this is the \"app directory\""
-         , "        doc/LICENSE"
-         , "        resources/..."
-         , ""
-         , "Be aware that when the --delete switch is used along with fhs type, the binaries WILL NOT be deleted, only the \"app directory\"."
-         , ""
-         , "COMPILING"
-         , ""
-         , "install.hs was intentionally left as a script, but if you would prefer to compile it, do this:"
-         , ""
-         , "  $ stack ghc -- -o util/install util/install.hs"
-         , ""
-         , ""
-         , "This script is part of the hsinstall package by Dino Morelli <dino@ui3.info>"
-         ]
-
-
-{-
-   Recursive file copying code
-
-   It was desireable to have a standalone recursive file copy in
-   this script for maximum cross-platform compatibility and to
-   avoid Haskell library dependencies.
-
-   Many thanks to [abuzittin gillifirca](https://codereview.stackexchange.com/users/20251/abuzittin-gillifirca) for the StackOverflow post [Copying files in Haskell](https://codereview.stackexchange.com/questions/68908/copying-files-in-haskell) where the following code was lifted.
--}
-
-copyTree :: Bool -> FilePath -> FilePath -> IO ()
-copyTree chatty s t = do
-    createDirectoryIfMissing True t
-    subItems <- getSubitems s
-    mapM_ (copyItem chatty s t) subItems
-
-
-getSubitems :: FilePath -> IO [(Bool, FilePath)]
-getSubitems path = getSubitems' ""
-  where
-    getChildren path =  (\\ [".", ".."]) <$> getDirectoryContents path
-
-    getSubitems' relPath = do
-        let absPath = path </> relPath
-        isDir <- doesDirectoryExist absPath
-        children <- if isDir then getChildren absPath else return []
-        let relChildren = [relPath </> p | p <- children]
-        ((isDir, relPath) :) . concat <$> mapM getSubitems' relChildren
-
-
-copyItem :: Bool -> FilePath -> FilePath -> (Bool, FilePath) -> IO ()
-copyItem chatty baseSourcePath baseTargetPath (isDir, relativePath) = do
-    let sourcePath = baseSourcePath </> relativePath
-    let targetPath = baseTargetPath </> relativePath
-
-    when chatty $
-       putStrLn $ "Copying " ++ sourcePath ++ " to " ++ targetPath
-
-    if isDir
-      then createDirectoryIfMissing False targetPath
-      else copyFile sourcePath targetPath
diff --git a/util/install.sh b/util/install.sh
new file mode 100644
--- /dev/null
+++ b/util/install.sh
@@ -0,0 +1,15 @@
+#! /usr/bin/env bash
+
+set -e
+
+projectName="epub-tools"
+version=$(awk '/^[Vv]ersion/ { print $2 }' "${projectName}.cabal")
+
+PREFIX=${PREFIX:-"${projectName}-${version}"}
+docDir="$PREFIX/usr/share/doc/${projectName}"
+licenseDir="$PREFIX/usr/share/licenses/${projectName}"
+
+stack install --local-bin-path="$PREFIX/usr/bin"
+install -Dm0644 "LICENSE" "$licenseDir/LICENSE"
+install -Dm0644 "README.md" "$docDir/README.md"
+install -Dm0644 "changelog.md" "$docDir/changelog.md"
diff --git a/util/win-dist.sh b/util/win-dist.sh
--- a/util/win-dist.sh
+++ b/util/win-dist.sh
@@ -1,12 +1,14 @@
-#! /bin/bash
+#! /usr/bin/env bash
 
-version=$(awk '/^[Vv]ersion/ { print $2 }' epub-tools.cabal)
+projectName="epub-tools"
+version=$(awk '/^[Vv]ersion/ { print $2 }' "${projectName}.cabal")
 
-buildDir="dist"
+buildDir="win-dist"
 
-./util/install.hs -p $buildDir -t bundle
+PREFIX="$buildDir" ./util/install.sh
+install -Dm0644 doc/INSTALL "${buildDir}/usr/share/doc/${projectName}/INSTALL"
 
-zipFile="epub-tools-$version-win.zip"
+zipFile="${projectName}-${version}-win.zip"
 
 rm $zipFile
 
