public CommandResult <(List <FileSystemPath> items, FindCounts counts)> Find(
     CommandEvaluationContext context,
     [Parameter("search path")] DirectoryPath path,
     [Option("p", "select names that matches the pattern", true, true)] string pattern,
     [Option("i", "if set and p is set, perform a non case sensisitive search")] bool ignoreCase,
     [Option("f", "check pattern on fullname instead of name")] bool checkPatternOnFullName,
     [Option("c", "files that contains the string", true, true)] string contains,
     [Option("a", "print file system attributes")] bool attributes,
     [Option("s", "print short pathes")] bool shortPathes,
     [Option("all", "select files and directories")] bool all,
     [Option("d", "select only directories")] bool dirs,
     [Option("t", "search in top directory only")] bool top
     )
 {
     if (path.CheckExists())
     {
         var sp     = string.IsNullOrWhiteSpace(pattern) ? "*" : pattern;
         var counts = new FindCounts();
         var items  = FindItems(context, path.FullName, sp, top, all, dirs, attributes, shortPathes, contains, checkPatternOnFullName, counts, true, false, ignoreCase);
         var f      = DefaultForegroundCmd;
         counts.Elapsed = DateTime.Now - counts.BeginDateTime;
         if (items.Count > 0)
         {
             context.Out.Echoln();
         }
         context.Out.Echoln($"found {ColorSettings.Numeric}{Plur("file",counts.FilesCount,f)} and {ColorSettings.Numeric}{Plur("folder",counts.FoldersCount,f)}. scanned {ColorSettings.Numeric}{Plur("file",counts.ScannedFilesCount,f)} in {ColorSettings.Numeric}{Plur("folder",counts.ScannedFoldersCount,f)} during {TimeSpanDescription(counts.Elapsed, ColorSettings.Numeric.ToString(), f)}");
         return(new CommandResult <(List <FileSystemPath>, FindCounts)>((items, counts)));
     }
     return(new CommandResult <(List <FileSystemPath>, FindCounts)>((new List <FileSystemPath>(), new FindCounts()), ReturnCode.Error));
 }
Beispiel #2
0
 public CommandResult <List <TextFileInfo> > More(
     CommandEvaluationContext context,
     [Parameter("file or folder path")] WildcardFilePath path,
     [Option("h", "hide-line-numbers", "hide line numbers")] bool hideLineNumbers,
     [Option("r", "raw", "turn on raw output")] bool raw
     )
 {
     if (path.CheckExists(context))
     {
         var counts = new FindCounts();
         var items  = FindItems(context, path.FullName, path.WildCardFileName ?? "*", true, false, false, true, false, null, false, counts, false, false);
         var r      = new List <TextFileInfo>();
         foreach (var item in items)
         {
             PrintFile(context, (FilePath)item, hideLineNumbers, raw);
             r.Add(new TextFileInfo((FilePath)item, null, OSPlatform.Create("?"), null));
         }
         if (items.Count == 0)
         {
             context.Errorln($"more: no such file: {path.OriginalPath}");
             return(new CommandResult <List <TextFileInfo> >(new List <TextFileInfo> {
                 new TextFileInfo(new FilePath(path.OriginalPath), null, OSPlatform.Create("?"), null)
             }, ReturnCode.Error));
         }
         context.Out.ShowCur();
         return(new CommandResult <List <TextFileInfo> >(r));
     }
     else
     {
         return(new CommandResult <List <TextFileInfo> >(new List <TextFileInfo> {
             new TextFileInfo(new FilePath(path.FullName), null, OSPlatform.Create("?"), null)
         }, ReturnCode.Error));
     }
 }
        public CommandResult <(List <FileSystemPath> items, FindCounts counts)> Dir(
            CommandEvaluationContext context,
            [Parameter("path where to list files and folders. if not specified is equal to the current directory. use wildcards * and ? to filter files and folders names", true)] WildcardFilePath path,
            [Option("na", "do not print file system attributes")] bool noattributes,
            [Option("r", "also list files and folders in sub directories. force display files full path")] bool recurse,
            [Option("w", "displays file names on several columns so output fills console width (only if not recurse mode). disable print of attributes")] bool wide
            )
        {
            var r = new List <FileSystemPath>();

            path ??= new WildcardFilePath(Environment.CurrentDirectory);
            if (path.CheckExists())
            {
                var  counts                  = new FindCounts();
                var  items                   = FindItems(context, path.FullName, path.WildCardFileName ?? "*", !recurse, true, false, !noattributes, !recurse, null, false, counts, false, false);
                var  f                       = DefaultForegroundCmd;
                long totFileSize             = 0;
                var  cancellationTokenSource = new CancellationTokenSource();
                if (wide)
                {
                    noattributes = true;
                }
                void postCmd(object o, EventArgs e)
                {
                    sc.CancelKeyPress -= cancelCmd;
                    context.Out.Echoln($"{Tab}{ColorSettings.Numeric}{Plur("file", counts.FilesCount, f),-30}{HumanFormatOfSize(totFileSize, 2," ", ColorSettings.Numeric.ToString(), f)}");
                    context.Out.Echoln($"{Tab}{ColorSettings.Numeric}{Plur("folder", counts.FoldersCount, f),-30}{Drives.GetDriveInfo(path.FileSystemInfo.FullName,false, ColorSettings.Numeric.ToString(), f," ",2)}");
                }

                void cancelCmd(object o, ConsoleCancelEventArgs e)
                {
                    e.Cancel = true;
                    cancellationTokenSource.Cancel();
                }

                int printResult()
                {
                    var i = 0;

                    int maxitlength = 0;

                    counts.FilesCount = counts.FoldersCount = 0;
                    foreach (var item in items)
                    {
                        if (item.IsFile)
                        {
                            totFileSize += ((FileInfo)item.FileSystemInfo).Length;
                            counts.FilesCount++;
                        }
                        else
                        {
                            counts.FoldersCount++;
                        }
                        maxitlength = Math.Max(item.Name.Length, maxitlength);
                    }
                    maxitlength += 4;
                    var(id, left, top, right, bottom) = DotNetConsole.ActualWorkArea();
                    var nbcols = Math.Floor((double)(right - left + 1) / (double)maxitlength);

                    int nocol = 0;

                    foreach (var item in items)
                    {
                        if (cancellationTokenSource.IsCancellationRequested)
                        {
                            return(i);
                        }

                        item.Print(!noattributes, !recurse, "", (!wide || recurse || nocol == nbcols - 1) ? Br : "", (wide && !recurse) ? maxitlength : -1);
                        i++;
                        nocol++;
                        if (nocol == nbcols)
                        {
                            nocol = 0;
                        }
                    }
                    if (!recurse && wide && nocol < nbcols && nocol > 0)
                    {
                        context.Out.Echoln();
                    }
                    return(i);
                }

                sc.CancelKeyPress += cancelCmd;
                var task = Task.Run <int>(() => printResult(),
                                          cancellationTokenSource.Token);
                try
                {
                    task.Wait(cancellationTokenSource.Token);
                }
                catch (OperationCanceledException)
                {
                    var res = task.Result;
                }
                postCmd(null, null);
                return(new CommandResult <(List <FileSystemPath>, FindCounts)>((items, counts)));
            }
            return(new CommandResult <(List <FileSystemPath>, FindCounts)>((r, new FindCounts()), ReturnCode.Error));
        }
 public CommandResult <(List <(FileSystemPath source, FileSystemPath target)> items, FindCounts counts)> Mv(
     CommandEvaluationContext context,
     [Parameter("source: file/directory or several corresponding to a wildcarded path")] WildcardFilePath source,
     [Parameter(1, "destination: a file or a directory")] FileSystemPath dest,
     [Option("i", "prompt before overwrite")] bool interactive,
     [Option("v", "explain what is being done")] bool verbose
     )
 {
     if (source.CheckExists())
     {
         var counts      = new FindCounts();
         var items       = FindItems(context, source.FullName, source.WildCardFileName ?? "*", true, true, false, true, false, null, false, counts, false, false);
         var sourceCount = items.Count;
         List <(FileSystemPath src, FileSystemPath tgt)> r = new List <(FileSystemPath src, FileSystemPath tgt)>();
         if (sourceCount > 1)
         {
             if (dest.CheckExists())
             {
                 if (!dest.IsDirectory)
                 {
                     Errorln("dest must be a directory");
                     return(new CommandResult <(List <(FileSystemPath, FileSystemPath)> items, FindCounts counts)>(
                                (new List <(FileSystemPath, FileSystemPath)> {
                         (source, dest)
                     }, counts), ReturnCode.Error
                                ));
                 }
                 else
                 {
                     // move multiple source to dest
                     foreach (var item in items)
                     {
                         var msg = $"move {item.GetPrintableName()} to {dest.GetPrintableName()}";
                         if (!interactive || Confirm("mv: " + msg))
                         {
                             if (source.IsFile)
                             {
                                 var newdest = Path.Combine(dest.FullName, item.Name);
                                 r.Add((item, new FileSystemPath(newdest)));
                                 File.Move(item.FullName, newdest);
                             }
                             else
                             {
                                 var newdest = Path.Combine(dest.FullName, item.Name);
                                 Directory.Move(item.FullName, newdest);
                                 r.Add((item, new DirectoryPath(newdest)));
                             }
                             if (verbose)
                             {
                                 context.Out.Echoln(msg.Replace("move ", "moved "));
                             }
                         }
                     }
                 }
             }
         }
         else
         {
             if (dest.CheckExists(false))
             {
                 if (dest.IsDirectory)
                 {
                     // move one source to dest
                     var msg = $"move {source.GetPrintableNameWithWlidCard()} to {dest.GetPrintableName()}";
                     if (!interactive || Confirm("mv: " + msg))
                     {
                         if (source.IsFile)
                         {
                             var newdest = Path.Combine(dest.FullName, source.NameWithWildcard);
                             File.Move(source.FullNameWithWildcard, newdest);
                             r.Add((new FilePath(source.FullNameWithWildcard), new FilePath(newdest)));
                         }
                         else
                         {
                             var newdest = Path.Combine(dest.FullName, source.NameWithWildcard);
                             Directory.Move(source.FullName, newdest);
                             r.Add((source, new DirectoryPath(newdest)));
                         }
                         if (verbose)
                         {
                             context.Out.Echoln(msg.Replace("move ", "moved "));
                         }
                     }
                 }
                 else
                 {
                     // rename source (file) to dest (overwrite dest)
                     var msg = $"rename {source.GetPrintableNameWithWlidCard()} to {dest.GetPrintableName()}";
                     if (!interactive || Confirm("mv: " + msg))
                     {
                         dest.FileSystemInfo.Delete();
                         File.Move(source.FullNameWithWildcard, dest.FullName);
                         r.Add((new FilePath(source.FullNameWithWildcard), dest));
                         if (verbose)
                         {
                             context.Out.Echoln(msg.Replace("rename ", "renamed "));
                         }
                     }
                 }
             }
             else
             {
                 // rename source to dest
                 var msg = $"rename {source.GetPrintableNameWithWlidCard()} to {dest.GetPrintableName()}";
                 if (!interactive || Confirm("mv: " + msg))
                 {
                     if (source.IsFile)
                     {
                         File.Move(source.FullNameWithWildcard, dest.FullName);
                         r.Add((new FilePath(source.FullNameWithWildcard), dest));
                     }
                     else
                     {
                         Directory.Move(source.FullName, dest.FullName);
                         r.Add((source, dest));
                     }
                     if (verbose)
                     {
                         context.Out.Echoln(msg.Replace("rename ", "renamed "));
                     }
                 }
             }
         }
         return(new CommandResult <(List <(FileSystemPath source, FileSystemPath target)> items, FindCounts counts)>
                    ((r, counts)));
     }
     else
     {
         return(new CommandResult <(List <(FileSystemPath, FileSystemPath)> items, FindCounts counts)>
                    ((new List <(FileSystemPath, FileSystemPath)> {
             (source, null)
         }, new FindCounts())));
     }
 }
        public CommandResult <(List <FileSystemPath> items, FindCounts counts)> Rm(
            CommandEvaluationContext context,
            [Parameter("file or folder path")] WildcardFilePath path,
            [Option("r", "also remove files and folders in sub directories")] bool recurse,
            [Option("i", "prompt before any removal")] bool interactive,
            [Option("v", "explain what is being done")] bool verbose,
            [Option("d", "remove empty directories")] bool rmEmptyDirs,
            [Option("na", "do not print file system attributes when verbose")] bool noattributes,
            [Option("s", "don't remove any file/or folder, just simulate the operation (enable verbose)")] bool simulate
            )
        {
            var r      = new List <FileSystemPath>();
            var counts = new FindCounts();

            if (path.CheckExists())
            {
                var items = FindItems(context, path.FullName, path.WildCardFileName ?? "*", !recurse, true, false, !noattributes, !recurse, null, false, counts, false, false);
                var cancellationTokenSource = new CancellationTokenSource();
                verbose |= simulate;
                void cancelCmd(object o, ConsoleCancelEventArgs e)
                {
                    e.Cancel = true;
                    cancellationTokenSource.Cancel();
                };
                void postCmd(object o, EventArgs e)
                {
                    sc.CancelKeyPress -= cancelCmd;
                }

                List <FileSystemPath> processRemove()
                {
                    var r = new List <FileSystemPath>();

                    foreach (var item in items)
                    {
                        if (cancellationTokenSource.IsCancellationRequested)
                        {
                            return(r);
                        }
                        bool deleted = false;
                        if (item.IsFile)
                        {
                            if (item.FileSystemInfo.Exists && !r.Contains(item))
                            {
                                if (interactive)
                                {
                                    if (Confirm("rm: remove file " + item.GetPrintableName(recurse)) && !simulate)
                                    {
                                        if (!simulate)
                                        {
                                            item.FileSystemInfo.Delete();
                                        }
                                        deleted = true;
                                    }
                                }
                                else
                                {
                                    if (!simulate)
                                    {
                                        item.FileSystemInfo.Delete();
                                    }
                                    deleted = true;
                                }
                            }
                        }
                        else
                        {
                            var dp = (DirectoryPath)item;
                            if ((rmEmptyDirs && dp.IsEmpty) || recurse)
                            {
                                if (dp.DirectoryInfo.Exists && !r.Contains(dp))
                                {
                                    if (interactive)
                                    {
                                        r.Merge(RecurseInteractiveDeleteDir(context, dp, simulate, noattributes, verbose, cancellationTokenSource));
                                    }
                                    else
                                    {
                                        if (!simulate)
                                        {
                                            dp.DirectoryInfo.Delete(recurse);
                                        }
                                        deleted = true;
                                    }
                                }
                            }
                        }
                        if (deleted)
                        {
                            if (verbose)
                            {
                                item.Print(!noattributes, !recurse, "", Br, -1, "removed ");
                            }
                            r.Add(item);
                        }
                    }
                    return(r);
                };
                sc.CancelKeyPress += cancelCmd;
                var task = Task.Run(() => processRemove(), cancellationTokenSource.Token);
                try
                {
                    task.Wait(cancellationTokenSource.Token);
                }
                catch (OperationCanceledException)
                {
                    r = task.Result;
                }
                postCmd(null, null);
                return(new CommandResult <(List <FileSystemPath>, FindCounts)>((r, counts), ReturnCode.OK));
            }
            else
            {
                return(new CommandResult <(List <FileSystemPath>, FindCounts)>((r, counts), ReturnCode.Error));
            }
        }
Beispiel #6
0
        public CommandResult <List <FilePath> > CheckIntegrity(
            CommandEvaluationContext context,
            [Parameter("path of a file to be checked or path from where find files to to be checked")] FileSystemPath fileOrDir,
            [Option("p", "pattern", "select names that matches the pattern", true, true)] string pattern,
            [Option("i", "non-sensitive", "if set and p is set, perform a non case sensisitive search")] bool ignoreCase,
            [Option("a", "attr", "print file system attributes")] bool printAttr,
            [Option("t", "top", "search in top directory only")] bool top,
            [Option("q", "quiet", "quiet mode: do not print error message below corrupted file name")] bool quiet,
            [Option("r", "ratio", "acceptable ratio of non printable characters", true, true)] double ratio     = 30,
            [Option("s", "min-size", "minimum size of analysed part of the text", true, true)] int minSeqLength = 1024
            )
        {
            var r = new List <FilePath>();

            if (fileOrDir.CheckExists(context))
            {
                if (fileOrDir.IsFile)
                {
                    var(isValid, filePath) = CheckIntegrity(context, new FilePath(fileOrDir.FullName), ratio, printAttr, minSeqLength, quiet);
                    if (!isValid)
                    {
                        r.Add(filePath);
                    }
                    return(new CommandResult <List <FilePath> >(r));
                }
                else
                {
                    var sp      = string.IsNullOrWhiteSpace(pattern) ? "*" : pattern;
                    var counts  = new FindCounts();
                    var items   = FindItems(context, fileOrDir.FullName, sp, top, false, false, printAttr, false, null, false, counts, false, false, ignoreCase);
                    var f       = context.ShellEnv.Colors.Default.ToString();
                    var elapsed = DateTime.Now - counts.BeginDateTime;
                    context.Out.Echoln($"found {context.ShellEnv.Colors.Numeric}{Plur("file", counts.FilesCount, f)} and {context.ShellEnv.Colors.Numeric}{Plur("folder", counts.FoldersCount, f)}. scanned {context.ShellEnv.Colors.Numeric}{Plur("file", counts.ScannedFilesCount, f)} in {context.ShellEnv.Colors.Numeric}{Plur("folder", counts.ScannedFoldersCount, f)} during {TimeSpanDescription(elapsed, context.ShellEnv.Colors.Numeric.ToString(), f)}");
                    if (items.Count > 0)
                    {
                        context.Out.Echoln($"analyzing files ({counts.FilesCount})...");
                        int corruptedFilesCount = 0;
                        foreach (var item in items)
                        {
                            if (context.CommandLineProcessor.CancellationTokenSource.Token.IsCancellationRequested)
                            {
                                break;
                            }
                            if (item.IsFile)
                            {
                                if (!CheckIntegrity(context, (FilePath)item, ratio, printAttr, minSeqLength, quiet).isValid)
                                {
                                    corruptedFilesCount++;
                                    r.Add((FilePath)item);
                                }
                            }
                        }
                        if (corruptedFilesCount > 0)
                        {
                            context.Out.Echoln();
                        }
                        var crprt = (double)corruptedFilesCount / (double)counts.FilesCount * 100d;
                        context.Out.Echoln($"found {context.ShellEnv.Colors.Numeric}{Plur("corrupted file", corruptedFilesCount, f)} in {context.ShellEnv.Colors.Numeric}{Plur("file", counts.FilesCount, f)} corruption ratio={Cyan}{crprt}%");
                        return(new CommandResult <List <FilePath> >(r));
                    }
                    else
                    {
                        return(new CommandResult <List <FilePath> >(r));
                    }
                }
            }
            else
            {
                return(new CommandResult <List <FilePath> >(new List <FilePath> {
                    new FilePath(fileOrDir.FullName)
                }, ReturnCode.Error));
            }
        }
        /// <summary>
        /// search items in file system
        /// </summary>
        /// <param name="context"></param>
        /// <param name="path"></param>
        /// <param name="pattern"></param>
        /// <param name="top"></param>
        /// <param name="all"></param>
        /// <param name="dirs"></param>
        /// <param name="attributes"></param>
        /// <param name="shortPathes"></param>
        /// <param name="contains"></param>
        /// <param name="checkPatternOnFullName"></param>
        /// <param name="counts"></param>
        /// <param name="print"></param>
        /// <param name="alwaysSelectDirs"></param>
        /// <param name="ignoreCase"></param>
        /// <param name="printMatches"></param>
        /// <returns></returns>
        public static List <FileSystemPath> FindItems(
            CommandEvaluationContext context,
            string path,
            string pattern,
            bool top,
            bool all,
            bool dirs,
            bool attributes,
            bool shortPathes,
            string contains,
            bool checkPatternOnFullName,
            FindCounts counts,
            bool print,
            bool alwaysSelectDirs = false,
            bool ignoreCase       = false,
            bool printMatches     = false)
        {
            bool isFile = File.Exists(path);
            var  dinf   = isFile ? null : new DirectoryInfo(path);
            List <FileSystemPath> items = new List <FileSystemPath>();
            bool hasPattern             = !string.IsNullOrWhiteSpace(pattern);
            bool hasContains            = !string.IsNullOrWhiteSpace(contains);

            if (context.CommandLineProcessor.IsCancellationRequested)
            {
                return(items);
            }

            try
            {
                if (!isFile)
                {
                    counts.ScannedFoldersCount++;
                }
                var scan = isFile ? new FileSystemInfo[] { new FileInfo(path) }
                    : dinf.GetFileSystemInfos();

                foreach (var fsinf in scan)
                {
                    var sitem = FileSystemPath.Get(fsinf);

                    if (sitem.IsDirectory)
                    {
                        if ((dirs || all) && (alwaysSelectDirs || (!hasPattern || MatchWildcard(pattern, checkPatternOnFullName ? sitem.FileSystemInfo.FullName : sitem.FileSystemInfo.Name, ignoreCase))))
                        {
                            items.Add(sitem);
                            if (print)
                            {
                                sitem.Echo(new EchoEvaluationContext(context.Out, context, new FileSystemPathFormattingOptions(attributes, shortPathes, "", Br)));
                            }
                            counts.FoldersCount++;
                        }
                        else
                        {
                            sitem = null;
                        }

                        if (!top)
                        {
                            items.AddRange(FindItems(context, fsinf.FullName, pattern, top, all, dirs, attributes, shortPathes, contains, checkPatternOnFullName, counts, print, alwaysSelectDirs, ignoreCase, printMatches));
                        }
                    }
                    else
                    {
                        counts.ScannedFilesCount++;
                        if (!dirs && (!hasPattern || MatchWildcard(pattern, checkPatternOnFullName ? sitem.FileSystemInfo.FullName : sitem.FileSystemInfo.Name, ignoreCase)))
                        {
                            var matches = new List <string>();
                            if (hasContains)
                            {
                                try
                                {
                                    if (!FilePath.IsBinaryFile(sitem.FileSystemInfo.FullName))
                                    {
                                        // skip non text files
                                        var(lines, platform, eol) = TextFileReader.ReadAllLines(sitem.FileSystemInfo.FullName);
                                        bool match = false;
                                        for (int i = 0; i < lines.Length; i++)
                                        {
                                            if (lines[i].Contains(contains))
                                            {
                                                match |= true;
                                                if (printMatches)
                                                {
                                                    int j   = lines[i].IndexOf(contains);
                                                    var loc = $"\t{context.ShellEnv.Colors.MarginText} {$"line {i},col {j}".PadRight(24)} ";
                                                    var txt = lines[i].Replace(contains, context.ShellEnv.Colors.TextExtractSelectionBlock + contains + Rdc + context.ShellEnv.Colors.TextExtract);
                                                    matches.Add(loc + context.ShellEnv.Colors.TextExtract + txt + Rdc);
                                                }
                                            }
                                        }
                                        if (!match)
                                        {
                                            sitem = null;
                                        }
                                    }
                                    else
                                    {
                                        sitem = null;
                                    }
                                }
                                catch (Exception ex)
                                {
                                    context.Errorln($"file read error: {ex.Message} when accessing file: {sitem.PrintableFullName}");
                                    sitem = null;
                                }
                            }
                            if (sitem != null)
                            {
                                counts.FilesCount++;
                                items.Add(sitem);
                                if (print && context.Out.IsModified && matches.Count > 0)
                                {
                                    context.Out.Echoln("");
                                }
                                if (print)
                                {
                                    sitem.Echo(new EchoEvaluationContext(context.Out, context, new FileSystemPathFormattingOptions(attributes, shortPathes, "", Br)));
                                }
                            }
                            if (matches.Count > 0)
                            {
                                matches.ForEach(x => context.Out.Echoln(x));
                            }
                            matches.Clear();
                        }
                        else
                        {
                            sitem = null;
                        }
                    }

                    if (context.CommandLineProcessor.IsCancellationRequested)
                    {
                        return(items);
                    }
                }
                return(items);
            }
            catch (UnauthorizedAccessException)
            {
                context.Errorln($"unauthorized access to {new DirectoryPath(path).PrintableFullName}");
                return(items);
            }
        }
        public static List <FileSystemPath> FindItems(
            CommandEvaluationContext context,
            string path,
            string pattern,
            bool top,
            bool all,
            bool dirs,
            bool attributes,
            bool shortPathes,
            string contains,
            bool checkPatternOnFullName,
            FindCounts counts,
            bool print,
            bool alwaysSelectDirs = false,
            bool ignoreCase       = false)
        {
            var dinf = new DirectoryInfo(path);
            List <FileSystemPath> items = new List <FileSystemPath>();
            bool hasPattern             = !string.IsNullOrWhiteSpace(pattern);
            bool hasContains            = !string.IsNullOrWhiteSpace(contains);

            if (context.CommandLineProcessor.CancellationTokenSource.Token.IsCancellationRequested)
            {
                return(items);
            }

            try
            {
                counts.ScannedFoldersCount++;
                var scan = dinf.GetFileSystemInfos();

                foreach (var fsinf in scan)
                {
                    var sitem = FileSystemPath.Get(fsinf);

                    if (sitem.IsDirectory)
                    {
                        if ((dirs || all) && (alwaysSelectDirs || (!hasPattern || MatchWildcard(pattern, checkPatternOnFullName ? sitem.FileSystemInfo.FullName : sitem.FileSystemInfo.Name, ignoreCase))))
                        {
                            items.Add(sitem);
                            if (print)
                            {
                                sitem.Print(attributes, shortPathes, "", Br);
                            }
                            counts.FoldersCount++;
                        }
                        else
                        {
                            sitem = null;
                        }

                        if (!top)
                        {
                            items.AddRange(FindItems(context, fsinf.FullName, pattern, top, all, dirs, attributes, shortPathes, contains, checkPatternOnFullName, counts, print, alwaysSelectDirs, ignoreCase));
                        }
                    }
                    else
                    {
                        counts.ScannedFilesCount++;
                        if (!dirs && (!hasPattern || MatchWildcard(pattern, checkPatternOnFullName ? sitem.FileSystemInfo.FullName : sitem.FileSystemInfo.Name, ignoreCase)))
                        {
                            if (hasContains)
                            {
                                try
                                {
                                    var str = File.ReadAllText(sitem.FileSystemInfo.FullName);
                                    if (!str.Contains(contains))
                                    {
                                        sitem = null;
                                    }
                                }
                                catch (Exception ex)
                                {
                                    Errorln($"file read error: {ex.Message} when accessing file: {sitem.PrintableFullName}");
                                }
                            }
                            if (sitem != null)
                            {
                                counts.FilesCount++;
                                items.Add(sitem);
                                if (print)
                                {
                                    sitem.Print(attributes, shortPathes, "", Br);
                                }
                            }
                        }
                        else
                        {
                            sitem = null;
                        }
                    }

                    if (context.CommandLineProcessor.CancellationTokenSource.Token.IsCancellationRequested)
                    {
                        return(items);
                    }
                }
                return(items);
            }
            catch (UnauthorizedAccessException)
            {
                Errorln($"unauthorized access to {new DirectoryPath(path).PrintableFullName}");
                return(items);
            }
        }
        public CommandResult <(List <FileSystemPath> items, FindCounts counts)> Dir(
            CommandEvaluationContext context,
            [Parameter("path where to list files and folders. if not specified is set to the current directory. use wildcards * and ? to filter files and folders names", true)] WildcardFilePath path,
            [Option("n", "name", "names only: do not print file system attributes")] bool noattributes,
            [Option("r", "recurse", "also list files and folders in sub directories. force display files full path")] bool recurse,
            [Option("w", "wide", "displays file names on several columns so output fills console width (only if not recurse mode). disable print of attributes")] bool wide,
            [Option("f", "file", "filter list on files (exclude directories). avoid -d")] bool filesOnly,
            [Option("d", "dir", "filter list on directories (exclude files)")] bool dirsOnly,
            [Option("s", "sort", "sort list of files and folders. default is alphabetic, mixing files and folders", true, true)] DirSort sort = DirSort.dir | DirSort.name
            )
        {
            var r = new List <FileSystemPath>();

            path ??= new WildcardFilePath(Environment.CurrentDirectory);
            if (path.CheckExists(context))
            {
                var counts = new FindCounts();

                var items = FindItems(context, path.FullName, path.WildCardFileName ?? "*", !recurse, true, false, !noattributes, !recurse, null, false, counts, false, false);

                if (filesOnly)
                {
                    dirsOnly = false;
                }
                if (filesOnly)
                {
                    items = items.Where(x => x.IsFile).ToList();
                }
                if (dirsOnly)
                {
                    items = items.Where(x => x.IsDirectory).ToList();
                }

                // apply sorts - default (.net) is name (==DirSort.name==1) - possible DirSort.not_specified==0 == default sort by name
                if (sort != DirSort.not_specified && sort != DirSort.name)
                {
                    IEnumerable <IGrouping <string, FileSystemPath> > grp = null;

                    if (sort.HasFlag(DirSort.ext))
                    {
                        grp = items.GroupBy((x) => Path.GetExtension(x.FullName));
                    }
                    else if (sort.HasFlag(DirSort.file))
                    {
                        grp = items.GroupBy((x) => x.IsDirectory + "");
                    }
                    else if (sort.HasFlag(DirSort.dir))
                    {
                        grp = items.GroupBy((x) => x.IsFile + "");
                    }
                    else if (sort.HasFlag(DirSort.size))
                    {
                        grp = items.GroupBy((x) => !x.IsFile + "");
                    }

                    var lst = grp.ToList();
                    lst.Sort((x, y) => x.Key.CompareTo(y.Key)); // sort alpha
                    if (!sort.HasFlag(DirSort.file) && !sort.HasFlag(DirSort.dir) && !sort.HasFlag(DirSort.size) && sort.HasFlag(DirSort.rev))
                    {
                        lst.Reverse();
                    }

                    items.Clear();
                    foreach (var g in lst)
                    {
                        var glst = g.ToList();
                        if (!sort.HasFlag(DirSort.size))
                        {
                            glst.Sort((x, y) => x.FullName.CompareTo(y.FullName));    // sort alpha
                        }
                        else
                        {
                            glst.Sort((x, y) => x.Length.CompareTo(y.Length));    // sort size
                        }
                        if (sort.HasFlag(DirSort.rev))
                        {
                            glst.Reverse();
                        }
                        items.AddRange(glst);
                    }
                }

                var  f                       = DefaultForegroundCmd;
                long totFileSize             = 0;
                var  cancellationTokenSource = new CancellationTokenSource();
                if (wide)
                {
                    noattributes = true;
                }

                // here we self handle the console Cancel event so we can output a partial result even if the seek phasis has been broke. print can also be broke.
                // TODO: seems there is now a simple way to do this. Just use the context cancellation token (as in findItems) :
                // TODO: if (context.CommandLineProcessor.CancellationTokenSource.Token.IsCancellationRequested) // then cancel com

                void postCmd(object o, EventArgs e)
                {
                    sc.CancelKeyPress -= cancelCmd;
                    context.Out.Echoln($"{Tab}{context.ShellEnv.Colors.Numeric}{Plur("file", counts.FilesCount, f),-30}{HumanFormatOfSize(totFileSize, 2, " ", context.ShellEnv.Colors.Numeric.ToString(), f)}");
                    context.Out.Echoln($"{Tab}{context.ShellEnv.Colors.Numeric}{Plur("folder", counts.FoldersCount, f),-30}{Drives.GetDriveInfo(path.FileSystemInfo.FullName, false, context.ShellEnv.Colors.Numeric.ToString(), f, " ", 2)}");
                }

                void cancelCmd(object o, ConsoleCancelEventArgs e)
                {
                    e.Cancel = true;
                    cancellationTokenSource.Cancel();
                }

                int printResult()
                {
                    var i = 0;

                    int maxitlength = 0;

                    counts.FilesCount = counts.FoldersCount = 0;
                    foreach (var item in items)
                    {
                        if (item.IsFile)
                        {
                            totFileSize += ((FileInfo)item.FileSystemInfo).Length;
                            counts.FilesCount++;
                        }
                        else
                        {
                            counts.FoldersCount++;
                        }
                        maxitlength = Math.Max(item.Name.Length, maxitlength);
                    }
                    maxitlength += 4;
                    var(id, left, top, right, bottom) = DotNetConsole.ActualWorkArea();
                    var nbcols = Math.Floor((double)(right - left + 1) / (double)maxitlength);

                    int nocol = 0;

                    foreach (var item in items)
                    {
                        if (cancellationTokenSource.IsCancellationRequested)
                        {
                            return(i);
                        }

                        item.Echo(
                            new EchoEvaluationContext(
                                context.Out, context,
                                new FileSystemPathFormattingOptions(
                                    !noattributes,
                                    !recurse,
                                    "",
                                    ((!wide || recurse || nocol == nbcols - 1) ? Br : ""),
                                    (wide && !recurse) ? maxitlength : -1, "")));
                        i++;
                        nocol++;
                        if (nocol == nbcols)
                        {
                            nocol = 0;
                        }
                    }
                    if (!recurse && wide && nocol < nbcols && nocol > 0)
                    {
                        context.Out.Echoln();
                    }
                    return(i);
                }

                sc.CancelKeyPress += cancelCmd;
                var task = Task.Run <int>(() => printResult(),
                                          cancellationTokenSource.Token);
                try
                {
                    task.Wait(cancellationTokenSource.Token);
                }
                catch (OperationCanceledException)
                {
                    var res = task.Result;
                }
                postCmd(null, null);
                return(new CommandResult <(List <FileSystemPath>, FindCounts)>((items, counts)));
            }
            return(new CommandResult <(List <FileSystemPath>, FindCounts)>((r, new FindCounts()), ReturnCode.Error));
        }