packages feed

epub-tools 1.0.0.1 → 1.1.0

raw patch · 35 files changed

+856/−1104 lines, 35 files

Files

epub-tools.cabal view
@@ -1,6 +1,6 @@ name:                epub-tools cabal-version:       >= 1.8-version:             1.0.0.1+version:             1.1.0 build-type:          Simple license:             BSD3 license-file:        LICENSE@@ -41,31 +41,8 @@    build-depends:    base >= 3 && < 5, directory, epub-metadata >= 2.2,                       mtl, regex-compat    hs-source-dirs:   src-   other-modules:    EpubTools.EpubName.Format.Anonymous-                     EpubTools.EpubName.Format.AuthorBasic-                     EpubTools.EpubName.Format.AuthorDouble-                     EpubTools.EpubName.Format.AuthorSt-                     EpubTools.EpubName.Format.AuthorThird-                     EpubTools.EpubName.Format.MagAeon-                     EpubTools.EpubName.Format.MagAnalog-                     EpubTools.EpubName.Format.MagApex-                     EpubTools.EpubName.Format.MagBcs-                     EpubTools.EpubName.Format.MagChallengingDestiny-                     EpubTools.EpubName.Format.MagClarkesworld-                     EpubTools.EpubName.Format.MagEclipse-                     EpubTools.EpubName.Format.MagFsf-                     EpubTools.EpubName.Format.MagFutureOrbits-                     EpubTools.EpubName.Format.MagGud-                     EpubTools.EpubName.Format.MagInterzone-                     EpubTools.EpubName.Format.MagLightspeed-                     EpubTools.EpubName.Format.MagNameIssue-                     EpubTools.EpubName.Format.MagNemesis-                     EpubTools.EpubName.Format.MagRageMachine-                     EpubTools.EpubName.Format.MagSomethingWicked-                     EpubTools.EpubName.Format.MagUniverse-                     EpubTools.EpubName.Format.SFBestOf-                     EpubTools.EpubName.Formatters-                     EpubTools.EpubName.Format.Util+   other-modules:    EpubTools.EpubName.Author+                     EpubTools.EpubName.Format                      EpubTools.EpubName.Opts                      EpubTools.EpubName.Util 
src/EpubTools/EpubMeta/Opts.hs view
@@ -103,5 +103,5 @@          , ""          , "For more information please see the IDPF OPF specification found here: http://idpf.org/epub/20/spec/OPF_2.0.1_draft.htm"          , ""-         , "Version 1.0.0.1  Dino Morelli <dino@ui3.info>"+         , "Version 1.1.0  Dino Morelli <dino@ui3.info>"          ]
+ src/EpubTools/EpubName/Author.hs view
@@ -0,0 +1,118 @@+-- Copyright: 2008-2011 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++module EpubTools.EpubName.Author+   ( extractAuthors+   , authorMatches+   )+   where++import Codec.Epub.Opf.Package+import Control.Monad+import Data.List ( intercalate )+import Data.Maybe ( isJust )+import Prelude hiding ( last )+import Text.Printf+import Text.Regex++import EpubTools.EpubName.Util+++extractAuthors :: Metadata -> String+extractAuthors = fmtAuthor . separateCombined . justAuthors+   where+      fmtAuthor []  = ""+      fmtAuthor [c] = formatSingleAuthor c+      fmtAuthor cs = (formatMultiAuthors cs) ++ "-"+++separateCombined :: [MetaCreator] -> [MetaCreator]+separateCombined = concatMap separateOne+++separateOne :: MetaCreator -> [MetaCreator]+separateOne c@(MetaCreator r _ di) =+   case (splitRegex (mkRegex " (&|and) ") di) of+      -- If there was only one, leave the file-as alone!+      [_] -> [c]+      -- Otherwise, explode them into separate MetaCreatorS+      ss  -> map (MetaCreator r Nothing) ss+++formatSingleAuthor :: MetaCreator -> String++formatSingleAuthor (MetaCreator _ (Just fa) di) = +   if ((fa == di) && all (/= ',') fa)+      then formatSingleAuthor $ MetaCreator Nothing Nothing di+      else authorSingle [fa]++formatSingleAuthor (MetaCreator _ _         di) = +   authorSingle . reverse . nameParts $ di+++nameParts :: String -> [String]+nameParts s = maybe [] id $ foldl mplus Nothing matches+   where+      matches =+         [ matchRegex (mkRegex "(.*) ([^ ]+)$") s+         , matchRegex (mkRegex "(.*)") s+         ]+++lastName' :: String -> String+lastName' s = maybe "" head $ foldl mplus Nothing matches+   where+      matches =+         [ matchRegex (mkRegex "(.*),.*") s+         , matchRegex (mkRegex ".* (.*)") s+         , matchRegex (mkRegex "(.*)") s+         ]++lastName :: MetaCreator -> String+lastName (MetaCreator _ (Just fa) _ ) = lastName' fa+lastName (MetaCreator _ _         di) = lastName' di+++formatMultiAuthors :: [MetaCreator] -> String+formatMultiAuthors = (intercalate "_") . (map lastName)+++justAuthors :: Metadata -> [MetaCreator]+justAuthors = (filter isAut) . metaCreators+   where+      isAut (MetaCreator (Just "aut") _ _) = True+      isAut (MetaCreator Nothing      _ _) = True+      isAut _                              = False+++{- A common simple formatter for many book authors+-}+authorSingle :: [String] -> String+authorSingle (last:rest:_) =+   printf "%s%s-" (filterCommon last) (filterCommon rest)+authorSingle [n]           = printf "%s-" $ filterCommon n+authorSingle _             = undefined+++{- Throws an error if no author matches the pattern+-}+authorMatches :: Metadata -> String -> EN ()+authorMatches md re = do+   let authorMatches' (MetaCreator _ _ di) =+         matchRegex (mkRegex re) di++   unless (any isJust $ map authorMatches' $ justAuthors md) $+      throwError "Specific author string not found"+++{- Author names with a postfix like II, III, Jr. or Sr.+   FIXME: Is anyone calling this?+-}+{-+authorPostfix :: [String] -> String+authorPostfix (rest:last:postfix:_) =+   printf "%s%s%s-" (filterCommon last) (filterCommon rest)+      (filterCommon postfix)+authorPostfix _             = undefined+-}
+ src/EpubTools/EpubName/Format.hs view
@@ -0,0 +1,397 @@+-- Copyright: 2008-2011 Dino Morelli+-- License: BSD3 (see LICENSE)+-- Author: Dino Morelli <dino@ui3.info>++module EpubTools.EpubName.Format+   ( tryFormatting+   )+   where++import Codec.Epub.Opf.Package+import Control.Monad.Error+import Data.List ( isPrefixOf )+import Text.Printf+import Text.Regex++import EpubTools.EpubName.Author+import EpubTools.EpubName.Opts+import EpubTools.EpubName.Util+++formatters :: [Metadata -> EN (String, [String])]+formatters =+   [ magAeon+   , magAnalog+   , magApex+   , magBcs+   , magBlackStatic+   , magChallengingDestiny+   , magClarkesworld+   , magEclipse+   , magFantasyMag+   , magFsf+   , magFutureOrbits+   , magGud+   , magInterzone+   , magLightspeedDate+   , magLightspeedIssue+   , magNemesis+   , magRageMachine+   , magSomethingWicked+   , magWeirdTales+   , magUniverse+   , compOfTheYear+   , compYearsBest+   , book   -- Kind of always want this one last as a catch-all+   ]+++magAeon :: Metadata -> EN (String, [String])+magAeon md = do+   (issue:_) <- extractTitle md "^A[eE]on ([^ ]+)$"+   let title = printf "AeonMagazine%s" (wordNum issue)++   return ("magAeon", [title])+++magAnalog :: Metadata -> EN (String, [String])+magAnalog md = do+   (prefix:month:year:_) <-+      extractTitle md "^(A[^ ]*).*, ([^ ]+) ([0-9]{4})$"++   let title = printf "%sSF%s-%s"+         (filterCommon prefix) year (monthNum month)++   return ("magAnalog", [title])+++magApex :: Metadata -> EN (String, [String])+magApex md = do+   (prefix:issue:_) <- extractTitle md "^(Apex)[^0-9]*([0-9]+)$"+   let title = printf "%sMagazine%03d"+         (filterCommon prefix) (read issue :: Int)++   return ("magApex", [title])+++magBcs :: Metadata -> EN (String, [String])+magBcs md = do+   (prefix:issue:_) <-+      extractTitle md "(Beneath Ceaseless.*) #([0-9]+).*"+   let title = printf "%s_Issue%03d"+         (filterCommon prefix) (read issue :: Int)++   return ("magBcs", [title])+++magBlackStatic :: Metadata -> EN (String, [String])+magBlackStatic md = do+   (prefix:issue:_) <-+      extractTitle md "^(Black Static Horror Magazine)[^0-9]*([0-9]+)$"+   let title = printf "%s%02d" (filterCommon prefix) (read issue :: Int)++   return ("magBlackStatic", [title])+++magChallengingDestiny :: Metadata -> EN (String, [String])+magChallengingDestiny md = do+   (prefix:issue:_) <- extractTitle md "^(Challenging Destiny) #([0-9]+).*"+   let title = printf "%sMagazine%03d"+         (filterCommon prefix) (read issue :: Int)++   return ("magChallengingDestiny", [title])+++magClarkesworld :: Metadata -> EN (String, [String])+magClarkesworld md = do+   (prefix:issue:_) <-+      extractTitle md "^(Clarkesworld)[^0-9]*([0-9]+)$"+   let title = printf "%s%03d" prefix (read issue :: Int)++   return ("magClarkesworld", [title])+++magEclipse :: Metadata -> EN (String, [String])+magEclipse md = do+   (prefix:issue:_) <-+      extractTitle md "^(Eclipse) ([^ ]+)$"+   let title = printf "%s%s" prefix (wordNum issue)++   return ("magEclipse", [title])+++magFantasyMag :: Metadata -> EN (String, [String])+magFantasyMag md = do+   (prefix:issue:_) <-+      extractTitle md "^(Fantasy Magazine)[^0-9]+([0-9]+).*"+   let title = printf "%s%03d" (filterCommon prefix) (read issue :: Int)++   return ("magFantasyMag", [title])+++magFsf :: Metadata -> EN (String, [String])+magFsf md = do+   authorMatches md "Spilogale"++   (month:year:_) <- extractTitle md ".* ([^ ]+) ([0-9]{4})$"+   let title = printf "FantasyScienceFiction%s-%s"+         year (monthNum month)++   return ("magFsf", [title])+++magFutureOrbits :: Metadata -> EN (String, [String])+magFutureOrbits md = do+   (prefix:issue:month:year:_) <- extractTitle md+      "(Future Orbits) Issue ([0-9]+), ([^ ]+) ([0-9]{4})$"+   let title = printf "%sMagazine%02d_%s-%s" (filterCommon prefix)+         (read issue :: Int) year (monthNum month)++   return ("magFutureOrbits", [title])+++magGud :: Metadata -> EN (String, [String])+magGud md = do+   authorMatches md "GUD Magazine Authors"++   (issue:_) <-+      extractTitle md ".* Magazine Issue ([0-9]+) ::.*"+   let title = printf "GUDMagazine%02d" (read issue :: Int)++   return ("magGud", [title])+++magInterzone :: Metadata -> EN (String, [String])+magInterzone md = do+   (prefix:issue:_) <-+      extractTitle md "^(Interzone)[^0-9]*([0-9]+)$"+   let title = printf "%sSFF%03d" prefix (read issue :: Int)++   return ("magInterzone", [title])+++magLightspeedDate :: Metadata -> EN (String, [String])+magLightspeedDate md = do+   (prefix:month:year:_) <-+      extractTitle md "(Lightspeed) Magazine, (.*) (.*)"+   let title = printf "%s%s-%s"+         prefix year (monthNum month)++   return ("magLightspeedDate", [title])+++magLightspeedIssue :: Metadata -> EN (String, [String])+magLightspeedIssue md = do+   (prefix:issue:_) <-+      extractTitle md "(Lightspeed) Magazine Issue (.*)"+   let title = printf "%s%03d" prefix (read issue :: Int)++   return ("magLightspeedIssue", [title])+++magNemesis :: Metadata -> EN (String, [String])+magNemesis md = do+   (prefix:issue:_) <-+      extractTitle md "(Nemesis Mag)azine #([0-9]+).*"+   let title = printf "%s%03d" (filterCommon prefix) (read issue :: Int)++   return ("magNemesis", [title])+++magRageMachine :: Metadata -> EN (String, [String])+magRageMachine md = do+   (prefix:month:year:_) <-+      extractTitle md "(Rage Machine.*)--([^ ]+) ([0-9]{4})$"+   let title = printf "%s_%s-%s"+         (filterCommon prefix) year (monthNum month)++   return ("magRageMachine", [title])+++magSomethingWicked :: Metadata -> EN (String, [String])+magSomethingWicked md = do+   (prefix:issue:_) <- extractTitle md "^(Something Wicked)[^0-9]*([0-9]+)"+   let title = printf "%s%03d"+         (filterCommon prefix) (read issue :: Int)++   return ("magSomethingWicked", [title])+++magUniverse :: Metadata -> EN (String, [String])+magUniverse md = do+   (prefix:vol:num:_) <-+      extractTitle md "^(Jim Baen's Universe)-Vol ([^ ]+) Num ([^ ]+)"+   let title = printf "%sVol%02dNum%02d" (filterCommon prefix)+         (read vol :: Int) (read num :: Int)++   return ("magUniverse", [title])+++magWeirdTales :: Metadata -> EN (String, [String])+magWeirdTales md = do+   (prefix:issue:_) <- extractTitle md "^(Weird Tales)[^0-9]*([0-9]+)$"+   let title = printf "%s%03d"+         (filterCommon prefix) (read issue :: Int)++   return ("magWeirdTales", [title])+++compOfTheYear :: Metadata -> EN (String, [String])+compOfTheYear md = do+   (title:_) <- extractTitle md "(.*of the Year.*)"++   return ("compOfTheYear", [filterCommon title])+++compYearsBest :: Metadata -> EN (String, [String])+compYearsBest md = do+   (title:_) <- extractTitle md "(.*Year's Best.*)"++   return ("compYearsBest", [filterCommon title])+++book :: Metadata -> EN (String, [String])+book md = do+   (title:_) <- extractTitle md "(.*)"+   year <- extractYear md++   return ("book", [extractAuthors md, filterCommon title, year])+++tryFormatting :: FilePath -> Metadata -> EN (String, String)+tryFormatting oldPath md = do+   foldr mplus+      (throwError $ printf "%s [ERROR No formatter found]" oldPath) $+      map (\f -> f md >>= finish md) formatters+++finish :: Metadata -> (String, [String]) -> EN (String, String)+finish md (label, parts) = do+   publisher <- fmap (extractPublisher md) $ asks optPublisher+   return ( label+      , foldr1 (++) (parts ++ [publisher, ".epub"]))+++extractTitle :: Metadata -> String -> EN [String]+extractTitle md re = do+   (MetaTitle _ oldTitle) <- case metaTitles md of+      [] -> throwError "format failed, no title present"+      ts -> return . head $ ts++   case matchRegex (mkRegex re) oldTitle of+      Just matches -> return matches+      Nothing      -> throwError $ printf "extract title failed: %s" re+++{- Look for a date tag with event="original-publication" in the+   metadata+-}+extractYear :: Metadata -> EN String+extractYear md = do+   inclY <- asks optYear+   return . maybe "" ('_' :) $+      (foldr mplus Nothing (map (maybeYear inclY) $ metaDates md))++   where+      maybeYear True (MetaDate (Just "original-publication") d) = Just d+      maybeYear _    _                                          = Nothing+++extractPublisher :: Metadata -> Bool -> String+extractPublisher _  False = ""+extractPublisher md True  = maybe "" ('_' :)+   (foldr mplus Nothing (map maybePub $ metaContributors md))++   where+      maybePub (MetaCreator (Just "bkp") (Just fa) _ ) = Just fa+      maybePub _                                       = Nothing+++{- Convert an English month name (with creative ranges and variations)+   into number form+-}+monthNum :: String -> String+monthNum x+   | isPrefixOf x "January"   = "01"+monthNum "January-February"   = "01_02"+monthNum "January/February"   = "01_02"+monthNum "Jan-Feb"            = "01_02"+monthNum "Jan/Feb"            = "01_02"+monthNum x+   | isPrefixOf x "February"  = "02"+monthNum x+   | isPrefixOf x "March"     = "03"+monthNum "March-April"        = "03_04"+monthNum "March/April"        = "03_04"+monthNum "Mar-Apr"            = "03_04"+monthNum "Mar/Apr"            = "03_04"+monthNum x+   | isPrefixOf x "April"     = "04"+monthNum "April-May"          = "04_05"+monthNum "April/May"          = "04_05"+monthNum "Apr-May"            = "04_05"+monthNum "Apr/May"            = "04_05"+monthNum "May"                = "05"+monthNum "May-June"           = "05_06"+monthNum "May/June"           = "05_06"+monthNum "May-Jun"            = "05_06"+monthNum "May/Jun"            = "05_06"+monthNum x+   | isPrefixOf x "June"      = "06"+monthNum "June/July"          = "06_07"+monthNum "June-July"          = "06_07"+monthNum "Jun-Jul"            = "06_07"+monthNum "Jun/Jul"            = "06_07"+monthNum x+   | isPrefixOf x "July"      = "07"+monthNum "July-August"        = "07_08"+monthNum "July/August"        = "07_08"+monthNum "Jul-Aug"            = "07_08"+monthNum "Jul/Aug"            = "07_08"+monthNum x+   | isPrefixOf x "August"    = "08"+monthNum "August-September"   = "08_09"+monthNum "August/September"   = "08_09"+monthNum "Aug-Sep"            = "08_09"+monthNum "Aug/Sep"            = "08_09"+monthNum x+   | isPrefixOf x "September" = "09"+monthNum "September-October"  = "09_10"+monthNum x+   | isPrefixOf x "October"   = "10"+monthNum "October-November"   = "10_11"+monthNum "October/November"   = "10_11"+monthNum "Oct-Nov"            = "10_11"+monthNum "Oct/Nov"            = "10_11"+monthNum "November-December"  = "11_12"+monthNum x+   | isPrefixOf x "November"  = "11"+monthNum x+   | isPrefixOf x "December"  = "12"+monthNum x                    = "[ERROR monthNum " ++ x ++ "]"+++{- Convert an English word for a number into number form+-}+wordNum :: String -> String+wordNum "One"       = "01"+wordNum "Two"       = "02"+wordNum "Three"     = "03"+wordNum "Four"      = "04"+wordNum "Five"      = "05"+wordNum "Six"       = "06"+wordNum "Seven"     = "07"+wordNum "Eight"     = "08"+wordNum "Nine"      = "09"+wordNum "Ten"       = "10"+wordNum "Eleven"    = "11"+wordNum "Twelve"    = "12"+wordNum "Thirteen"  = "13"+wordNum "Fourteen"  = "14"+wordNum "Fifteen"   = "15"+wordNum "Sixteen"   = "16"+wordNum "Seventeen" = "17"+wordNum "Eighteen"  = "18"+wordNum "Nineteen"  = "19"+wordNum "Twenty"    = "20"+wordNum x           = "[ERROR wordNum " ++ x ++ "]"
− src/EpubTools/EpubName/Format/Anonymous.hs
@@ -1,23 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--{-# LANGUAGE FlexibleContexts #-}--module EpubTools.EpubName.Format.Anonymous-   ( fmtAnonymous )-   where--import Control.Monad.Error--import EpubTools.EpubName.Format.Util-   ( format-   , authorSingle , titleSimple-   )-import EpubTools.EpubName.Util ( Fields )---fmtAnonymous :: (MonadError String m) => Fields -> m (String, String)-fmtAnonymous = format "Anonymous"-   "(.*)(Anonymous)" authorSingle-   "(.*)" titleSimple
− src/EpubTools/EpubName/Format/AuthorBasic.hs
@@ -1,34 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.AuthorBasic-   ( fmtAuthorBasic )-   where--import Codec.Epub.Opf.Package.Metadata--import EpubTools.EpubName.Format.Util-   ( format-   , author, titleSimple-   )-import EpubTools.EpubName.Util---fmtAuthorBasic :: Metadata -> EN (String, String)-fmtAuthorBasic = format "AuthorBasic"-   ".*" author-   "(.*)" titleSimple---{--authorPatterns :: [(String, [String] -> String)]-authorPatterns =-   [ ( ".* ([^ ]+) and .* ([^ ]+)", authorDouble )-   , ( "(.*)(Anonymous)", authorSingle )-   , ( "(.*) ([^ ]+ III)$", authorSingle )-   , ( "(.*) ([^ ]+ Jr\\.)$", authorSingle )-   , ( "(.*) (St\\. [^ ]+)$", authorSingle )-   , ( "(.*) ([^ ]+)$", authorSingle )-   ]--}
− src/EpubTools/EpubName/Format/AuthorDouble.hs
@@ -1,34 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.AuthorDouble-   ( fmtAuthorDouble )-   where--import Codec.Epub.Opf.Package.Metadata-import Data.Maybe ( fromJust )-import Prelude hiding ( last )-import Text.Printf-import Text.Regex--import EpubTools.EpubName.Format.Util ( filterCommon, format, justAuthors, -   titleSimple )-import EpubTools.EpubName.Util---fmtAuthorDouble :: Metadata -> EN (String, String)-fmtAuthorDouble = format "AuthorDouble"-   ".* and .*" authorDouble-   "(.*)" titleSimple---authorDouble :: Metadata -> String-authorDouble = fmtAuthors . extractParts . head . justAuthors-   where-      extractParts (MetaCreator _ _ di) = fromJust . -         matchRegex (mkRegex ".* ([^ ]+) and .* ([^ ]+)") $ di--      fmtAuthors (last1:last2:_) = -         printf "%s_%s-" (filterCommon last1) (filterCommon last2)-      fmtAuthors _ = undefined
− src/EpubTools/EpubName/Format/AuthorSt.hs
@@ -1,23 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--{-# LANGUAGE FlexibleContexts #-}--module EpubTools.EpubName.Format.AuthorSt-   ( fmtAuthorSt )-   where--import Control.Monad.Error--import EpubTools.EpubName.Format.Util-   ( format-   , authorSingle, titleSimple-   )-import EpubTools.EpubName.Util ( Fields )---fmtAuthorSt :: (MonadError String m) => Fields -> m (String, String)-fmtAuthorSt = format "AuthorSt"-   "(.*) (St\\. [^ ]+)$" authorSingle-   "(.*)" titleSimple
− src/EpubTools/EpubName/Format/AuthorThird.hs
@@ -1,23 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--{-# LANGUAGE FlexibleContexts #-}--module EpubTools.EpubName.Format.AuthorThird-   ( fmtAuthorThird )-   where--import Control.Monad.Error--import EpubTools.EpubName.Format.Util-   ( format-   , authorPostfix, titleSimple-   )-import EpubTools.EpubName.Util ( Fields )---fmtAuthorThird :: (MonadError String m) => Fields -> m (String, String)-fmtAuthorThird = format "AuthorThird"-   "(.*) ([^ ]+) (III)$" authorPostfix-   "(.*)" titleSimple
− src/EpubTools/EpubName/Format/MagAeon.hs
@@ -1,45 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.MagAeon-   ( fmtMagAeon )-   where--import Codec.Epub.Opf.Package.Metadata--import EpubTools.EpubName.Format.Util ( format )-import EpubTools.EpubName.Util---fmtMagAeon :: Metadata -> EN (String, String)-fmtMagAeon = format "MagAeon"-   ".* Authors" (const "")-   "^A[eE]on ([^ ]+)$" titleMagAeon---titleMagAeon :: String -> [String] -> String-titleMagAeon _ (numWord:_) = "AeonMagazine" ++ (num numWord)-   where-      num "One"       = "01"-      num "Two"       = "02"-      num "Three"     = "03"-      num "Four"      = "04"-      num "Five"      = "05"-      num "Six"       = "06"-      num "Seven"     = "07"-      num "Eight"     = "08"-      num "Nine"      = "09"-      num "Ten"       = "10"-      num "Eleven"    = "11"-      num "Twelve"    = "12"-      num "Thirteen"  = "13"-      num "Fourteen"  = "14"-      num "Fifteen"   = "15"-      num "Sixteen"   = "16"-      num "Seventeen" = "17"-      num "Eighteen"  = "18"-      num "Nineteen"  = "19"-      num "Twenty"    = "20"-      num x           = "[ERROR titleMagAeon " ++ x ++ "]"-titleMagAeon _ _      = undefined
− src/EpubTools/EpubName/Format/MagAnalog.hs
@@ -1,25 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.MagAnalog-   ( fmtMagAnalog )-   where--import Codec.Epub.Opf.Package.Metadata-import Text.Printf--import EpubTools.EpubName.Format.Util ( filterCommon, format, monthNum )-import EpubTools.EpubName.Util---fmtMagAnalog :: Metadata -> EN (String, String)-fmtMagAnalog = format "MagAnalog"-   "Dell Magazine.*" (const "")-   "([^ ]*).*, ([^ ]+) ([0-9]{4})$" title---title :: String -> [String] -> String-title _ (prefix:month:year:_) =-   printf "%sSF%s-%s" (filterCommon prefix) year (monthNum month)-title _ _ = undefined
− src/EpubTools/EpubName/Format/MagApex.hs
@@ -1,25 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.MagApex-   ( fmtMagApex )-   where--import Codec.Epub.Opf.Package.Metadata-import Text.Printf--import EpubTools.EpubName.Format.Util ( format )-import EpubTools.EpubName.Util---fmtMagApex :: Metadata -> EN (String, String)-fmtMagApex = format "MagApex"-   ".*" (const "")-   "^Apex[^0-9]*([0-9]+)$" title---title :: String -> [String] -> String-title _ (issue:_) =-   printf "ApexMagazine%03d" (read issue :: Int)-title _ _         = undefined
− src/EpubTools/EpubName/Format/MagBcs.hs
@@ -1,25 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.MagBcs-   ( fmtMagBcs )-   where--import Codec.Epub.Opf.Package.Metadata-import Text.Printf--import EpubTools.EpubName.Format.Util ( filterCommon, format )-import EpubTools.EpubName.Util---fmtMagBcs :: Metadata -> EN (String, String)-fmtMagBcs = format "MagBcs"-   ".*" (const "")-   "(Beneath Ceaseless.*) #([0-9]+).*" title---title :: String -> [String] -> String-title _ (name:issue:_) =-   printf "%s_Issue%03d" (filterCommon name) (read issue :: Int)-title _ _              = undefined
− src/EpubTools/EpubName/Format/MagChallengingDestiny.hs
@@ -1,25 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.MagChallengingDestiny-   ( fmtMagChallengingDestiny )-   where--import Codec.Epub.Opf.Package.Metadata-import Text.Printf--import EpubTools.EpubName.Format.Util ( filterCommon, format )-import EpubTools.EpubName.Util---fmtMagChallengingDestiny :: Metadata -> EN (String, String)-fmtMagChallengingDestiny = format "MagChallengingDestiny"-   "Crystalline Sphere .*" (const "")-   "(Challenging Destiny) #([0-9]+).*" title---title :: String -> [String] -> String-title _ (name:issue:_) =-   printf "%sMagazine%03d" (filterCommon name) (read issue :: Int)-title _ _              = undefined
− src/EpubTools/EpubName/Format/MagClarkesworld.hs
@@ -1,25 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.MagClarkesworld-   ( fmtMagClarkesworld )-   where--import Codec.Epub.Opf.Package.Metadata-import Text.Printf--import EpubTools.EpubName.Format.Util ( format )-import EpubTools.EpubName.Util---fmtMagClarkesworld :: Metadata -> EN (String, String)-fmtMagClarkesworld = format "MagClarkesworld"-   ".*" (const "")-   "^(Clarkesworld)[^0-9]*([0-9]+)$" title---title :: String -> [String] -> String-title _ (name:issue:_) =-   printf "%s%03d" name (read issue :: Int)-title _ _              = undefined
− src/EpubTools/EpubName/Format/MagEclipse.hs
@@ -1,45 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.MagEclipse-   ( fmtMagEclipse )-   where--import Codec.Epub.Opf.Package.Metadata--import EpubTools.EpubName.Format.Util ( format )-import EpubTools.EpubName.Util---fmtMagEclipse :: Metadata -> EN (String, String)-fmtMagEclipse = format "MagEclipse"-   ".*" (const "")-   "^(Eclipse) ([^ ]+)$" titleMagEclipse---titleMagEclipse :: String -> [String] -> String-titleMagEclipse _ (magName:numWord:_) = magName ++ (num numWord)-   where-      num "One"       = "01"-      num "Two"       = "02"-      num "Three"     = "03"-      num "Four"      = "04"-      num "Five"      = "05"-      num "Six"       = "06"-      num "Seven"     = "07"-      num "Eight"     = "08"-      num "Nine"      = "09"-      num "Ten"       = "10"-      num "Eleven"    = "11"-      num "Twelve"    = "12"-      num "Thirteen"  = "13"-      num "Fourteen"  = "14"-      num "Fifteen"   = "15"-      num "Sixteen"   = "16"-      num "Seventeen" = "17"-      num "Eighteen"  = "18"-      num "Nineteen"  = "19"-      num "Twenty"    = "20"-      num x           = "[ERROR titleMagEclipse " ++ x ++ "]"-titleMagEclipse _ _      = undefined
− src/EpubTools/EpubName/Format/MagFsf.hs
@@ -1,25 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.MagFsf-   ( fmtMagFsf )-   where--import Codec.Epub.Opf.Package.Metadata-import Text.Printf--import EpubTools.EpubName.Format.Util ( format, monthNum )-import EpubTools.EpubName.Util---fmtMagFsf :: Metadata -> EN (String, String)-fmtMagFsf = format "MagFsf"-   "Spilogale.*" (const "")-   ".* ([^ ]+) ([0-9]{4})$" title---title :: String -> [String] -> String-title _ (month:year:_) =-   printf "FantasyScienceFiction%s-%s" year (monthNum month)-title _ _ = undefined
− src/EpubTools/EpubName/Format/MagFutureOrbits.hs
@@ -1,26 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.MagFutureOrbits-   ( fmtMagFutureOrbits )-   where--import Codec.Epub.Opf.Package.Metadata-import Text.Printf--import EpubTools.EpubName.Format.Util ( filterCommon, format, monthNum )-import EpubTools.EpubName.Util---fmtMagFutureOrbits :: Metadata -> EN (String, String)-fmtMagFutureOrbits = format "MagFutureOrbits"-   ".*" (const "")-   "(Future Orbits) Issue ([0-9]+), ([^ ]+) ([0-9]{4})$" title---title :: String -> [String] -> String-title _ (name:issue:month:year:_) =-   printf "%sMagazine%02d_%s-%s"-      (filterCommon name) (read issue :: Int) year (monthNum month)-title _ _ = undefined
− src/EpubTools/EpubName/Format/MagGud.hs
@@ -1,25 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.MagGud-   ( fmtMagGud )-   where--import Codec.Epub.Opf.Package.Metadata-import Text.Printf--import EpubTools.EpubName.Format.Util ( format )-import EpubTools.EpubName.Util---fmtMagGud :: Metadata -> EN (String, String)-fmtMagGud = format "MagGud"-   "GUD Magazine Authors" (const "")-   ".* Magazine Issue ([0-9]+) ::.*" title---title :: String -> [String] -> String-title _ (issue:_) =-   printf "GUDMagazine%02d" (read issue :: Int)-title _ _              = undefined
− src/EpubTools/EpubName/Format/MagInterzone.hs
@@ -1,25 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.MagInterzone-   ( fmtMagInterzone )-   where--import Codec.Epub.Opf.Package.Metadata-import Text.Printf--import EpubTools.EpubName.Format.Util ( format )-import EpubTools.EpubName.Util---fmtMagInterzone :: Metadata -> EN (String, String)-fmtMagInterzone = format "MagInterzone"-   ".* Authors" (const "")-   "^(Interzone)[^0-9]*([0-9]+)$" title---title :: String -> [String] -> String-title _ (name:issue:_) =-   printf "%sSFF%d" name (read issue :: Int)-title _ _              = undefined
− src/EpubTools/EpubName/Format/MagLightspeed.hs
@@ -1,25 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.MagLightspeed-   ( fmtMagLightspeed )-   where--import Codec.Epub.Opf.Package.Metadata-import Text.Printf--import EpubTools.EpubName.Format.Util ( format, monthNum )-import EpubTools.EpubName.Util---fmtMagLightspeed :: Metadata -> EN (String, String)-fmtMagLightspeed = format "MagInterzone"-   ".*" (const "")-   "(Lightspeed) Magazine, (.*) (.*)" title---title :: String -> [String] -> String-title _ (name:month:year:_) =-   printf "%s%s-%s" name year (monthNum month)-title _ _              = undefined
− src/EpubTools/EpubName/Format/MagNameIssue.hs
@@ -1,25 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.MagNameIssue-   ( fmtMagNameIssue )-   where--import Codec.Epub.Opf.Package.Metadata-import Text.Printf--import EpubTools.EpubName.Format.Util ( filterCommon, format )-import EpubTools.EpubName.Util---fmtMagNameIssue :: Metadata -> EN (String, String)-fmtMagNameIssue = format "MagNameIssue"-   ".* Authors" (const "")-   "(.*) #([0-9]+).*" title---title :: String -> [String] -> String-title _ (name:issue:_) =-   printf "%s%02d" (filterCommon name) (read issue :: Int)-title _ _              = undefined
− src/EpubTools/EpubName/Format/MagNemesis.hs
@@ -1,25 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.MagNemesis-   ( fmtMagNemesis )-   where--import Codec.Epub.Opf.Package.Metadata-import Text.Printf--import EpubTools.EpubName.Format.Util ( filterCommon, format )-import EpubTools.EpubName.Util---fmtMagNemesis :: Metadata -> EN (String, String)-fmtMagNemesis = format "MagNemesis"-   "Stephen Adams" (const "")-   "(Nemesis Mag)azine #([0-9]+).*" title---title :: String -> [String] -> String-title _ (name:issue:_) =-   printf "%s%03d" (filterCommon name) (read issue :: Int)-title _ _              = undefined
− src/EpubTools/EpubName/Format/MagRageMachine.hs
@@ -1,25 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.MagRageMachine-   ( fmtMagRageMachine )-   where--import Codec.Epub.Opf.Package.Metadata-import Text.Printf--import EpubTools.EpubName.Format.Util ( filterCommon, format, monthNum )-import EpubTools.EpubName.Util---fmtMagRageMachine :: Metadata -> EN (String, String)-fmtMagRageMachine = format "MagRageMachine"-   ".*" (const "")-   "(Rage Machine.*)--([^ ]+) ([0-9]{4})$" title---title :: String -> [String] -> String-title _ (name:month:year:_) =-   printf "%s_%s-%s" (filterCommon name) year (monthNum month)-title _ _ = undefined
− src/EpubTools/EpubName/Format/MagSomethingWicked.hs
@@ -1,25 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.MagSomethingWicked-   ( fmtMagSomethingWicked )-   where--import Codec.Epub.Opf.Package.Metadata-import Text.Printf--import EpubTools.EpubName.Format.Util ( filterCommon, format )-import EpubTools.EpubName.Util---fmtMagSomethingWicked :: Metadata -> EN (String, String)-fmtMagSomethingWicked = format "MagSomethingWicked"-   ".* Authors" (const "")-   "(Something Wicked).* #([0-9]+).*" title---title :: String -> [String] -> String-title _ (name:issue:_) =-   printf "%sMagazine%02d" (filterCommon name) (read issue :: Int)-title _ _              = undefined
− src/EpubTools/EpubName/Format/MagUniverse.hs
@@ -1,26 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.MagUniverse-   ( fmtMagUniverse )-   where--import Codec.Epub.Opf.Package.Metadata-import Text.Printf--import EpubTools.EpubName.Format.Util ( filterCommon, format )-import EpubTools.EpubName.Util---fmtMagUniverse :: Metadata -> EN (String, String)-fmtMagUniverse = format "MagUniverse"-   ".*" (const "")-   "^(Jim Baen's Universe)-Vol ([^ ]+) Num ([^ ]+)" title---title :: String -> [String] -> String-title _ (name:vol:num:_) =-   printf "%sVol%02dNum%02d" (filterCommon name) (read vol :: Int) -      (read num :: Int)-title _ _         = undefined
− src/EpubTools/EpubName/Format/SFBestOf.hs
@@ -1,24 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.SFBestOf-   ( fmtSFBestOf )-   where--import Codec.Epub.Opf.Package.Metadata-import Text.Printf--import EpubTools.EpubName.Format.Util ( filterCommon, format )-import EpubTools.EpubName.Util---fmtSFBestOf :: Metadata -> EN (String, String)-fmtSFBestOf = format "SFBestOf"-   "Rich Horton.*" (const "")-   "(.*)" title---title :: String -> [String] -> String-title year (name:_) = printf "%s%s" (filterCommon name) year-title _ _ = undefined
− src/EpubTools/EpubName/Format/Util.hs
@@ -1,290 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Format.Util-   ( filterCommon, format-   , author-   , authorSingle, authorPostfix-   , justAuthors-   , titleSimple-   , monthNum )-   where--import Codec.Epub.Opf.Package-import Control.Monad.Error-import Data.Char-import Data.List ( foldl', intercalate, isPrefixOf )-import Data.Maybe ( fromJust )---import Debug.Trace-import Prelude hiding ( last )-import Text.Printf-import Text.Regex--import EpubTools.EpubName.Opts-import EpubTools.EpubName.Util----{- Convenience function to make a regex replacer for a given pattern -   and replacement string. Helps by doing the 'flip' of str and rpl-   so you can partial eval.--}-repl :: String -> String -> String -> String-repl re rpl str = subRegex (mkRegex re) str rpl---{- Transforms a string like this:-      "the quick brown McCheeseburger" -> "TheQuickBrownMccheeseburger"--}-capFirstAndDeSpace :: String -> String-capFirstAndDeSpace s = concat $ map capFirst $ words s-   where-      capFirst (first:rest) = (toUpper first) : (map toLower rest)-      capFirst _ = undefined---{- A set of common string filters that apply to any and all parts-   of every single string we process in this project.--}-commonFilters :: [(String -> String)]-commonFilters =-   [ repl "[',\\?();#’]"  ""-   , repl "\\."           " "-   , repl ":"             "_"-   , filter (/= '"')-   , repl "]"             ""-   , repl "\\*"           ""-   , repl "!"             ""-   , repl "-"             " "-   , repl "\\["           "_ "-   -- Decided that I like the article included in titles-   --, repl "^The "         ""-   , repl "&"             " And "-   , capFirstAndDeSpace-   ]---{- Utility function to apply the above commonFilters to a string,-   giving you back the transformed string--}-filterCommon :: String -> String-filterCommon s = foldl' (flip id) s commonFilters---{- Convert an English month name (with creative ranges and variations)-   into number form--}-monthNum :: String -> String-monthNum x-   | isPrefixOf x "January"   = "01"-monthNum "January-February"   = "01_02"-monthNum "January/February"   = "01_02"-monthNum "Jan-Feb"            = "01_02"-monthNum "Jan/Feb"            = "01_02"-monthNum x-   | isPrefixOf x "February"  = "02"-monthNum x-   | isPrefixOf x "March"     = "03"-monthNum "March-April"        = "03_04"-monthNum "March/April"        = "03_04"-monthNum "Mar-Apr"            = "03_04"-monthNum "Mar/Apr"            = "03_04"-monthNum x-   | isPrefixOf x "April"     = "04"-monthNum "April-May"          = "04_05"-monthNum "April/May"          = "04_05"-monthNum "Apr-May"            = "04_05"-monthNum "Apr/May"            = "04_05"-monthNum "May"                = "05"-monthNum "May-June"           = "05_06"-monthNum "May/June"           = "05_06"-monthNum "May-Jun"            = "05_06"-monthNum "May/Jun"            = "05_06"-monthNum x-   | isPrefixOf x "June"      = "06"-monthNum "June/July"          = "06_07"-monthNum "June-July"          = "06_07"-monthNum "Jun-Jul"            = "06_07"-monthNum "Jun/Jul"            = "06_07"-monthNum x-   | isPrefixOf x "July"      = "07"-monthNum "July-August"        = "07_08"-monthNum "July/August"        = "07_08"-monthNum "Jul-Aug"            = "07_08"-monthNum "Jul/Aug"            = "07_08"-monthNum x-   | isPrefixOf x "August"    = "08"-monthNum "August-September"   = "08_09"-monthNum "August/September"   = "08_09"-monthNum "Aug-Sep"            = "08_09"-monthNum "Aug/Sep"            = "08_09"-monthNum x-   | isPrefixOf x "September" = "09"-monthNum "September-October"  = "09_10"-monthNum x-   | isPrefixOf x "October"   = "10"-monthNum "October-November"   = "10_11"-monthNum "October/November"   = "10_11"-monthNum "Oct-Nov"            = "10_11"-monthNum "Oct/Nov"            = "10_11"-monthNum "November-December"  = "11_12"-monthNum x-   | isPrefixOf x "November"  = "11"-monthNum x-   | isPrefixOf x "December"  = "12"-monthNum x                    = "[ERROR monthNum " ++ x ++ "]"---formatAuthor-   :: String-   -> (Metadata -> String)-   -> Metadata-   -> EN String-formatAuthor re f md =-   maybe (throwError "formatAuthor failed")-      (const (return . f $ md)) (tryPats $ justAuthors md)--   where-      justAuthorStrings = map (\(MetaCreator _ _ n) -> n)--      match = matchRegex (mkRegex re)--      tryPats [] = Just []  -- Special case of no authors at all-      tryPats as = foldr mplus Nothing-         ((map match) . justAuthorStrings $ as)---formatTitle -   :: String-   -> (String -> [String] -> String)-   -> String-   -> String-   -> EN String-formatTitle re f year s = case matchRegex (mkRegex re) s of-   Just xs -> return $ f year xs-   Nothing -> throwError "formatTitle failed"---{- Look for a date tag with event="original-publication" in the-   metadata--}-extractYear :: Metadata -> EN String-extractYear md = do-   inclY <- asks optYear-   return . maybe "" ('_' :) $-      (foldr mplus Nothing (map (maybeYear inclY) $ metaDates md))--   where-      maybeYear True (MetaDate (Just "original-publication") d) = Just d-      maybeYear _    _                                          = Nothing---extractPublisher :: Metadata -> Bool -> String-extractPublisher _  False = ""-extractPublisher md True  = maybe "" ('_' :)-   (foldr mplus Nothing (map maybePub $ metaContributors md))--   where-      maybePub (MetaCreator (Just "bkp") (Just fa) _ ) = Just fa-      maybePub _                                       = Nothing---{- This is the main work performing function that's called by every -   formatter. It expects to see a regexp pattern and format function -   for both author and title parts of the book info. And then the map -   of fields from a book.-   If the pattern matches, the resulting list of match results is sent -   to the supplied format function. If any of this fails, the entire -   action throws.--}-format-   :: String-   -> String-   -> (Metadata -> String)-   -> String-   -> (String -> [String] -> String)-   -> Metadata-   -> EN (String, String)-format label authorPat authorFmt titlePat titleFmt md = do-   newAuthor <- formatAuthor authorPat authorFmt md-   --trace newAuthor (return ())--   (MetaTitle _ oldTitle) <- case metaTitles md of-      [] -> throwError "format failed, no title present"-      ts -> return . head $ ts-   year <- extractYear md-   newTitle <- formatTitle titlePat titleFmt year oldTitle--   publisher <- fmap (extractPublisher md) $ asks optPublisher--   return-      ( label-      , printf "%s%s%s.epub" newAuthor newTitle publisher-      )---formatSingleAuthor :: MetaCreator -> String--formatSingleAuthor (MetaCreator _ (Just fa) di) = -   if ((fa == di) && all (/= ',') fa)-      then formatSingleAuthor $ MetaCreator Nothing Nothing di-      else authorSingle [fa]--formatSingleAuthor (MetaCreator _ _         di) = -   authorSingle . reverse $ parts-   where-      parts = fromJust .-         (matchRegex (mkRegex "(.*) ([^ ]+)$")) $ di---lastName :: MetaCreator -> String-lastName (MetaCreator _ (Just fa) _ ) = head . fromJust .-   (matchRegex (mkRegex "(.*),.*")) $ fa-lastName (MetaCreator _ _         di) = head . fromJust .-   (matchRegex (mkRegex ".* (.*)")) $ di---formatMultiAuthors :: [MetaCreator] -> String-formatMultiAuthors = (intercalate "_") . (map lastName)---justAuthors :: Metadata -> [MetaCreator]-justAuthors = (filter isAut) . metaCreators-   where-      isAut (MetaCreator (Just "aut") _ _) = True-      isAut (MetaCreator Nothing      _ _) = True-      isAut _                              = False---author :: Metadata -> String-author = fmtAuthor . justAuthors-   where-      fmtAuthor [c] = formatSingleAuthor c-      fmtAuthor cs = (formatMultiAuthors cs) ++ "-"---{- A common simple formatter for many book authors--}-authorSingle :: [String] -> String-authorSingle (last:rest:_) =-   printf "%s%s-" (filterCommon last) (filterCommon rest)-authorSingle [n]           = printf "%s-" $ filterCommon n---authorSingle x             = show x-authorSingle _             = undefined---{- Author names with a postfix like II, III, Jr. or Sr.--}-authorPostfix :: [String] -> String-authorPostfix (rest:last:postfix:_) =-   printf "%s%s%s-" (filterCommon last) (filterCommon rest)-      (filterCommon postfix)-authorPostfix _             = undefined---{- A common simple formatter for many book titles. Handles year too.--}-titleSimple :: String -> [String] -> String-titleSimple year (old:_) = printf "%s%s" (filterCommon old) year-titleSimple _ _          = undefined
− src/EpubTools/EpubName/Formatters.hs
@@ -1,74 +0,0 @@--- Copyright: 2008-2011 Dino Morelli--- License: BSD3 (see LICENSE)--- Author: Dino Morelli <dino@ui3.info>--module EpubTools.EpubName.Formatters-   ( tryFormatting )-   where--import Codec.Epub.Opf.Package.Metadata-import Control.Monad-import Text.Printf----import EpubTools.EpubName.Format.Anonymous-import EpubTools.EpubName.Format.AuthorBasic-import EpubTools.EpubName.Format.AuthorDouble---import EpubTools.EpubName.Format.AuthorSt---import EpubTools.EpubName.Format.AuthorThird-import EpubTools.EpubName.Format.MagAeon-import EpubTools.EpubName.Format.MagAnalog-import EpubTools.EpubName.Format.MagApex-import EpubTools.EpubName.Format.MagBcs-import EpubTools.EpubName.Format.MagChallengingDestiny-import EpubTools.EpubName.Format.MagClarkesworld-import EpubTools.EpubName.Format.MagEclipse-import EpubTools.EpubName.Format.MagFsf-import EpubTools.EpubName.Format.MagFutureOrbits-import EpubTools.EpubName.Format.MagGud-import EpubTools.EpubName.Format.MagInterzone-import EpubTools.EpubName.Format.MagLightspeed-import EpubTools.EpubName.Format.MagNameIssue-import EpubTools.EpubName.Format.MagNemesis-import EpubTools.EpubName.Format.MagRageMachine-import EpubTools.EpubName.Format.MagSomethingWicked-import EpubTools.EpubName.Format.MagUniverse-import EpubTools.EpubName.Format.SFBestOf-import EpubTools.EpubName.Util---formatters :: [Metadata -> EN (String, String)]-formatters =-{--   , fmtAnonymous-   , fmtAuthorThird-   , fmtAuthorSt--}-   [ fmtMagAnalog-   , fmtMagNemesis-   , fmtMagAeon-   , fmtMagEclipse-   , fmtMagChallengingDestiny-   , fmtMagClarkesworld-   , fmtMagGud-   , fmtMagInterzone-   , fmtMagLightspeed-   , fmtMagSomethingWicked-   , fmtMagRageMachine-   , fmtMagFsf-   , fmtMagFutureOrbits-   , fmtMagBcs-   , fmtMagApex-   , fmtMagNameIssue-   , fmtMagUniverse-   , fmtSFBestOf-   , fmtAuthorDouble--   , fmtAuthorBasic-   ]---tryFormatting :: (FilePath, Metadata) -> EN (String, String)-tryFormatting (oldPath, md) = do-   foldr mplus -      (throwError $ printf "%s [ERROR No formatter found]" oldPath) $-      map (\f -> f md) formatters
src/EpubTools/EpubName/Opts.hs view
@@ -81,8 +81,8 @@          ]       footer = init $ unlines          [ "Verbosity levels:"-         , "   1      - Include which formatter processed the file"-         , "   2      - Include the OPF Package and Metadata info"+         , "   1 - Include which formatter processed the file"+         , "   2 - Include the OPF Package and Metadata info"          , ""          , "Exit codes:"          , "   0 - success"@@ -91,6 +91,8 @@          , ""          , "Book names are constructed by examining parts of the OPF Package metadata such as the title, creators, contributors and dates."          , ""+         , "Strings from the OPF metadata fields are stripped of punctuation, CamelCased and stripped of spaces. Resulting file names look like this:"+         , ""          , "For books with a single author:"          , "   LastFirst[Middle]-TitleText[_year][_publisher].epub"          , ""@@ -102,7 +104,7 @@          , ""          , "Only creator tags with either a role attribute of 'aut' or no role at all are considered authors. If a file-as attribute is present, this will be the preferred string. If not, the program tries to do some intelligent parsing of the name."          , ""-         , "The date is taken from the first date tag with an event attribute of 'original-publication', if present. The OPF spec is a little thin on this. I noticed frequent use of this attribute in books, and so went with that."+         , "The publication year is taken from the first date tag with an event attribute of 'original-publication', if present. The OPF spec is a little thin on this. I noticed frequent use of this attribute in books, and so went with that."          , ""          , "Publisher: I wanted to provide a way to have multiple copies of the same book produced by different publishers and name them sort-of unambiguously. I came up with the idea of expecting a contributor tag with role attribute of 'bkp' (so far, this is fairly normal). And then use a file-as attribute on that tag to contain a small string to be used in the filename. The idea here is short and sweet for the file-as."          , ""@@ -110,5 +112,5 @@          , ""          , "For more information please see the IDPF OPF specification found here: http://idpf.org/epub/20/spec/OPF_2.0.1_draft.htm"          , ""-         , "Version 1.0.0.1  Dino Morelli <dino@ui3.info>"+         , "Version 1.1.0  Dino Morelli <dino@ui3.info>"          ]
src/EpubTools/EpubName/Util.hs view
@@ -6,11 +6,15 @@    ( EN , runEN    , throwError    , asks+   , filterCommon    )    where  import Control.Monad.Error import Control.Monad.Reader+import Data.Char+import Data.List ( foldl' )+import Text.Regex  import EpubTools.EpubName.Opts @@ -19,3 +23,49 @@  runEN :: Options -> EN a -> IO (Either String a) runEN env ev = runErrorT $ runReaderT ev env+++{- Convenience function to make a regex replacer for a given pattern +   and replacement string. Helps by doing the 'flip' of str and rpl+   so you can partial eval.+-}+repl :: String -> String -> String -> String+repl re rpl str = subRegex (mkRegex re) str rpl+++{- Transforms a string like this:+      "the quick brown McCheeseburger" -> "TheQuickBrownMccheeseburger"+-}+capFirstAndDeSpace :: String -> String+capFirstAndDeSpace s = concat $ map capFirst $ words s+   where+      capFirst (first:rest) = (toUpper first) : (map toLower rest)+      capFirst _ = undefined+++{- A set of common string filters that apply to any and all parts+   of every single string we process in this project.+-}+commonFilters :: [(String -> String)]+commonFilters =+   [ repl "[',\\?();#’]"  ""+   , repl "\\."           " "+   , repl ":"             "_"+   , filter (/= '"')+   , repl "]"             ""+   , repl "\\*"           ""+   , repl "!"             ""+   , repl "-"             " "+   , repl "\\["           "_ "+   -- Decided that I like the article included in titles+   --, repl "^The "         ""+   , repl "&"             " And "+   , capFirstAndDeSpace+   ]+++{- Utility function to apply the above commonFilters to a string,+   giving you back the transformed string+-}+filterCommon :: String -> String+filterCommon s = foldl' (flip id) s commonFilters
src/EpubTools/EpubZip/Opts.hs view
@@ -68,5 +68,5 @@          , ""          , "For more information please see the IDPF OPF specification found here: http://idpf.org/epub/20/spec/OPF_2.0.1_draft.htm"          , ""-         , "Version 1.0.0.1  Dino Morelli <dino@ui3.info>"+         , "Version 1.1.0  Dino Morelli <dino@ui3.info>"          ]
src/EpubTools/epubname.hs view
@@ -15,7 +15,7 @@                  ) import Text.Printf -import EpubTools.EpubName.Formatters ( tryFormatting )+import EpubTools.EpubName.Format ( tryFormatting ) import EpubTools.EpubName.Opts ( Options (..), parseOpts, usageText ) import EpubTools.EpubName.Util @@ -50,7 +50,7 @@    result <- runEN opts $ do       pkg <- parseEpubOpf oldPath       let md = opMeta pkg-      (fmtUsed, newPath) <- tryFormatting (oldPath, md)+      (fmtUsed, newPath) <- tryFormatting oldPath md        when (not $ optOverwrite opts) $ do          fileExists <- liftIO $ doesFileExist newPath
src/EpubTools/epubzip.hs view
@@ -13,7 +13,7 @@ import System.FilePath import Text.Printf -import EpubTools.EpubName.Formatters ( tryFormatting )+import EpubTools.EpubName.Format ( tryFormatting ) import qualified EpubTools.EpubName.Opts as EN import EpubTools.EpubName.Util ( runEN ) import EpubTools.EpubZip.Opts@@ -39,7 +39,7 @@                (_, contents) <- opfContentsFromDir "."                parseXmlToOpf contents             (_, newPath) <- tryFormatting-               ("CURRENT DIRECTORY", opMeta package)+               "CURRENT DIRECTORY" $ opMeta package             return $ inputPath </> newPath          else return . Right $ inputPath 
testsuite/epubname.hs view
@@ -10,7 +10,7 @@ import Test.HUnit ( Counts (..), Test (..), assertEqual, runTestTT ) import Test.HUnit.Base ( Assertion ) -import EpubTools.EpubName.Formatters ( tryFormatting )+import EpubTools.EpubName.Format ( tryFormatting ) import EpubTools.EpubName.Opts import EpubTools.EpubName.Util @@ -33,12 +33,15 @@ tests :: Test tests = TestList    [ testAuthorMinimal+   , testAuthorOneName    , testAuthorRole    , testAuthorFileas    , testAuthorFull-   , testAuthorDoubleAnd+   , testAuthorSeveral+   , testSeveralAuthors    , testNoAuthor-   , testNoAuthorPubDate+   , testCreatorsNoAuthor+   , testCreatorsNoAuthorPubDate    , testCapsTitle    , testColon    , testBracketTitle@@ -52,20 +55,25 @@    , testMagApexShort    , testChallengingDestinyShort    , testChallengingDestinyLong-   , testChallengingDestinyPub    , testAnalog    , testAsimovs+   , testFantasyMagazine    , testFsfShort    , testFsfLong+   , testFsfAmpersand    , testMagFutureOrbits    , testGudShort    , testGudLong+   , testGudVeryLong    , testInterzoneShort    , testInterzoneLong    , testNemesisShort    , testNemesisLong    , testMagSomethingWicked+   , testMagSomethingWickedMonth    , testSFBestOf+   , testBestSF+   , testYearsBest    , testMagBlackStatic    , testRageMachineMag    , testEclipseMag@@ -75,13 +83,16 @@    , testBkpMissing    , testMagUniverse    , testMagClarkesworld+   , testLightspeedDate+   , testLightspeedIssue+   , testMagWeirdTales    ]   assertNewNameOpts :: Options -> String -> Metadata     -> (String, String) -> Assertion assertNewNameOpts opts desc meta expected = do-   result <- runEN opts $ tryFormatting ("", meta)+   result <- runEN opts $ tryFormatting "" meta    let actual = either (\em -> ("NO FORMATTER", em)) id result    assertEqual desc expected actual @@ -99,11 +110,25 @@          , metaTitles = [MetaTitle Nothing "Moby Dick"]          }       expected =-         ( "AuthorBasic"+         ( "book"          , "MelvilleHerman-MobyDick.epub"          )  +testAuthorOneName :: Test+testAuthorOneName = TestCase $+   assertNewName "author is a single word" meta expected+   where+      meta = emptyMetadata+         { metaCreators = [MetaCreator Nothing Nothing "Melville"]+         , metaTitles = [MetaTitle Nothing "Moby Dick"]+         }+      expected =+         ( "book"+         , "Melville-MobyDick.epub"+         )++ testAuthorRole :: Test testAuthorRole = TestCase $    assertNewName "author with role" meta expected@@ -114,7 +139,7 @@          , metaTitles = [MetaTitle Nothing "Moby Dick"]          }       expected =-         ( "AuthorBasic"+         ( "book"          , "MelvilleHerman-MobyDick.epub"          ) @@ -130,7 +155,7 @@          , metaTitles = [MetaTitle Nothing "Moby Dick"]          }       expected =-         ( "AuthorBasic"+         ( "book"          , "MelvilleHerman-MobyDick.epub"          ) @@ -146,16 +171,53 @@          , metaTitles = [MetaTitle Nothing "Moby Dick"]          }       expected =-         ( "AuthorBasic"+         ( "book"          , "MelvilleHerman-MobyDick.epub"          )  +testAuthorSeveral :: Test+testAuthorSeveral = TestCase $+   assertNewName "several authors" meta expected+   where+      meta = emptyMetadata+         { metaCreators =+            [ MetaCreator (Just "aut") Nothing+               "James Patrick Kelly"+            , MetaCreator (Just "aut") (Just "Kessel, John")+               "John Kessel"+            , MetaCreator (Just "aut") Nothing+               "Jonathan Lethem"+            ]+         , metaTitles = [MetaTitle Nothing+            "Ninety Percent of Everything"]+         }+      expected =+         ( "book"+         , "Kelly_Kessel_Lethem-NinetyPercentOfEverything.epub"+         )++ testNoAuthor :: Test testNoAuthor = TestCase $-   assertNewName "no author(s) at all" meta expected+   assertNewName "no creator(s) at all" meta expected    where       meta = emptyMetadata+         { metaCreators = []+         , metaTitles = [MetaTitle Nothing+            "Some Collection of Fine Stories, Volume 1"]+         }+      expected =+         ( "book"+         , "SomeCollectionOfFineStoriesVolume1.epub"+         )+++testCreatorsNoAuthor :: Test+testCreatorsNoAuthor = TestCase $+   assertNewName "creators, but no author(s) at all" meta expected+   where+      meta = emptyMetadata          { metaCreators =             [ MetaCreator (Just "edt") Nothing                "Graham Spindlewest"@@ -166,14 +228,14 @@             "Some Collection of Fine Stories, Volume 1"]          }       expected =-         ( "SFBestOf"+         ( "book"          , "SomeCollectionOfFineStoriesVolume1.epub"          )  -testNoAuthorPubDate :: Test-testNoAuthorPubDate = TestCase $-   assertNewName "no author(s) at all, with publication date" +testCreatorsNoAuthorPubDate :: Test+testCreatorsNoAuthorPubDate = TestCase $+   assertNewName "creators, but no author(s) at all, with pub date"        meta expected    where       meta = emptyMetadata@@ -189,23 +251,23 @@             "2008"]          }       expected =-         ( "SFBestOf"+         ( "book"          , "SomeCollectionOfFineStoriesVolume1_2008.epub"          )  -testAuthorDoubleAnd :: Test-testAuthorDoubleAnd = TestCase $-   assertNewName "two authors separated by and" meta expected+testSeveralAuthors :: Test+testSeveralAuthors = TestCase $+   assertNewName "more than one author separated by & and/or and" meta expected    where       meta = emptyMetadata          { metaCreators = [MetaCreator Nothing Nothing-            "Kevin J. Anderson and Rebecca Moesta"]-         , metaTitles = [MetaTitle Nothing "Rough Draft"]+            "Yvonne Q. Anderson & Eva Tunglewacker and Jefferson Milner"]+         , metaTitles = [MetaTitle Nothing "Big Trouble"]          }       expected =-         ( "AuthorDouble"-         , "Anderson_Moesta-RoughDraft.epub"+         ( "book"+         , "Anderson_Tunglewacker_Milner-BigTrouble.epub"          )  @@ -218,7 +280,7 @@          , metaTitles = [MetaTitle Nothing "EON"]          }       expected =-         ( "AuthorBasic"+         ( "book"          , "BearGreg-Eon.epub"          ) @@ -233,7 +295,7 @@             "Book 1: 3rd World Products, Inc."]          }       expected =-         ( "AuthorBasic"+         ( "book"          , "HowdersheltEd-Book1_3rdWorldProductsInc.epub"          ) @@ -247,7 +309,7 @@          , metaTitles = [MetaTitle Nothing "SKitty [Shipscat series #1]"]          }       expected =-         ( "AuthorBasic"+         ( "book"          , "LackeyMercedes-Skitty_ShipscatSeries1.epub"          ) @@ -277,7 +339,7 @@             "The *crazy*: Sand-box. Of Smedley's discontent, fear & Malnourishment? (Maybe not!); [Part #2]"]          }       expected =-         ( "AuthorBasic"+         ( "book"          , "MorelliDino-TheCrazy_SandBoxOfSmedleysDiscontentFearAndMalnourishmentMaybeNot_Part2.epub"          ) @@ -292,7 +354,7 @@          , metaDates = [MetaDate (Just "original-publication") "2003"]          }       expected =-         ( "AuthorBasic"+         ( "book"          , "JonesJim-ATimelessStory_2003.epub"          ) @@ -310,7 +372,7 @@          , metaDates = [MetaDate (Just "original-publication") "2003"]          }       expected =-         ( "AuthorBasic"+         ( "book"          , "JonesJim-ATimelessStory.epub"          ) @@ -324,7 +386,7 @@          , metaTitles = [MetaTitle Nothing "Aeon Eight"]          }       expected =-         ( "MagAeon"+         ( "magAeon"          , "AeonMagazine08.epub"          ) @@ -338,7 +400,7 @@          , metaTitles = [MetaTitle Nothing "Aeon Thirteen"]          }       expected =-         ( "MagAeon"+         ( "magAeon"          , "AeonMagazine13.epub"          ) @@ -354,7 +416,7 @@             "Apex Science Fiction and Horror Digest #9"]          }       expected =-         ( "MagApex"+         ( "magApex"          , "ApexMagazine009.epub"          ) @@ -370,14 +432,14 @@             "Apex Magazine Issue 17"]          }       expected =-         ( "MagApex"+         ( "magApex"          , "ApexMagazine017.epub"          )   testChallengingDestinyShort :: Test testChallengingDestinyShort = TestCase $-   assertNewName "Challenging Destiny Magazine, short" meta expected+   assertNewName "Challenging Destiny Magazine, short title" meta expected    where       meta = emptyMetadata          { metaCreators = [MetaCreator Nothing Nothing@@ -386,14 +448,14 @@             "Challenging Destiny #23"]          }       expected =-         ( "MagChallengingDestiny"+         ( "magChallengingDestiny"          , "ChallengingDestinyMagazine023.epub"          )   testChallengingDestinyLong :: Test testChallengingDestinyLong = TestCase $-   assertNewName "Challenging Destiny Magazine, long" meta expected+   assertNewName "Challenging Destiny Magazine, long title" meta expected    where       meta = emptyMetadata          { metaCreators = [MetaCreator Nothing Nothing @@ -402,28 +464,11 @@             "Challenging Destiny #24: August 2007"]          }       expected =-         ( "MagChallengingDestiny"+         ( "magChallengingDestiny"          , "ChallengingDestinyMagazine024.epub"          )  -testChallengingDestinyPub :: Test-testChallengingDestinyPub = TestCase $-   assertNewName "Challenging Destiny Magazine, Publishing in author"-      meta expected-   where-      meta = emptyMetadata-         { metaCreators = [MetaCreator Nothing Nothing -            "Crystalline Sphere Publishing"]-         , metaTitles = [MetaTitle Nothing -            "Challenging Destiny #18"]-         }-      expected =-         ( "MagChallengingDestiny"-         , "ChallengingDestinyMagazine018.epub"-         )-- testAnalog :: Test testAnalog = TestCase $    assertNewName "Analog" meta expected@@ -435,7 +480,7 @@             "Analog SFF, July-August 2003"]          }       expected =-         ( "MagAnalog"+         ( "magAnalog"          , "AnalogSF2003-07_08.epub"          ) @@ -451,11 +496,33 @@             "Asimov's SF, August 2003"]          }       expected =-         ( "MagAnalog"+         ( "magAnalog"          , "AsimovsSF2003-08.epub"          ) +testFantasyMagazine :: Test+testFantasyMagazine = TestCase $+   assertNewName "Fantasy Magazine" meta expected+   where+      meta = emptyMetadata+         { metaCreators =+            [ MetaCreator (Just "aut") (Just "Howard, Kat & Tanzer, Molly & Beagle, Peter S. & Howard, Jonathan L. & Valentine, Genevieve & Vaughn, Carrie & Pilinovsky, Helen") "Kat Howard"+            , MetaCreator (Just "aut") Nothing "Molly Tanzer"+            , MetaCreator (Just "aut") Nothing "Peter S. Beagle"+            , MetaCreator (Just "aut") Nothing "Jonathan L. Howard"+            , MetaCreator (Just "aut") Nothing "Genevieve Valentine"+            , MetaCreator (Just "aut") Nothing "Carrie Vaughn"+            , MetaCreator (Just "aut") Nothing "Helen Pilinovsky"+            ]+         , metaTitles = [MetaTitle Nothing+            "Fantasy Magazine Issue 49"]+         }+      expected =+         ( "magFantasyMag"+         , "FantasyMagazine049.epub"+         ) + testFsfShort :: Test testFsfShort = TestCase $    assertNewName "FSF Magazine, short" meta expected@@ -466,7 +533,7 @@          , metaTitles = [MetaTitle Nothing "FSF, April 2008"]          }       expected =-         ( "MagFsf"+         ( "magFsf"          , "FantasyScienceFiction2008-04.epub"          ) @@ -482,11 +549,26 @@             "FSF Magazine, April 2006"]          }       expected =-         ( "MagFsf"+         ( "magFsf"          , "FantasyScienceFiction2006-04.epub"          )  +testFsfAmpersand :: Test+testFsfAmpersand = TestCase $+   assertNewName "FSF Magazine, ampersand" meta expected+   where+      meta = emptyMetadata+         { metaCreators = [MetaCreator Nothing Nothing+            "Spilogale"]+         , metaTitles = [MetaTitle Nothing "F & SF Dec 2003"]+         }+      expected =+         ( "magFsf"+         , "FantasyScienceFiction2003-12.epub"+         )++ testMagFutureOrbits :: Test testMagFutureOrbits = TestCase $    assertNewName "testMagFutureOrbits" meta expected@@ -498,7 +580,7 @@             "Future Orbits Issue 5, June/July 2002"]          }       expected =-         ( "MagFutureOrbits"+         ( "magFutureOrbits"          , "FutureOrbitsMagazine05_2002-06_07.epub"          ) @@ -514,7 +596,7 @@             "GUD Magazine Issue 0 :: Spring 2007"]          }       expected =-         ( "MagGud"+         ( "magGud"          , "GUDMagazine00.epub"          ) @@ -530,11 +612,27 @@             "GUD Magazine Issue 2 :: Spring 2008"]          }       expected =-         ( "MagGud"+         ( "magGud"          , "GUDMagazine02.epub"          )  +testGudVeryLong :: Test+testGudVeryLong = TestCase $+   assertNewName "Gud Magazine, very long" meta expected+   where+      meta = emptyMetadata+         { metaCreators = [MetaCreator Nothing Nothing +            "GUD Magazine Authors"]+         , metaTitles = [MetaTitle Nothing +            "Greatest Uncommon Denominator Magazine Issue 4 :: Spring 2009"]+         }+      expected =+         ( "magGud"+         , "GUDMagazine04.epub"+         )++ testInterzoneShort :: Test testInterzoneShort = TestCase $    assertNewName "Interzone Magazine, short" meta expected@@ -546,7 +644,7 @@             "Interzone SFF #212"]          }       expected =-         ( "MagInterzone"+         ( "magInterzone"          , "InterzoneSFF212.epub"          ) @@ -562,7 +660,7 @@             "Interzone Science Fiction and Fantasy Magazine #216"]          }       expected =-         ( "MagInterzone"+         ( "magInterzone"          , "InterzoneSFF216.epub"          ) @@ -578,7 +676,7 @@             "Nemesis Magazine #2"]          }       expected =-         ( "MagNemesis"+         ( "magNemesis"          , "NemesisMag002.epub"          ) @@ -594,7 +692,7 @@             "Nemesis Magazine #7: Featuring Victory Rose in Death Stalks the Ruins"]          }       expected =-         ( "MagNemesis"+         ( "magNemesis"          , "NemesisMag007.epub"          ) @@ -610,11 +708,28 @@             "Something Wicked SF and Horror Magazine #5"]          }       expected =-         ( "MagSomethingWicked"-         , "SomethingWickedMagazine05.epub"+         ( "magSomethingWicked"+         , "SomethingWicked005.epub"          )  +testMagSomethingWickedMonth :: Test+testMagSomethingWickedMonth = TestCase $+   assertNewName "Something Wicked Magazine, month in title" meta expected+   where+      meta = emptyMetadata+         { metaCreators = [MetaCreator (Just "aut")+            (Just "Authors, Something Wicked")+            "Something Wicked Authors"]+         , metaTitles = [MetaTitle Nothing +            "Something Wicked #14 (October 2011)"]+         }+      expected =+         ( "magSomethingWicked"+         , "SomethingWicked014.epub"+         )++ testSFBestOf :: Test testSFBestOf = TestCase $    assertNewName "Science Fiction: The Best of the Year" meta expected@@ -626,11 +741,45 @@             "Science Fiction: The Best of the Year, 2007 Edition"]          }       expected =-         ( "SFBestOf"+         ( "compOfTheYear"          , "ScienceFiction_TheBestOfTheYear2007Edition.epub"          )  +testBestSF :: Test+testBestSF = TestCase $+   assertNewName "The Best Science Fiction and Fantasy of the Year"+      meta expected+   where+      meta = emptyMetadata+         { metaCreators = [MetaCreator (Just "aut")+            (Just "Strahan, Jonathan")+            "Jonathan Strahan"]+         , metaTitles = [MetaTitle Nothing +            "The Best Science Fiction and Fantasy of the Year: Volume 2"]+         }+      expected =+         ( "compOfTheYear"+         , "TheBestScienceFictionAndFantasyOfTheYear_Volume2.epub"+         )+++testYearsBest :: Test+testYearsBest = TestCase $+   assertNewName "The Year's Best SF" meta expected+   where+      meta = emptyMetadata+         { metaCreators = [MetaCreator (Just "aut") Nothing +            "Rich Horton, Michael Swanwick, Karen Joy Fowler"]+         , metaTitles = [MetaTitle Nothing +            "The Year's Best Science Fiction: 2008 Edition"]+         }+      expected =+         ( "compYearsBest"+         , "TheYearsBestScienceFiction_2008Edition.epub"+         )++ testMagBlackStatic :: Test testMagBlackStatic = TestCase $    assertNewName "Black Static Magazine" meta expected@@ -642,7 +791,7 @@             "Black Static Horror Magazine #5"]          }       expected =-         ( "MagNameIssue"+         ( "magBlackStatic"          , "BlackStaticHorrorMagazine05.epub"          ) @@ -658,7 +807,7 @@             "Rage Machine Magazine #1--December 2005"]          }       expected =-         ( "MagRageMachine"+         ( "magRageMachine"          , "RageMachineMagazine1_2005-12.epub"          ) @@ -675,7 +824,7 @@             "Eclipse One"]          }       expected =-         ( "MagEclipse"+         ( "magEclipse"          , "Eclipse01.epub"          ) @@ -697,7 +846,7 @@             "Beneath Ceaseless Skies #32"]          }       expected =-         ( "MagBcs"+         ( "magBcs"          , "BeneathCeaselessSkies_Issue032.epub"          ) @@ -717,7 +866,7 @@          , metaTitles = [MetaTitle Nothing "Moby Dick"]          }       expected =-         ( "AuthorBasic"+         ( "book"          , "MelvilleHerman-MobyDick_acme.epub"          ) @@ -737,7 +886,7 @@          , metaTitles = [MetaTitle Nothing "Moby Dick"]          }       expected =-         ( "AuthorBasic"+         ( "book"          , "MelvilleHerman-MobyDick.epub"          ) @@ -755,7 +904,7 @@          , metaTitles = [MetaTitle Nothing "Moby Dick"]          }       expected =-         ( "AuthorBasic"+         ( "book"          , "MelvilleHerman-MobyDick.epub"          ) @@ -773,7 +922,7 @@          , metaTitles = [MetaTitle Nothing "Jim Baen's Universe-Vol 4 Num 6"]          }       expected =-         ( "MagUniverse"+         ( "magUniverse"          , "JimBaensUniverseVol04Num06.epub"          ) @@ -790,6 +939,57 @@             "Clarkesworld Magazine - Issue 21"]          }       expected =-         ( "MagClarkesworld"+         ( "magClarkesworld"          , "Clarkesworld021.epub"+         )+++testLightspeedDate :: Test+testLightspeedDate = TestCase $+   assertNewName "Lightspeed Magazine, date in title" meta expected+   where+      meta = emptyMetadata+         { metaCreators = [MetaCreator (Just "aut") Nothing+            "Edited by John Joseph Adams"]+         , metaTitles = [MetaTitle Nothing +            "Lightspeed Magazine, June 2010"]+         }+      expected =+         ( "magLightspeedDate"+         , "Lightspeed2010-06.epub"+         )+++testLightspeedIssue :: Test+testLightspeedIssue = TestCase $+   assertNewName "Lightspeed Magazine, issue number in title" meta expected+   where+      meta = emptyMetadata+         { metaCreators = [MetaCreator (Just "aut") (Just "Magazine, Lightspeed & Clark, Maggie & Valentine, Genevieve & Baxter, Stephen & Okorafor, Nnedi & Wilison, Daniel H. & Reed, Robert & Sedia, Ekaterina") "Lightspeed Magazine"]+         , metaTitles = [MetaTitle Nothing +            "Lightspeed Magazine Issue 10"]+         }+      expected =+         ( "magLightspeedIssue"+         , "Lightspeed010.epub"+         )+++testMagWeirdTales :: Test+testMagWeirdTales = TestCase $+   assertNewName "Weird Tales magazine" meta expected+   where+      meta = emptyMetadata+         { metaCreators =+            [ MetaCreator (Just "aut") Nothing "VanderMeer"+            , MetaCreator (Just "aut") Nothing "Ann"+            , MetaCreator (Just "aut") Nothing "Spinrad"+            , MetaCreator (Just "aut") Nothing "Norman"+            ]+         , metaTitles = [MetaTitle Nothing +            "Weird Tales #350"]+         }+      expected =+         ( "magWeirdTales"+         , "WeirdTales350.epub"          )