int CopyFiles(Dictionary <string, string> opts, Dictionary <string, string> pars, List <string> srcFilesList)
        {
            // Replicate a given a list of files to a destination directory
            // e.g. :  consutil1 -v oper=findf src=c:\junk des=g:\junk\_des\d1\
            if (ValidateDestinationDir(opts, pars) != 0)
            {
                return(lastErr);
            }

            string        des            = pars["des"];
            List <string> desFilesList   = new List <string>(); // Accumulate src/des pairs so can parallel copies
            Hashtable     directoryNames = new Hashtable();     // to cut down on probes for create
            char          sdSepChar      = '|';

            foreach (var sName in srcFilesList)
            {
                try {
                    FileInfo sfi     = new FileInfo(sName);
                    string   srcPath = sfi.DirectoryName;
                    string   dPath   = Path.Combine(des, srcPath.Substring(3));
                    if (directoryNames[dPath] == null)
                    {
                        if (!Directory.Exists(dPath))
                        {
                            Directory.CreateDirectory(dPath);
                            directoryNames[dPath] = 1;
                        }
                    }
                    string dName = Path.Combine(dPath, sfi.Name);
                    desFilesList.Add(sName + sdSepChar + dName);
                }
                catch (Exception exc) {
                    string emsg = SimpleUtils.ExceptionMsg(exc, "dirs for CopyFiles at " + sName);
                    Console.WriteLine(emsg);
                    myLog(emsg);
                }
            }
            ParallelLoopResult result = Parallel.ForEach(desFilesList, n => {
                try {
                    string[] sdPair = n.Split(sdSepChar);
                    File.Copy(sdPair[0], sdPair[1]);
                }
                catch (Exception exc) {
                    string emsg = SimpleUtils.ExceptionMsg(exc, "parallel CopyFiles at " + n);
                    Console.WriteLine(emsg);
                    myLog(emsg);
                }
            });

            myLog(SimpleUtils.InfoMsg("parallel copy result", (result.IsCompleted) ? 0 : 1, "oper.copy walk"));
            return(lastErr);
        }
        int DedupList(Dictionary <string, string> opts, Dictionary <string, string> pars, List <string> srcFilesList, List <string> desDirsList)
        {
            Hashtable htDirs = new Hashtable();

            foreach (var sName in srcFilesList)
            {
                if (htDirs[sName] == null)
                {
                    htDirs.Add(sName, 1);
                    desDirsList.Add(sName);
                }
            }
            myLog(SimpleUtils.InfoMsg("found", desDirsList.Count, "DedupList"));
            return(lastErr);
        }
        private void CopyDirectories(string[] args, Dictionary <string, string> opts, Dictionary <string, string> pars, List <string> resultFinalList)
        {
            Parallel.ForEach(resultFinalList, dir => {
                Dictionary <string, string> lpars = pars.Keys.ToDictionary(par => par, par => pars[par]);
                lpars["oper"] = "findf";
                lpars["src"]  = dir;
                try {
                    FileSystemOps fso = new FileSystemOps(myLog);
                    fso.FilterFSInfo(args, opts, lpars);
                }
                catch (Exception exc) {
                    string emsg = SimpleUtils.ExceptionMsg(exc, "CopyDirectories parallel exception at " + dir);
                    Console.WriteLine(emsg);
                    myLog(emsg);
                }
            });


            string msg = SimpleUtils.InfoMsg("findd with des length ", resultFinalList.Count, "oper.findd walk");

            Console.WriteLine(msg);
            myLog(msg);
        }
        int InvokeDirectories(string[] args, Dictionary <string, string> opts, Dictionary <string, string> pars, List <string> srcFilesList)
        {
            List <string> desFilesList = new List <string>(); // Accumulate src/des pairs so can parallel copies
            char          sdSepChar    = '|';
            string        des          = null;

            if (pars.ContainsKey("des"))
            {
                des = pars["des"];
            }
            foreach (var sName in srcFilesList)    // accumulate into a list that can remain unaltered during a parallel
            {
                FileInfo sfi     = new FileInfo(sName);
                string   srcPath = sfi.DirectoryName;
                string   dPath   = (des != null)?Path.Combine(des, srcPath.Substring(3)):""; // might not have a destination, but still want a list of sources
                string   dName   = (des != null) ? Path.Combine(dPath, sfi.Name) : "";
                desFilesList.Add(sName + sdSepChar + dName);
            }


            if (pars.ContainsKey("destf"))
            {
                StreamWriter sw = new StreamWriter(pars["destf"]);
                foreach (string stg in desFilesList)
                {
                    string[] sdPair = stg.Split(sdSepChar);
                    sw.WriteLine(sdPair[1]);
                }
                sw.Close();
            }
            if (pars.ContainsKey("nocmd"))
            {
                return(0);
            }

            string workDir = null;

            if (pars.ContainsKey("workDir"))
            {
                workDir = pars["workDir"];
            }

            int procLimit = 10;

            if (opts.ContainsKey("procLimit"))
            {
                int.TryParse(opts["procLimit"], out procLimit);
            }
            if (procLimit > 100)
            {
                procLimit = 100;
            }
            if (procLimit < 1)
            {
                procLimit = 1;
            }

            int    currentProcs = 0;
            string processLock  = "";

            ParallelLoopResult result = Parallel.ForEach(desFilesList, n => {
                ProcessStartInfo psi = new ProcessStartInfo();
                string[] sdPair      = n.Split(sdSepChar);
                try {
                    if (workDir != null)
                    {
                        switch (workDir)
                        {
                        case "@takesrc":
                            psi.WorkingDirectory = sdPair[0];
                            sdPair[0]            = "";
                            break;

                        case "@takedes":
                            psi.WorkingDirectory = sdPair[1];
                            sdPair[1]            = "";
                            break;

                        case "src":
                            psi.WorkingDirectory = sdPair[0];
                            break;

                        case "des":
                            psi.WorkingDirectory = sdPair[1];
                            break;
                        }
                    }
                    psi.FileName   = (pars.ContainsKey("cmd")) ? pars["cmd"] : "needCmdParToExecute.exe";
                    string copts   = (pars.ContainsKey("opts")) ? pars["opts"] : "";
                    string cmdOpts = $"{copts} {sdPair[0]} {sdPair[1]}";

                    psi.Arguments = cmdOpts;

                    while (currentProcs > procLimit)
                    {
                        Thread.Sleep(20);
                    }
                    lock (processLock) {
                        currentProcs++;
                    }
                    Process process   = new Process();
                    process.StartInfo = psi;
                    process.Start();
                    process.WaitForExit();  // this lets us put a clamp on the number of parallel processes.
                }
                catch (Exception exc) {
                    string psiTxt = string.Format("{ psi file='{0}', Args='{1}'", psi.FileName, psi.Arguments);
                    string emsg   = SimpleUtils.ExceptionMsg(exc, "parallel InvokeDirectories: " + psiTxt + " at " + n);
                    Console.WriteLine(emsg);
                    myLog(emsg);
                }
                finally {
                    lock (processLock) {
                        currentProcs--;
                    }
                }
            });

            myLog(SimpleUtils.InfoMsg("parallel copy result", (result.IsCompleted) ? 0 : 1, "oper.copy walk"));
            return(lastErr);
        }