Crash on execution of script creating an information report for CSV files (resolved)

Crash on execution of script creating an information report for CSV files (resolved)

6

    Apr 30, 2013#1

    I'm creating a script to find all files that are missing the final line termination at the end of the file. These are both pipe-delimited and tab-delimited files. This script will 'eventually' become the core of a recursive call into a folder structure to do the following.

    1) Open a file (read-only if necessary)
    2) Go to the bottom line and check for a final "/r/n"
    3) Select all 'complete' rows of text and read into an array
    4) Get the length of the array
    5) Persist the file name, the number of 'good' rows (array length), and whether "/r/n" is missing in the last row (as a CSV line) in a new tab.
    6) Close the open file without saving.

    The end result should be a CSV file that has all files in a folder structure with the number of lines and whether the final line termination is present. The 'core' script seems to be fine - as long as the file size is not too large. Files with row counts at 500,000 are no problem.

    THE PROBLEM: large files (around 900,000 rows and larger) will cause UltraEdit to crash - sometimes creating a dump file, sometimes not.

    Here is the 'core' script. I have intentionally stripped out all of the file-check conditional logic keep things down to the core functionality.

    Code: Select all

       UltraEdit.insertMode();
       if (typeof(UltraEdit.columnModeOff) == "function") UltraEdit.columnModeOff();
       else if (typeof(UltraEdit.activeDocument.columnModeOff) == "function") UltraEdit.activeDocument.columnModeOff();
       UltraEdit.activeDocument.hexOff();
       UltraEdit.activeDocument.findReplace.searchDown=true;
       UltraEdit.activeDocument.findReplace.matchCase=false;
       UltraEdit.activeDocument.findReplace.matchWord=false;
       
       var strings; 
       //variable which holds selection
       var stringArray = new Array(); 
       //create array to hold string values
       var arrayLength = 0; 
       //array length -- asserting a value here seems to make no difference in the ability to parse a greater line count
       var lineTerminator = "\r\n"; 
       //line terminator character pattern
       
       
       var rCount = 0;
       UltraEdit.activeDocument.bottom();
       // Is the last line of the file terminated with a line ending?
       if (UltraEdit.activeDocument.isColNumGt(1)) {
          // Count if there's a line at the bottom of the file without the \r\n terminators
          rCount++;
          UltraEdit.outputWindow.write("Found "+rCount+" bad row.");
          // Go to beginning of last line to start selection from last line with good terminator
          UltraEdit.activeDocument.cancelSelect();
          UltraEdit.activeDocument.key("HOME");
       }
       UltraEdit.activeDocument.startSelect();
       // Now select only 'full' lines - based on conditional cursor placement above
       UltraEdit.activeDocument.selectToTop();
       // Get selection
       strings = UltraEdit.activeDocument.selection;
       //split string at line terminator characters 
       stringArray = strings.split(lineTerminator);
       // assign array length into a variable
       arrayLength = stringArray.length
       // make 'totalLines' the correct length by subtracting 1 from the array length. Dunno why this is required.
       var totalLines = (arrayLength - 1);
       UltraEdit.outputWindow.write("Found "+totalLines+" lines in array.")
       
    To check whether it's the size of the file itself causing the problem, I modified the selection pattern to grab a limited set of row (5-10) from the bottom of a 1+million-row file, and the script ran without complaining. So I'm guessing that it's either the undo buffer or the array buffer that's being overrun when doing a split that's so large.

    BEAR IN MIND: I'm not committed to using this approach. I've toyed with the idea of doing a

    Code: Select all

    if (UltraEdit.activeDocument.findReplace.regExp("%[A-Za-z0-9]")) 
    or something similar to do a count of the first character of each line and 'manually' iterate through the lines in the selected range. But I can't seem to get that conditional logic to work. From what I read in the scripting commands page, the .regExp parameter in findReplace is boolean, but the error I get back seems to indicate that's not valid. My point is - I'm open to any approach that *works* reliably for large file sizes.

    Any insight/help/pointers would be greatly appreciated. Thanks in advance!

    6,603548
    Grand MasterGrand Master
    6,603548

      May 01, 2013#2

      The reason why UltraEdit crashes is that you select large blocks and access them within the script. That is in general not a problem. But as I needed to write in the last few weeks several times, there is a bad memory management design within UltraEdit. Whenever UltraEdit.activeDocument.selection is accessed from within a script, UltraEdit copies the string into RAM and keep it their up to the script termination even if the selection is canceled or replaced by another selection. So if a script selects a huge amount of data in one step or in total in several steps in a loop like your script does, this bad design on memory management of selected strings results in more and more usage of RAM until there is no allocatable RAM available anymore for a 32-bit application like UltraEdit. The next approach to allocate memory for a string results in either a break of script execution by the integrated JavaScript interpreter with a not explainable error message in output window or UltraEdit crashes.

      However, your script can be simplified if at Advanced - Configuration - Editor Display - Miscellaneous
      • the configuration settings Disable line numbers and Count wrapped lines as new lines in number display are both not enabled and
      • the files do not contain lines longer than value of configuration setting Maximum columns before line wraps.

      Code: Select all

         UltraEdit.insertMode();
         if (typeof(UltraEdit.columnModeOff) == "function") UltraEdit.columnModeOff();
         else if (typeof(UltraEdit.activeDocument.columnModeOff) == "function") UltraEdit.activeDocument.columnModeOff();;
         UltraEdit.activeDocument.hexOff();
         UltraEdit.activeDocument.bottom();
         // The current line number at end of file is usually by one higher the number of lines.
         var totalLines = UltraEdit.activeDocument.currentLineNum;
         var bNoTermAtEnd = UltraEdit.activeDocument.isColNumGt(1);
         // Is the last line of the file not terminated with a line
         // ending, the total number of lines must be increased by one.
         if (!bNoTermAtEnd) totalLines--;
         UltraEdit.outputWindow.write("File \""+UltraEdit.activeDocument.path+"\" has "+totalLines+" line"+((totalLines!=1)?"s":"")+" with last line being"+(bNoTermAtEnd?" not":"")+" terminated.");
      By the way: Line breaks in double quoted string values in a CSV file are valid and should not be interpreted as end of row, see Wikipedia article about CSV files. But most programmers make a poor job on reading CSV files. For example writing a multiline text in Excel using Alt+Return to insert a line break within the text and saving the spreadsheet as MS-DOS CSV file, results in a CSV file, where the multiline string is enclosed in double quotes and for the line break there is just \n within the double quoted value where end of row is terminated with \r\n. In UltraEdit there is the setting Only recognize DOS terminated lines (CR/LF) as new lines for editing at File Handling - DOS/Unix/Mac Handling to be able to open such CSV files with a line-feed only within double quoted string values not being interpreted as end of line. The option Never prompt to convert files to DOS format must be selected to recognize only CR+LF as line termination.


      Some more hints:

      The command startSelect is needed only when making a selection using the command key, in other words when making a selection with moving the caret by emulated key hits. Commands like selectToTop do not need an explicit startSelect as those commands always create a selection or expand a selection if there is already one.

      UltraEdit.activeDocument.findReplace.regExp("%[A-Za-z0-9]") is an invalid command and a script with this string results on execution in a syntax error reported by the integrated JavaScript interpreter. regExp is a property (parameter) of a find or replace command which can have value true or false.

      Code: Select all

      UltraEdit.ueReOn();   // Define search respectively regular expression engine.
      // Define all parameters for a Find which do not depend on other parameters.
      UltraEdit.activeDocument.findReplace.mode=0;
      // Run search with ignoring case of letters which means [A-Z] is equal [a-z] is equal [A-Za-z].
      // Special hint: Case-sensitive searches are always faster and should be therefore preferred.
      UltraEdit.activeDocument.findReplace.matchCase=false;
      UltraEdit.activeDocument.findReplace.matchWord=false;
      UltraEdit.activeDocument.findReplace.regExp=true;
      UltraEdit.activeDocument.findReplace.searchDown=true;
      UltraEdit.activeDocument.findReplace.searchInColumn=false;
      if (UltraEdit.activeDocument.findReplace.find("%[a-z0-9]")) {
         UltraEdit.messageBox("Yes, a character being a letter A-Z in any case or a digit is found at beginning of a line.");
      }

      6

        May 01, 2013#3

        Thanks a million, Mofi!

        I *thought* I tried the "find" command with the regex markup, but that didn't seem to yield a usable result. That was probably me just flailing around trying to find something that works. The rest of the information you provide makes perfect sense, as I guessed that there was an issue with memory management in UE. I'll definitely try your suggestions on changing the config as well as going to a Perl RegExp, just to become familiar with using that patter. But the real 'nuke it from orbit' solution - to use the native javascript call is pretty elegant - something I probably *never* would have found on my own.

        This is usually the part of the thread where people go out of their way to express their gratitude for the amount of expertise you offer here - and I am no exception. I have gleaned an exceptional amount of information from your posts here, Mofi - certainly not just this particular thread. These forums are a reference source for me as I gain more familiarity with UE - and I'm am constantly amazed at how much ground can be covered simply by pouring through your responses. Add me to the chorus of "Mofi fanbois". :) Thanks again.

        6,603548
        Grand MasterGrand Master
        6,603548

          May 02, 2013#4

          Thank you for your kind words. As most questioners do not take trouble over writing a short and simple THANKS, it does good to read a reply like yours which motivates me to continue helping users and explaining things in such a manner which of course requires a lot of my spare time.

          I would asked for more contributions by other users as I could myself learn also from others. I learned a lot from others in the past, but in the last 2 years the contributions from other users became very rare.

          6

            May 03, 2013#5

            Nice! A rising tide lifts all ships. :)

            I will post the full script once it's complete - to show a few things which are specific to my case - and may be helpful to others. I'm going back to the ** Find Results ** tab and parsing each line to create a new CSV file with the results. This includes adding whether there's a header row, which form of line termination is used for the active document, and adjusting the line count *if* the header row is present. Once that's done I'll post the final script. I'm sure there will be more efficient ways to accomplish what I've done, but hopefully it will be a good starting point for those that want to do the same type of thing with UE.

              May 03, 2013#6

              OK - here's my 'final' script. I put 'final' in quotes because I'm sure there will be some follow-on tweaking to do. I of course have relied heavily on Mofi and other contributors here for much of what I've put together. I also borrowed the date-time code from Rob on Programming, with a little wrinkle I added to 0->1 adjust the month and two-pad the non-year portions of the date. This preserves sort ordering (and common sense, in the case of fixing the month to a human-readable value). :)

              As for *my* assumptions, they are in general, as follows:
              1) I'm looking for text files only. I'm ignoring anything that's not text in the folder structure (such as *.xlsx and the like)
              2) There are basically two file formats, one with headers and one without. Since the file loader ignores the header row, I compensate the count accordingly.
              3) I *want* to count the bottom row if there's *no* final line terminator for that row.
              4) I want to *ignore* the final row of a file where the final line terminator is present, so I again compensate the file loader's line count for that condition.
              5) I 'flipped' the logic when looking for a final EOL in the file. This is to make the result more human-readable in the CSV. It might look confusing to see a "!"[not] flipping the boolean, but that's just so that when the line terminator is present the CSV reads "TRUE".
              6) Line termination type matters to me, so I created a case statement that makes the indicator *in*sensitive to whether UE is set to convert line termination type at the time that the file was loaded. Check out the UE scripts "lineTerminator" command to see what the various values mean. It's actually pretty clever.

              I ran the GetListofFiles.js as a stand-alone process on my targeted archive folder, and found more than 30,000 files - so I'll be setting this up to run over the weekend.  8O

              And now for the script I put together.

              Code: Select all

              // The GetListOfFiles javascript is in my "scripts" folder, so I don't need to use an explicit path.
              // include GetListOfFiles.js
              
              // First set a filePath variable - since it's used both for finding files and 
              // for stripping the path from the results window.
              var filePath = "C:\\archive\\top\\level\\folder\\";
              
              // Go through archive 'recursive' and retrieve all *.txt files.
              GetListOfFiles(0,filePath,"*.txt",true)
              
              if (GetListOfFiles(0,filePath,"*.txt",true)) {
              
                 // If there was no error and files are found in the directory, the
                 // function made the search results output with the file names active.
                 // Select all lines in the file and load the file names into an array.
                 UltraEdit.activeDocument.selectAll();
                 var asFileNames = UltraEdit.activeDocument.selection.split("\r\n");
              
                 // Leave the ** Find Results ** tab open, because this script needs it to create the resulting CSV file,
                 // so the line below is commented out.
                 // UltraEdit.closeFile(UltraEdit.activeDocument.path,2);
                 asFileNames.pop();  // Remove empty string at end of the list.
                 
                 // Now open one file after the other, process it, save and close the file.   
                 for (var nFileIndex = 0; nFileIndex < asFileNames.length; nFileIndex++) {
              
                    // No file in the list should be already open on script start! Why?
                    // http://www.<raedit.com/forums/viewtopic.php?f=52&t=4596#p26710
                    // contains the answer on point 7 and the solution to enhance
                    // this script further if this is necessary in some cases.
                    UltraEdit.insertMode();
                    if (typeof(UltraEdit.columnModeOff) == "function") UltraEdit.columnModeOff();
                    else if (typeof(UltraEdit.activeDocument.columnModeOff) == "function") UltraEdit.activeDocument.columnModeOff();
                    UltraEdit.ueReOn();
                    UltraEdit.activeDocument.hexOff();
                    UltraEdit.activeDocument.findReplace.matchCase=false;
                    UltraEdit.activeDocument.findReplace.matchWord=false;
                    UltraEdit.activeDocument.findReplace.searchDown=true;
                    UltraEdit.activeDocument.findReplace.searchInColumn=false;
                    UltraEdit.activeDocument.findReplace.regExp=true; // This will change later when searching for the file path.
                    // Open the file from the index position of the array 'asFileName'
                    UltraEdit.open(asFileNames[nFileIndex]);
                    // Get line termination type for the file
                    var lineTerminator = UltraEdit.activeDocument.lineTerminator;
                    // set default value for lineType (DOS)
                    var lineType = 0;
                    // Set the appropriate line terminator value regardless of editor config.
                    switch (lineTerminator)     
                       {
                       case -2:  lineType = "MAC"; break;
                       case -1:  lineType = "UNIX"; break;
                       case 1:  lineType = "UNIX"; break;
                       case 2:  lineType = "MAC"; break;
                       default: lineType = "DOS";   break;
                       }
                    // set a boolean for checking whether the first row has alpha characters (is row 1 a header row?)   
                    var bHeader = false;
                    // Check for "Account" at top of file
                    UltraEdit.activeDocument.top();
                    if (UltraEdit.activeDocument.findReplace.find("%[a-z]")) {
                     bHeader = true;
                    }
                    UltraEdit.activeDocument.bottom();
                    // The current line number at end of file is usually by one higher the number of lines.
                    var totalLines = UltraEdit.activeDocument.currentLineNum;
                    var bTermAtEnd = !(UltraEdit.activeDocument.isColNumGt(1));
                    // If file has a header row the total lines must be decreased by one.
                    if (bHeader) totalLines--;
                    // If file has final line termination the total lines must be decreased by one.
                    if (bTermAtEnd) totalLines--;
                    // Close the file without saving.
                    UltraEdit.closeFile(UltraEdit.activeDocument.path,2);
                    // set line number to the index position
                    var lineNumber = nFileIndex;
                    // Set the ** Find Results ** tab as the active document
                    UltraEdit.document[0].setActive();
                    // Go to the line in the tab equal to the the index
                    UltraEdit.activeDocument.gotoLine(lineNumber,0);
                    // Delete the file path, to make the result file smaller - but leaves the 
                    // sub-directories in-place so that traceability is maintained.
                    UltraEdit.activeDocument.findReplace.regExp=false; // Here we don't want to use a regex - looking for filePath string.
                    UltraEdit.activeDocument.findReplace.replace(filePath,"");
                    // Go to the end of the file to start appending the data collected
                    UltraEdit.activeDocument.key("END");
                    // Append CSV entries for the targeted file
                    UltraEdit.activeDocument.write(","+bHeader+","+lineType+","+totalLines+","+bTermAtEnd);
                    // Write the information to the Output window too.
                    UltraEdit.outputWindow.write("File \""+asFileNames[nFileIndex]+"\" has "+(bHeader?" Header row, has ":"")+lineType+" terminators, "+totalLines+" line"+((totalLines!=1)?"s":"")+", is"+(!bTermAtEnd?" NOT":"")+" terminated.");
                 }
              // Copy the final result set to create a new CSV file
              UltraEdit.activeDocument.selectAll();
              UltraEdit.activeDocument.copy();
              // Create new file to save as CSV
              UltraEdit.newFile();
              // Create header row for CSV
              UltraEdit.activeDocument.write("File [sub-dirs]\\Name,Has Header,Line Type,Number of Lines,Final CrLf\r\n");
              // Insert data from clipboard
              UltraEdit.activeDocument.paste();
              //Create pad function to parse two-digit date elements
              function pad2(number) { return (number < 10 ? '0' : '') + number }
              // Create date stamp for file name
              var now = new Date();
              // Save file with date stamp, using pad2 function for non-year portions of the date sequence. (preserves sort order)
              UltraEdit.saveAs(filePath
                    +"Results_"
                    + now.getFullYear()
                    + pad2(now.getMonth()+1)
                    + pad2(now.getDate())
                    + "-"
                    + pad2(now.getHours())
                    + pad2(now.getMinutes())
                    + pad2(now.getSeconds())
                    + ".csv");
              // Close file without saving
              UltraEdit.closeFile(UltraEdit.activeDocument.path,2);
               
              // Go back to the results.
              UltraEdit.document[0].setActive();
              // Close results tab without saving.
              UltraEdit.closeFile(UltraEdit.activeDocument.path,2);
              }
              If anyone has any questions - ask Mofi. :D

              I kid! I kid!! You can ask away - but I will will defer early and often to the true experts that roam the boards here.

              6,603548
              Grand MasterGrand Master
              6,603548

                May 04, 2013#7

                Some notes on your script.

                The line var lineType = 0; is not good as this line creates a variable with a number object with value zero. On next code block this number object is discarded and instead a string object is created assigned to variable lineType.

                Better would be you create just the variable (typeless pointer) by using just var lineType;

                The code block for determining if a header is present is not so good. First, the comment // Check for "Account" at top of file is not right which I think is caused by the fact that the script you use checks in real for the word Account at top of the file. Second, the regular expression find is not good as if first line does not start with a letter, it searches in entire file for a line starting with a letter. This can easily produce a wrong result about existence of a header, but definitely makes the script slow if there are many CSV files not containing a letter at beginning of any line.

                To check for the word Account at top of a file use following code:

                Code: Select all

                UltraEdit.activeDocument.top();
                UltraEdit.activeDocument.selectWord();
                if (UltraEdit.activeDocument.isSel()) {
                   if (UltraEdit.activeDocument.selection == "Account") bHeader = true;
                }
                or

                Code: Select all

                var sHeaderBegin = "Account";
                UltraEdit.activeDocument.top();
                for (var nCharIndex = 0; nCharIndex < sHeaderBegin.length; nCharIndex++) {
                   if (UltraEdit.activeDocument.currentChar != sHeaderBegin[nCharIndex]) break;
                   UltraEdit.activeDocument.key("RIGHT ARROW");
                }
                if (nCharIndex == sHeaderBegin.length) bHeader = true;
                The replacement for UltraEdit.activeDocument.findReplace.find("%[a-z]") would be

                Code: Select all

                UltraEdit.activeDocument.top();
                if (UltraEdit.activeDocument.isChar("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")) bHeader = true;
                As with this modification there is no find executed anymore on every opened CSV file, just on the results file with the file name, the block

                Code: Select all

                      UltraEdit.insertMode();
                      if (typeof(UltraEdit.columnModeOff) == "function") UltraEdit.columnModeOff();
                      else if (typeof(UltraEdit.activeDocument.columnModeOff) == "function") UltraEdit.activeDocument.columnModeOff();
                      UltraEdit.ueReOn();
                      UltraEdit.activeDocument.hexOff();
                      UltraEdit.activeDocument.findReplace.matchCase=false;
                      UltraEdit.activeDocument.findReplace.matchWord=false;
                      UltraEdit.activeDocument.findReplace.searchDown=true;
                      UltraEdit.activeDocument.findReplace.searchInColumn=false;
                      UltraEdit.activeDocument.findReplace.regExp=false;
                can be moved up now above the for loop to define the environment for the script and the parameters for the replaces on the results file only once. This speeds up the loop a little bit.

                6

                  May 06, 2013#8

                  As usual - I bow to your exceeding experience and wisdom. I *did* notice that when coursing through the list of 30,000 docs that things bogged down, and I'm sure that you nailed why that's happening. Fortunately the file format is fairly regular - one of two types - but it still wastes cycles to go through the file (and perhaps it inadvertently increases heap/memory consumption?) I'll make the corrections and re-run on the larger record set - as you would expect, the one I kicked off over the weekend stopped mid-process. :oops:

                  Thanks again.

                    May 07, 2013#9

                    So - here's my 'final final' script which actually starts with a 'static' file I created from a query. I *had* to do this because the result set of recursively gathering the location of every file in the archive seemed to be too large for UE to handle (when opening and parsing 30,000 files) and wasn't really useful to my purpose. I found out that only a fraction of the files are 'valid' for my circumstance, and the only way to know that is to check our database to see the final status of a particular file. So created a query that looked for all files with the proper status that were transmitted since New Year of 2012. That gave me 1600+ records, which is a set that UE seemed to handle well - and gave me enough data for useful finding.

                    However, because the database might have a record of a file that's not in the 'standard' path for the file archive, some file handling might error out. That's where this forum comes to the rescue again. I included the "FileExist" script and called it to make sure that a particular file was present before opening and parsing it. If it didn't find the file I echoed a statement to that effect to the output window and continued through the array.

                    Below is the script I used to generate the data.

                    Code: Select all

                    // include FileExist.js
                    
                    var filePath = "C:\\myfilepath\\input.txt";
                    
                       UltraEdit.open(filePath)
                    
                       // Select all lines in the file and load the file names into an array.
                       UltraEdit.activeDocument.selectAll();
                       var asFileNames = UltraEdit.activeDocument.selection.split("\r\n");
                    
                       // The list is not needed anymore and therefore the results window is closed.
                       // UltraEdit.closeFile(UltraEdit.activeDocument.path,2);
                       asFileNames.pop();  // Remove empty string at end of the list.
                          UltraEdit.insertMode();
                          if (typeof(UltraEdit.columnModeOff) == "function") UltraEdit.columnModeOff();
                          else if (typeof(UltraEdit.activeDocument.columnModeOff) == "function") UltraEdit.activeDocument.columnModeOff();
                          UltraEdit.ueReOn();
                          UltraEdit.activeDocument.hexOff();
                          UltraEdit.activeDocument.findReplace.matchCase=false;
                          UltraEdit.activeDocument.findReplace.matchWord=false;
                          UltraEdit.activeDocument.findReplace.searchDown=true;
                          UltraEdit.activeDocument.findReplace.searchInColumn=false;
                          UltraEdit.activeDocument.findReplace.regExp=false;
                       
                       // Now open one file after the other, process it, save and close the file.   
                       for (var nFileIndex = 0; nFileIndex < asFileNames.length; nFileIndex++) {
                     if (!FileExist(asFileNames[nFileIndex])) {
                     UltraEdit.outputWindow.write("Row "+lineNumber+" - "+asFileNames[nFileIndex]+" does not exist in archive.");
                     } 
                     else {
                          // No file in the list should be already open on script start! Why?
                          // https://forums.ultraedit.com/viewtopic.php?f=52&t=4596#p26710
                          // contains the answer on point 7 and the solution to enhance
                          // this script further if this is necessary in some cases.
                    
                          // Open the file from the index position of the array 'asFileName'
                          UltraEdit.open(asFileNames[nFileIndex]);
                          // Get line termination type for the file
                          var lineTerminator = UltraEdit.activeDocument.lineTerminator;
                          // set default value for lineType (DOS)
                          var lineType = 0;
                          // Set the appropriate line terminator value regardless of editor config.
                          switch (lineTerminator)     
                             {
                             case -2:  lineType = "MAC"; break;
                             case -1:  lineType = "UNIX"; break;
                             case 1:  lineType = "UNIX"; break;
                             case 2:  lineType = "MAC"; break;
                             default: lineType = "DOS";   break;
                             }
                          // set a boolean for checking whether the first row has alpha characters (is row 1 a header row?)   
                          var bHeader = false;
                          // Check for "Account" at top of file
                          var sHeaderBegin = "Account";
                          UltraEdit.activeDocument.top();
                          for (var nCharIndex = 0; nCharIndex < sHeaderBegin.length; nCharIndex++) {
                             if (UltraEdit.activeDocument.currentChar != sHeaderBegin[nCharIndex]) break;
                             UltraEdit.activeDocument.key("RIGHT ARROW");
                          }
                          if (nCharIndex == sHeaderBegin.length) bHeader = true;
                          // Go to bottom of document, get initial row count and do end-of-line check
                          UltraEdit.activeDocument.bottom();
                          // The current line number at end of file is usually by one higher the number of lines.
                          // but only if the end-of-line termination is correct. (CrLf or /r/n)
                          var totalLines = UltraEdit.activeDocument.currentLineNum;
                          var bTermAtEnd = !(UltraEdit.activeDocument.isColNumGt(1));
                          // If file has a header row the total lines must be decreased by one.
                          if (bHeader) totalLines--;
                          // If file has final line termination the total lines must be decreased by one.
                          if (bTermAtEnd) totalLines--;
                          // Close the file without saving.
                          UltraEdit.closeFile(UltraEdit.activeDocument.path,2);
                          // set line number to the index position
                          var lineNumber = nFileIndex;
                          lineNumber++;
                          // Set the ** Find Results ** tab as the active document
                          UltraEdit.document[0].setActive();
                          // Go to the line in the tab equal to the the index
                          UltraEdit.activeDocument.gotoLine(lineNumber,0);
                          // Go to end of line to append values
                          UltraEdit.activeDocument.key("END");
                          // Append CSV entries for the targeted file
                          UltraEdit.activeDocument.write(","+bHeader+","+lineType+","+totalLines+","+bTermAtEnd);
                        }
                         
                       }
                    // Copy the final result set to create a new CSV file
                    UltraEdit.activeDocument.selectAll();
                    UltraEdit.activeDocument.copy();
                    // Create new file to save as CSV
                    UltraEdit.newFile();
                    // Create header row for CSV
                    UltraEdit.activeDocument.write("File [sub-dirs]\\Name,Has Header,Line Type,Number of Lines,Final CrLf\r\n");
                    // Insert data from clipboard
                    UltraEdit.activeDocument.paste();
                    //Create pad function to parse two-digit date elements
                    function pad2(number) { return (number < 10 ? '0' : '') + number }
                    // Create date stamp for file name
                    var now = new Date();
                    // Save file with date stamp, using pad2 function for non-year portions of the date sequence. (preserves sort order)
                    UltraEdit.saveAs("C:\\myfilepath\\"
                          +"Results_"
                          + now.getFullYear()
                          + pad2(now.getMonth()+1)
                          + pad2(now.getDate())
                          + "-"
                          + pad2(now.getHours())
                          + pad2(now.getMinutes())
                          + pad2(now.getSeconds())
                          + ".csv");
                    // Close file without saving
                    UltraEdit.closeFile(UltraEdit.activeDocument.path,2);
                     
                    // Go back to the results.
                    UltraEdit.document[0].setActive();
                    // Close input file without saving.
                    UltraEdit.closeFile(UltraEdit.activeDocument.path,2);