diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,13 @@
 # dnf-repo releases
 
+## 0.5.2 (2022-11-28)
+- --releasever now induces using a separate dnf cache subdir
+- YumRepoFile: do not sort modes
+- silence "already enabled/disabled" warnings when there are actions
+- improve --save: use yesno prompt and only act if changes
+- --clear-expires: error if no repos set to expire
+- warning when run as sudo
+
 ## 0.5.1 (2022-11-08)
 - check if new copr or koji repo exists with http-directory
 - remove initial/trailing / or : from reponames
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@
 
 ```shellsession
 $ dnf-repo --version
-0.5.1
+0.5.2
 $ dnf-repo --help
 DNF wrapper repo tool
 
@@ -66,7 +66,7 @@
   -c,--add-copr COPR       Create repo file for copr repo
   -k,--add-koji REPO       Create repo file for koji repo (f37-build, rawhide,
                            epel9-build, etc)
-  -u,--repourl URL         temporary repo from a baseurl
+  -u,--repourl URL         Use temporary repo from a baseurl
 ```
 
 ## Usage examples
@@ -114,18 +114,18 @@
 ### Use only source repos
 ```shellsession
 $ dnf-repo -d \* --enable-source
-with 'fedora-source' enabled
-with 'updates-source' enabled
-with 'fedora' disabled
-with 'updates' disabled
+with enabled 'fedora-source'
+with enabled 'updates-source'
+with disabled 'fedora'
+with disabled 'updates'
 ```
 
 ### Switch system from rawhide
 Switch a system from Rawhide to F37:
 ```shellsession
 $ dnf-repo -d rawhide -e fedora distrosync --releasever 37 fedora-\*
-with 'rawhide' disabled
-with 'fedora' enabled
+with disabled 'rawhide'
+with enabled 'fedora'
 
 :
 ```
diff --git a/dnf-repo.cabal b/dnf-repo.cabal
--- a/dnf-repo.cabal
+++ b/dnf-repo.cabal
@@ -1,5 +1,5 @@
 name:                dnf-repo
-version:             0.5.1
+version:             0.5.2
 synopsis:            DNF wrapper tool to control repos
 description:
         A command-line wrapper of the dnf package manager to
@@ -20,7 +20,7 @@
                      ||  == 8.8.4
                      ||  == 8.10.7
                      ||  == 9.0.2
-                     ||  == 9.2.4
+                     ||  == 9.2.5
 data-dir:            data
 data-files:          copr.fedorainfracloud.orgCOLONOWNERCOLONREPO.repo
                      koji-REPO.repo
@@ -44,7 +44,8 @@
                        Glob,
                        http-directory >= 0.1.9,
                        simple-cmd,
-                       simple-cmd-args >= 0.1.8
+                       simple-cmd-args >= 0.1.8,
+                       unix
   default-language:    Haskell2010
   ghc-options:         -Wall
   if impl(ghc >= 8.0)
diff --git a/src/ExpireRepos.hs b/src/ExpireRepos.hs
--- a/src/ExpireRepos.hs
+++ b/src/ExpireRepos.hs
@@ -5,6 +5,7 @@
 
 import Control.Monad
 import Data.List (nub)
+import SimpleCmd (error')
 
 import Sudo
 
@@ -36,7 +37,7 @@
 clearExpired dryrun debug = do
   old <- read <$> readFile expiredFile :: IO [String]
   if null old
-    then return ()
+    then error' "no expired repos"
     else do
     mapM_ putStrLn old
     putStrLn ""
diff --git a/src/Main.hs b/src/Main.hs
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -6,7 +6,9 @@
 
 import Control.Monad.Extra
 import Data.Bifunctor (bimap)
+import Data.Char (isDigit)
 import Data.List.Extra
+import Data.Maybe (mapMaybe)
 import Network.HTTP.Directory (httpExists', (+/+))
 import SimpleCmd
 import SimpleCmdArgs
@@ -14,6 +16,7 @@
 import System.FilePath
 import System.IO (hSetBuffering, stdout, BufferMode(NoBuffering))
 import System.IO.Extra (withTempDir)
+import System.Posix.User (getEffectiveUserName,getLoginName)
 import System.Time.Extra (sleep)
 
 import Paths_dnf_repo (getDataFileName, version)
@@ -23,6 +26,7 @@
 
 main :: IO ()
 main = do
+  checkEuid
   simpleCmdArgs' (Just version)
     "DNF wrapper repo tool"
     "see https://github.com/juhp/dnf-repo#readme" $
@@ -88,7 +92,7 @@
     let actions = selectRepo exact nameStates modes
         moreoutput = not (null args) || null actions || listrepos
     unless (null actions || quiet) $ do
-      mapM_ (printAction save) actions
+      mapM_ putStrLn $ reduceOutput $ mapMaybe (printAction save) actions
       when moreoutput $
         warning ""
     outputs <-
@@ -106,24 +110,28 @@
         _ -> return False
     when (or outputs && (save || moreoutput) && not quiet) $
       warning ""
-    when save $ do
+    when save $
       if null actions
         then putStrLn "no changes to save"
         else do
-        prompt_ "Press Enter to save repo enabled state"
-        doSudo dryrun debug "dnf" $
-          "config-manager" : concatMap saveRepo actions
-      putStrLn ""
+        let changes = concatMap saveRepo actions
+        unless (null changes) $ do
+          ok <- yesno $ "Save changed repo" ++ " enabled state" ++ ['s' | length changes > 1]
+          when ok $
+            doSudo dryrun debug "dnf" $ "config-manager" : changes
     if null args
       then
-      when (null actions || listrepos) $
+      when (null actions || listrepos) $ do
+      when save $ putStrLn ""
       listRepos $ map (updateState actions) nameStates
       else do
       sleep 1
+      when save $ putStrLn ""
       let repoargs = concatMap changeRepo actions
           weakdeps = maybe [] (\w -> ["--setopt=install_weak_deps=" ++ show w]) mweakdeps
           quietopt = if quiet then ("-q" :) else id
-        in doSudo dryrun debug "dnf" $ quietopt repoargs ++ weakdeps ++ args
+          cachedir = ["--setopt=cachedir=/var/cache/dnf" </> relver | Just relver <- [maybeReleaseVer args]]
+        in doSudo dryrun debug "dnf" $ quietopt repoargs ++ cachedir ++ weakdeps ++ args
 
 -- FIXME pull non-fedora copr repo file
 addCoprRepo :: Bool -> Bool -> String -> IO ()
@@ -192,3 +200,30 @@
 filesWithExtension dir ext =
   filter (ext `isExtensionOf`) <$> listDirectory dir
 #endif
+
+maybeReleaseVer :: [String] -> Maybe String
+maybeReleaseVer args =
+  let releaseverOpt = "--releasever"
+  in
+    case findIndices (releaseverOpt `isPrefixOf`) args of
+      [] -> Nothing
+      is -> let lst = last is
+                opt = args !! lst
+                relver =
+                  case dropPrefix releaseverOpt opt of
+                    "" ->
+                      if lst == length args
+                      then error' $ releaseverOpt +-+ "without version"
+                      else args !! (lst + 1)
+                    '=':rv -> rv
+                    _ -> error' $ "could not parse" +-+ opt
+            in if all isDigit relver || relver `elem` ["rawhide","eln"]
+               then Just relver
+               else error' $ "unknown releasever:" +-+ relver
+
+checkEuid :: IO ()
+checkEuid = do
+  user <- getLoginName
+  euser <- getEffectiveUserName
+  when (user /= euser) $
+    warning "*Recommended not to run dnf-repo as root*"
diff --git a/src/YumRepoFile.hs b/src/YumRepoFile.hs
--- a/src/YumRepoFile.hs
+++ b/src/YumRepoFile.hs
@@ -5,6 +5,7 @@
   RepoState,
   ChangeEnable(Expire,UnExpire,Delete),
   printAction,
+  reduceOutput,
   selectRepo,
   changeRepo,
   saveRepo,
@@ -14,11 +15,12 @@
   )
 where
 
+import Data.Either (partitionEithers)
 import Data.List.Extra (dropPrefix, dropSuffix,
                         isPrefixOf, isInfixOf, isSuffixOf, nub,
-                        replace, sort, sortOn, stripInfix, trim)
+                        replace, sortOn, stripInfix, trim)
 import Data.Maybe (mapMaybe)
-import SimpleCmd (error', warning)
+import SimpleCmd (error')
 import System.FilePath.Glob (compile, match)
 
 type RepoState = (String,(Bool,FilePath))
@@ -65,30 +67,38 @@
                   | BaseURL String
   deriving (Eq,Ord,Show)
 
-printAction :: Bool -> ChangeEnable -> IO ()
+printAction :: Bool -> ChangeEnable -> Maybe (Either String String)
 printAction save (Disable r s) =
   if s
   then
+    Just $ Right $
     if save
-    then putStrLn $ "disable " ++ quote r
-    else warning $ "with " ++ quote r ++ " disabled"
-  else warning $ quote r ++ " already disabled"
+    then "disable " ++ quote r
+    else "with disabled " ++ quote r
+  else Just $ Left $ quote r ++ " already disabled"
 printAction save (Enable r s) =
   if s
   then
+    Just . Right $
     if save
-    then putStrLn $ "enable " ++ quote r
-    else warning $ "with " ++ quote r ++ " enabled"
-  else warning $ quote r ++ " already enabled"
-printAction _ (Expire _) = return ()
+    then "enable " ++ quote r
+    else "with enabled " ++ quote r
+  else Just $ Left $ quote r ++ " already enabled"
+printAction _ (Expire _) = Nothing
 printAction _ UnExpire =
-  putStrLn "unexpire:"
+  Just $ Right "unexpire:"
 printAction _ (Delete f s) =
   if s
-  then putStrLn $ "delete " ++ quote f
-  else warning $ quote f ++ " deletion skipped"
-printAction _ (BaseURL _) = return ()
+  then Just $ Right $ "delete " ++ quote f
+  else Just $ Left $ quote f ++ " deletion skipped"
+printAction _ (BaseURL _) = Nothing
 
+reduceOutput :: [Either String String] -> [String]
+reduceOutput os =
+  case partitionEithers os of
+    (is,[]) -> is
+    (_,cs) -> cs
+
 quote :: String -> String
 quote s = '\'' : s ++ "'"
 
@@ -136,7 +146,7 @@
 
 selectRepo :: Bool -> [RepoState] -> [Mode] -> [ChangeEnable]
 selectRepo exact repostates modes =
-  nub $ foldr selectRepo' [] (nub (sort modes))
+  nub $ foldr selectRepo' [] (nub modes)
   where
     selectRepo' :: Mode -> [ChangeEnable] -> [ChangeEnable]
     selectRepo' mode acc =
