lsfrom 0.1.1.1 → 1.0
raw patch · 7 files changed
+147/−45 lines, 7 filesdep +safedep ~simple-cmddep ~simple-cmd-argssetup-changed
Dependencies added: safe
Dependency ranges changed: simple-cmd, simple-cmd-args
Files
- ChangeLog.md +8/−0
- LICENSE +1/−1
- Main.hs +70/−14
- README.md +38/−12
- Setup.hs +0/−2
- lsfrom.cabal +11/−9
- test/tests.hs +19/−7
ChangeLog.md view
@@ -1,5 +1,13 @@ # Release history for lsfrom +## 1.0 (2024-01-07)+- add --after: start with the next file after the specified one+- add --until: include files only up to the specified file+- add --before: changes the behaviour of --until+- add --strict: error if the specified files do not exist+- the short option for --all is now -A+-+ ## 0.1.1.1 (2022-04-24) - add missing test files - fix testsuite for lts 12 and earlier
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2018, Jens Petersen+Copyright (c) 2018 and 2021-2024, Jens Petersen All rights reserved.
Main.hs view
@@ -1,27 +1,83 @@ module Main (main) where +import Data.List (dropWhileEnd) import SimpleCmd import SimpleCmdArgs+import Safe (tailSafe) import System.FilePath import Paths_lsfrom (version) main :: IO () main =- simpleCmdArgs (Just version) "List files from pattern"- "lsfrom lists the files in a directory that follow from the given pattern" $- lsfrom <$> switchWith 'a' "all" "include hidden (dot) files" <*> strArg "FILEPAT"+ simpleCmdArgs (Just version) "List directories files starting from file"+ "lsfrom lists the files in a directory that follow from the given file" $+ lsfrom+ <$> switchWith 's' "strict" "fail if specified file(s) do not exist"+ <*> switchWith 'A' "all" "include hidden (dot) files"+ <*> switchWith 'a' "after" "files after STARTFILE [default: from STARTFILE]"+ <*> optional (removeTrailing <$> strOptionWith 'u' "until" "LASTFILE" "files until FILE")+ <*> switchWith 'b' "before" "files before LASTFILE (only affects --until)"+ <*> (removeTrailing <$> strArg "STARTFILE")+ where+ removeTrailing "" = ""+ removeTrailing "/" = "/"+ removeTrailing f =+ if last f == '/'+ then removeTrailing $ init f+ else f -lsfrom :: Bool -> FilePath -> IO ()-lsfrom hidden file = do- let isdir = last file == pathSeparator- file' = if isdir then takeDirectory file else file- (dir,pat) = splitFileName file'- pat' = pat ++ if isdir then [pathSeparator] else ""- sorted <- do- files <- cmd "ls" $ ["-A" | hidden || head pat == '.'] ++ [dir]- lines <$> cmdStdIn "sort" [] (pat' ++ "\n" ++ files)- let result = tail $ dropWhile (pat' /=) sorted- mapM_ (putStrLn . (renderDir dir </>)) result+lsfrom :: Bool -> Bool -> Bool -> Maybe FilePath -> Bool -> FilePath -> IO ()+lsfrom strict hidden after muntil before file =+ case reverse (splitDirectories file) of+ [] -> error' "empty filename!"+ ("":_) -> error' "empty filename!"+ (entry@(e:_):revdir) -> do+ let dir = joinPath $ reverse revdir+ lsout <- cmd "ls" $+ ["-A" | hidden || e == '.'] ++ [dir | not (null dir)]+ let lsEntries = lines lsout+ entryExists = entry `elem` lsEntries+ muntilExists =+ case muntil of+ Nothing -> Nothing+ Just until' ->+ Just (until', until' `elem` lsEntries)+ listingWith <-+ lines <$>+ if strict+ then return lsout+ else if entryExists+ then case muntilExists of+ Just (until',False) ->+ sortLs $ prepend until' lsout+ _ -> return lsout+ else case muntilExists of+ Just (until',False) ->+ sortLs $+ if entry == until'+ then prepend entry lsout+ else prepend entry $ prepend until' lsout+ _ -> sortLs $ prepend entry lsout+ let result =+ takeUntil muntilExists $+ (if after || not entryExists then tailSafe else id) $+ dropWhile (entry /=) listingWith+ mapM_ (putStrLn . (renderDir dir </>)) result where renderDir dir = if dir == "./" then "" else dir++ sortLs :: String -> IO String+ sortLs = cmdStdIn "sort" []++ prepend :: String -> String -> String+ prepend miss ls = miss ++ '\n' : ls++ takeUntil :: Maybe (String,Bool) -> [String] -> [String]+ takeUntil Nothing es = es+ takeUntil (Just _) [] = []+ takeUntil (Just (until',exists)) es =+ case dropWhileEnd (until' /=) es of+ [] -> []+ es' ->+ (if before || not exists then init else id) es'
README.md view
@@ -1,38 +1,64 @@ # lsfrom -`lsfrom` lists the files and dirs in a directory starting from the first one-that matches the given filename prefix or the first filename after that-if there is no match, using the current locale collation order.+`lsfrom` lists the files and dirs in a directory starting from+the given filename or the first file after that+if the file does not exist, using the current locale collation order. -```+```shellsession $ ls A a B C-$ lsfrom a-a-B-C+$ echo $(lsfrom a)+a B C $ lsfrom a/+a B C $ lsfrom /sy /sys-/sysroot /tmp /usr /var ``` -This may be useful for continuing a script on the content of a directory+It can be useful for continuing a script on the entries of a directory after a failure, etc: ```shellsession $ myscript.sh $(lsfrom next) ``` +or only running a command on a subrange of files in a directory.++## Usage++`$ lsfrom --version`+```+1.0+```+`$ lsfrom -h`+```+List directories files starting from file++Usage: lsfrom [--version] [-s|--strict] [-A|--all] [-a|--after]+ [-u|--until LASTFILE] [-b|--before] STARTFILE++ lsfrom lists the files in a directory that follow from the given file++Available options:+ -h,--help Show this help text+ --version Show version+ -s,--strict fail if specified file(s) do not exist+ -A,--all include hidden (dot) files+ -a,--after files after STARTFILE [default: from STARTFILE]+ -u,--until LASTFILE files until FILE+ -b,--before files before LASTFILE (only affects --until)+```+ ## Requirements -It uses system `ls` order to preserve locale collation-and requires ls with the `-A` option to exclude `.` and `..` (eg coreutils).+It uses `ls` to list files with locale sorting+and requires the `-A` option to exclude `.` and `..` (ie coreutils).+It also uses `sort` when injecting missing marker files. It has been tested on Linux. ## Installation
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
lsfrom.cabal view
@@ -1,10 +1,11 @@+cabal-version: 2.0 name: lsfrom-version: 0.1.1.1+version: 1.0 synopsis: List directory files starting from a specific name description:- `lsfrom` lists the contents of a directory starting from- a particular prefix and those files after it- with respect to locale collation.+ `lsfrom` lists the entries of a directory starting from+ a particular file and then the files after it+ with respect to locale collation ordering. This can be useful for example for continuing a command or script in a directory after a failure.@@ -12,12 +13,11 @@ license-file: LICENSE author: Jens Petersen <petersen@redhat.com> maintainer: Jens Petersen <petersen@redhat.com>-copyright: 2018,2021-2022 Jens Petersen+copyright: 2018,2021-2024 Jens Petersen category: Utility homepage: https://github.com/juhp/lsfrom bug-reports: https://github.com/juhp/lsfrom/issues build-type: Simple-cabal-version: 1.18 extra-doc-files: ChangeLog.md README.md extra-source-files: test/files/A@@ -31,10 +31,12 @@ executable lsfrom main-is: Main.hs other-modules: Paths_lsfrom+ autogen-modules: Paths_lsfrom build-depends: base < 5, filepath,- simple-cmd,- simple-cmd-args >= 0.1.1+ safe,+ simple-cmd >= 0.1.4,+ simple-cmd-args >= 0.1.2 default-language: Haskell2010 ghc-options: -fwarn-missing-signatures -Wall if impl(ghc >= 8.0)@@ -63,4 +65,4 @@ , directory >= 1.2.3.0 , filepath , simple-cmd >= 0.1.1- build-tools: lsfrom+ build-tool-depends: lsfrom:lsfrom
test/tests.hs view
@@ -4,20 +4,32 @@ import SimpleCmd import System.Directory -lsfrom :: (FilePath, String, [String]) -> IO ()-lsfrom (dir, arg, expect) = do+type Test = (FilePath, [String], [String])++lsfrom :: Test -> IO ()+lsfrom (dir, args, expect) = do out <- withCurrentDirectory dir $- cmdLines "lsfrom" [arg]+ cmdLines "lsfrom" args unless (out == expect) $ do- cmdN "lsfrom" [arg]+ cmdN "lsfrom" args putStrLn $ "returned> " ++ show out putStrLn $ "expected> " ++ show expect error' "failed" -tests :: [(FilePath, String,[String])]+tests :: [Test] tests =- [ ("test/files", "B", ["B","C"])- , ("test", "files/B/", ["files/C"])+ [ ("test/files", ["B"], ["B","C"])+ , ("test/files", ["-a", "B"], ["C"])+ , ("test", ["files/C/"], ["files/C"])+ , ("test/files", ["Bb"], ["C"])+ , ("test/files", ["-u", "B", "A"], ["A", "B"])+ , ("test/files", ["-u", "C", "-b", "A"], ["A", "B"])+ , ("test/files", ["-u", "C", "-b", "-a", "A"], ["B"])+ , ("test/files", ["-u", "A", "-b", "-a", "C"], [])+ , ("test/files", ["-u", "B", "-b", "B"], [])+ , ("test/files", ["-u", "B", "-a", "B"], [])+ , ("test/files", ["-u", "B", "-a", "-b", "B"], [])+ , ("test/files", ["-s", "-u", "B", "A"], ["A", "B"]) ] main :: IO ()