Full Code of hi5/TF for AI

master f31a9c24167d cached
3 files
110.6 KB
33.2k tokens
1 requests
Download .txt
Repository: hi5/TF
Branch: master
Commit: f31a9c24167d
Files: 3
Total size: 110.6 KB

Directory structure:
gitextract_bq5uzlh1/

├── license.txt
├── readme.md
└── tf.ahk

================================================
FILE CONTENTS
================================================

================================================
FILE: license.txt
================================================
                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

                    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License. (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code. (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

                            NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

                     END OF TERMS AND CONDITIONS

================================================
FILE: readme.md
================================================
# TF: Textfile & String Library for AutoHotkey [lib] - v3.8

__A "Swiss Army Knife" library for Text (files)__

## Introduction

As the name suggest this is an AutoHotkey (AHK) Library with a number of functions to "manipulate" **text**,
both **files** such as \*.txt, \*.ahk, \*.html, \*.css etc AND **Strings** (or variables). For example
you can delete specific lines, replace words or specific lines, number lines, remove or insert columns
of text, etc. See the list of functions below for a complete overview.

It is **not** useful for binary files or data such as MS Office files, PDFs, EXEcutables, images etc.
(Tip: search the AHK forum for "binread" to find some pointers on how to read, write and "edit" binary files.)

## Functions

1.  [TF](#TF)
2.  [TF_Save](#TF_Save)
3.  [TF_CountLines](#TF_CountLines)
4.  [TF_Count](#TF_Count) *1
5.  [TF_ReadLines](#TF_ReadLines)
6.  [TF_Tail](#TF_Tail)
7.  [TF_Replace](#TF_Replace)
8.  [TF_ReplaceInLines](#TF_ReplaceInLines)
9.  [TF_RegExReplace](#TF_RegExReplace)
10. [TF_RegExReplaceInLines](#TF_RegExReplaceInLines)
11. [TF_RemoveLines](#TF_RemoveLines)
12. [TF_RemoveBlankLines](#TF_RemoveBlankLines)
13. [TF_RemoveDuplicateLines](#TF_RemoveDuplicateLines)
14. [TF_InsertLine](#TF_InsertLine)
15. [TF_ReplaceLine](#TF_ReplaceLine)
16. [TF_InsertPrefix](#TF_InsertPrefix)
17. [TF_InsertSuffix](#TF_InsertSuffix)
18. [TF_TrimLeft](#TF_TrimLeft)
19. [TF_TrimRight](#TF_TrimRight)
20. [TF_AlignLeft](#TF_AlignLeft)
21. [TF_AlignCenter](#TF_AlignCenter)
22. [TF_AlignRight](#TF_AlignRight)
23. [TF_LineNumber](#TF_LineNumber)
24. [TF_ConCat](#TF_ConCat)
25. [TF_ColGet](#TF_ColGet)
26. [TF_ColPut](#TF_ColPut)
27. [TF_ColCut](#TF_ColCut)
28. [TF_ReverseLines](#TF_ReverseLines)
29. [TF_Find](#TF_Find) (can be used as basic grep)
30. [TF_SplitFileByLines](#TF_SplitFileByLines)
31. [TF_SplitFileByText](#TF_SplitFileByText)
32. [TF_Merge](#TF_Merge) *2
33. [TF_Prepend](#TF_Prepend) *2
34. [TF_Append](#TF_Append) *2
35. [TF_Wrap](#TF_Wrap)
36. [TF_WhiteSpace](#TF_WhiteSpace)
37. [TF_Substract](#TF_Substract)
38. [TF_RangeReplace](#TF_RangeReplace)
39. [TF_MakeFile](#TF_MakeFile)
40. [TF_Tab2Spaces](#TF_Tab2Spaces)
41. [TF_Spaces2Tab](#TF_Spaces2Tab)
42. [TF_Sort](#TF_Sort)
43. [TF_Join](#TF_Join)

**Notes**

1. TF_Count does not support files and only works with variables.
2. TF_Merge, TF_Prepend, TF_Append currently do not support variables and only work with FILES.
 
**Concept**

It is important to understand a few basic concepts before you start working with the TF library:

- If you pass on a file to a TF function the output will generally be a file (there are a few exceptions). By default it writes the output to a COPY of the input file, leaving the original input file intact.
- You **can** overwrite the input file by using a ! as prefix. See "Quick intro to Parameters" and "Textfile and the ! Prefix" below for more details.
- If you pass on a variable to a TF function the returned output will also be a variable.
- Most TF Functions allow you to work on specific lines or sections of lines.
- TF "knows" if something is a file or a variable, even if the variable passed on to TF is meant to represent a file. (This is done in the helper function TF_GetData for those interested).

To understand how to work with **files and variables** please read the "Textfile and the ! Prefix"
and "Files & Variables" sections below.

**File encoding, codepage**

If you experience TF is changing the file encoding (codepage) try to set the correct file encoding
at the top of your script (or at least before you call a TF function) using the following AutoHotkey command:
http://ahkscript.org/docs/commands/FileEncoding.htm

Please note, there is no way to determine the file encoding 100% accurately, even if a file
contains BOM. See the discussion about this topic and possibly useful additional functions
at the AutoHotkey forum http://www.autohotkey.com/board/topic/95986-filegetencoding-filegetformat/
(The result of these functions are simply a best guess assuming UTF-8 more common when BOM - https://en.wikipedia.org/wiki/Byte_Order_Mark - 
is missing).

## How to "install" / use

You can either place TF.ahk in your LIB directory (see [http://ahkscript.org/docs/Functions.htm#lib](http://ahkscript.org/docs/Functions.htm#lib))
or use #include (see [http://ahkscript.org/docs/commands/_Include.htm](http://ahkscript.org/docs/commands/_Include.htm))

You can find examples of most functions in the "example script" here
[http://www.autohotkey.com/forum/viewtopic.php?p=280363#280363](http://www.autohotkey.com/forum/viewtopic.php?p=280363#280363)

**Note:** Because most of these functions operate on a line by line basis they WILL be
slower compared to a function or script which could operate on an entire file or
variable at once. Keep this in mind if you need to process many files/variables or very
large files/variables in case speed is an issue.

## Quick intro to Parameters

Almost all functions accept the following basic parameters:

<table border="1" width="100%" cellspacing="0" cellpadding="3" bordercolor="#C0C0C0">
      <tr valign="top">
		<td><b>Parameter</b></td>
		<td><b>Meaning</b></td>
	  </tr>	
	  <tr valign="top">
	    <td>Text</td>
	    <td>The Filename (may include (absolute) path) to read from and save to (for all functions that write an output file).<br />
         <b>Note:</b> by default a filename_copy will be created, use the ! prefix if you want to OverWrite the TextFile (e.g. the source file)<br />
	     As of v3 "Text" can also be a variable or indeed text directly passed on to the function.<br />
         See <a href='#textfile-and-the--prefix'>Textfile and the ! Prefix</a>.
	    </td>
	  </tr>
      <tr valign="top">
        <td>! Prefix</td>
        <td>If Text starts with ! (eg: "!c:\sample.txt") it will overwrite the text file, otherwise it will save the new file to a copy of the text file eg: Filename_copy.txt (All functions, apart from reading functions because there is no output file) <br />
        <b>Tip:</b> you can use concatenation to add the !, e.g. "!" . "Filename.txt", see the examples in the AHK thread.<br />
        Note: If Text is a variable, it can start with a ! as TF will detect automatically that it is not a file and will therefore not create a file but return the altered variable instead.<br />
        See <a href='#textfile-and-the--prefix'>Textfile and the ! Prefix</a>.
	    </td>
	  </tr>
      <tr valign="top">
        <td>Lines</td>
        <td>Number of lines to read</td>
      </tr>
      <tr valign="top">
        <td>StartLine</td>
        <td>Start of Range (Almost all).<br />
	    See <a href='#startline-endline-syntax'>(StartLine, Endline) Syntax</a>.
	    </td>
	  </tr>
      <tr valign="top">
        <td>EndLine</td>
        <td>End of Range (Almost all).<br />
  	    See <a href='#startline-endline-syntax'>(StartLine, Endline) Syntax</a>.
	    </td>
	  </tr>
      <tr valign="top">
        <td>SearchText</td>
        <td>Text to Find (Find / Replace functions)</td>
      </tr>
      <tr valign="top">
        <td>ReplaceText</td>
        <td>Text to Replace (Find / Replace functions)</td>
      </tr>
</table>

**Notes**

1. Many functions also have specific parameters, see the description of each function below.
2. **Backup files:** If a subdirectory "backup" is present in the directory of TextFile 
a backup is made before overwriting the original file (both for file.txt and file_copy.txt)
with the BAK extension
3. You can find examples of most functions in the "example script" here [http://www.autohotkey.com/forum/viewtopic.php?p=280363#280363](http://www.autohotkey.com/forum/viewtopic.php?p=280363#280363)

### (StartLine, Endline) Syntax

You can pass on multiple lines (sections) by using the StartLine parameter.

Examples:

- (5) ; start from line 5 to the end (StartLine)
- (5, 15) ; Section: lines 5 to 15 (StartLine, EndLine)
- ("5,13,45,67", 135) ; Specific lines: 5,13,45,67, ignore 135 ("StartLine", Endline - **EndLine value is ignored**)
- ("5-13,45-51", 135) ; Multiple sections of lines: 5 to 13, 45 to 51 ignore 135 ("StartLine", Endline - **EndLine value is ignored**)

Incremental (EndLine will **not** be ignored) in the following cases:

- ("2+3", 150) ; start with line 2, increment 3 up-to line 150 so 2,5,8,11,14 etc
- ("5+15") ; start with line 5, increment 15 until end of file so 5,20,35 etc

Negative Startline

- A Negative startline value makes a TF function operate on the last X lines.
- TF_ColGet and TF_ColPut accept negative StartColumn parameter. This can be used to "Get" or "Put" text in the x-th "column" from the right

### Textfile and the ! Prefix

Valid examples for using a Textfile:

```autohotkey
TF_("file.txt", .... ; Process file.txt and write output to file_copy.txt

F=file.txt ; ; Note how F is a variable, but AHK/TF will see it is meant to represent file.txt and write output to file_copy.txt
TF_(F, .... ; Process file.txt and write output to file_copy.txt
TF_("!file.txt", .... ; Process file.txt and overwrite file.txt

F=file.txt
TF_("!" . F, .... ; Process file.txt and overwrite file.txt

F=!file.txt
TF_(F, .... ; Process file.txt and overwrite file.txt

In a Loop, FilePattern:
TF_(A_LoopFileName, .... ; Process file and write output to file_copy
TF_("!" . A_LoopFileName, .... ; Process file and overwrite file

Loop, *.txt
    {
     TF_(A_LoopFileName, .... ; Process file and write output to file_copy
    }
```

If you want to use multiple TF functions on a single file it is advised to use the ! Prefix

```autohotkey
F=!file.txt 
TF_RemoveBlankLines(F)        ; remove all empty lines from file.txt and overwrite original file.txt
TF_LineNumber(F)              ; add linenumbers to all lines and overwrite original file.txt
TF_Replace(F, "this", "that") ; Replace the word "this" with "that" and overwrite original file.txt
; So the original file.txt has undergone three changes
```

**Files & Variables (v3+)**

You can use TF functions directly on files as well as variables. 
Below you will find some easy examples.

**Introduced in v3: TF()**

In order to save you the trouble of using a fileread to read a file into
a variable and proceed to use various TF functions there is the TF()
Function.


```autohotkey
TF("pathtofile", CreateGlobalVar="T")
```

By default it reads the contents of the file in a global variable named **t** and 
returns it for further processing. By default it creates variable **t** if a variable
name is **omitted** so if you do not like to use **t** you can use anything you like.
It is NOT mandatory to use TF(), you are welcome to use a fileread as well, TF() is 
merely added for convenience as we will see in the examples below. TF() doesn't suddenly
make the TF library "not standard compliant" as some seem to think as it only creates a 
global variable if you use the TF() function.

**Repeat:** You only have to use TF() if you want to read a file into a variable 
and use it on multiple TF_\* Functions but it is NOT required.

See also [TF Lib Errors](#tf-lib-errors) below.

**TF() Examples:**


```autohotkey
TF("file.txt") ; will create global var t
TF("file.txt", "MyVar") ; will create global var MyVar
```
   
**Examples on how to use files & variables with TF:**


```autohotkey
#Include tf.ahk
TestFile= ; create variable
(join`r`n
1 Hi this
2 a test variable
3 to demonstrate
4 how to 
5 use this
6 new version
7 of TF.AHK
)
FileDelete, TestFile.txt
FileAppend, %TestFile%, TestFile.txt ; create file
F=TestFile.txt ; just a shorthand code for TextFile.txt, so when
; we are using 'F' below we are still passing on a TextFile, not a variable!

; pass on file, read lines 5 to end of file:
MsgBox % "From File 1:`n" TF_ReadLines("TestFile.txt",5)
MsgBox % "From File 2:`n" TF_ReadLines(F,5)              ; same

; pass on variable, read lines 1 to 5
MsgBox % "From Variable 1:`n" TF_ReadLines(TestFile,"1-5")  
MsgBox % "From Variable 2:`n" TF_ReadLines("Hi`nthis`nis`na`ntest`nvariable`nfor`ntesting",1,3) ; pass on string

; Examples using TF(): (it will save you a FileRead if you want to work with Variables)

TF("TestFile.txt") ; read file, create global var t
t:=TF_ReadLines(t,5) ; pass on global var t created by TF(), read lines 5 to end of file, assign result to t 
MsgBox % "TF(), example 1:`n" t

TF("TestFile.txt", "MyVar") ; read file, create global var MyVar
MyVar:=TF_ReadLines(MyVar,5) ; pass on global var MyVar created by TF(), read lines 5 to end of file, assign result to MyVar
MsgBox % "TF(), example 2:`n" MyVar

; Note how we can use TF() here
t:=TF_ReadLines(TF("TestFile.txt"),5) ; pass on global var t created by TF(), read lines 5 to end of file, assign result to t 
MsgBox % "TF(), example 3:`n" t

MyVar:=TF_ReadLines(TF("TestFile.txt","MyVar"),5) ; pass on global var t created by TF(), read lines 5 to end of file, assign result to t 
MsgBox % "TF(), example 4:`n" MyVar

t:=TF_ReadLines(TF(F),5) ; pass on global var t created by TF(), read lines 5 to end of file, assign result to t
t:=TF_ReverseLines(t,5) ; pass on global var t created by TF(), reverse lines, assign result to t
MsgBox % "TF(), example 5:`n" t

; Work directly with the clipboard or another other variable
Clipboard=Line 1`nLine 2`nLine 3`nLine 4`nLine 5`nLine 6`nLine 7`nLine 8
Clipboard:=TF_RemoveLines(Clipboard, 3, 6) ; remove lines 3 to 6
MsgBox % "Clipboard, example 6:`n" Clipboard
```
    
**Note** TF_Merge, TF_Prepend, TF_Append currently do not support variables and only work with FILES.

**Common mistake(s):**


```autohotkey
MyVar:=TF_ReadLines("TestFile.txt",5) ; this is wrong. It is AN INCORRECT EXAMPLE!
```

The example above is incorrect: You pass on a file so the output is
testfile_copy.txt. In this case nothing meaningful is assigned to the
variable MyVar. Correct would be:


```autohotkey
MyVar:=TF_ReadLines(MyVar,5) ; this is OK
MyVar:=TF_ReadLines(TF("TestFile.txt","MyVar"),5) ; this is OK
```

--------------------------------------------------------------------------

# Functions and Examples

**Note:** Some of these functions are very similar, but they are included in the library for ease of use ...

You can find examples of most functions in the "example script" here [http://www.autohotkey.com/forum/viewtopic.php?p=280363#280363](http://www.autohotkey.com/forum/viewtopic.php?p=280363#280363)

<a name="TF"></a>

**TF(TextFile, CreateGlobalVar="T")**

- Purpose: Read contents of file to create global variable, **T** by default.
- Parameters: TextFile, CreateGlobalVar (=preferred name of global variable)
- Credits: See TF thread & discussion http://www.autohotkey.com/forum/viewtopic.php?p=313120#313120

Note: see background info and examples above at [Files and Variables](#FilesAndVariables).

<a name="TF_CountLines"></a>

**TF_CountLines(Text)**

- Purpose: Returns the number of lines in a file or variable
- Parameters: Text
- Credits: SKAN


```autohotkey
MsgBox % TF_CountLines("File.txt") ; show the number of lines of file in a MsgBox
Lines:=TF_CountLines("File.txt") ; store the number of lines of file in a variable
```

<a name="TF_Count"></a>    

**TF_Count(String, Char)**

- Purpose: Count the number of occurrence of Char using StringReplace
- Parameters: String, Char

Notes: do **not** use it to count lines (using `n) because it will return an incorrect value,
use TF_CountLines for counting lines. TF_Count does not support files and only works with variables.


```autohotkey
MsgBox % TF_Count("Hello this is an example", "i") ; count how many i's there are in the string
```

<a name="TF_ReadLines"></a>

**TF_ReadLines(Text, StartLine = 1, EndLine = 0, RemoveTrailing = 0)**

- Purpose: Return the specified lines, can be used to read sections of a file or variable.
- Parameters: Text, StartLine, EndLine

Note: by default it will add a newline character to the last line, so if you want to 
append new data you don't have to start it with a newline char. If you don't want
that use RemoveTrailing = 1.


```autohotkey
MsgBox % TF_ReadLines("File.txt",5) ; Read lines 5 to end of file, show result in a MsgBox
Lines:=TF_ReadLines("File.txt",5) ; Read lines 5 to end of file, store result in variable
MsgBox % TF_ReadLines("File.txt",5,0,1) ; 0 for end line indicates until end of file, remove trailing empty line.
```

<a name="TF_Tail"></a>

**TF_Tail(Text, Lines = 1, RemoveTrailing = 0, ReturnEmpty = 1)**

- Purpose: Read last X lines from file or variable
- Parameters: Text, Lines, RemoveTrailing (0 or 1), ReturnEmpty (0 or 1)
- Credits: incl. ideas from Tuncay

Notes:

1.  Lines can be a negative number: -1 will get the second to last line.
    If you use a negative number it will ALWAYS retrieve a SINGLE line
2.  By default TF_Tail will return empty lines, if you don't want that
    use ReturnEmpty = 0
3.  By default TF_Tail will add a newline character to the last line, so
    if you want to append new data to the returned data you don't have
    to start it with a newline char. If you don't want that use
    RemoveTrailing = 1


```autohotkey
MsgBox % TF_Tail("File.txt", 3) ; get the last three lines
MsgBox % TF_Tail("File.txt", -2) ; get second to last line, negative values only return one line
MsgBox % TF_Tail("File.txt", 5, 0, 0) ; return the last five lines, with trailing new line and excluding empty lines
```

<a name="TF_Replace"></a>

**TF_Replace(Text, SearchText, Replacement="")**

- Purpose: Find and Replace text in entire file (using StringReplace)
- Parameters: Text, SearchText, Replacement


```autohotkey
TF_Replace("File.txt","key","lock")            ; pass on a file, replace the word "key" with "lock" in file_copy.txt
```

<a name="TF_ReplaceInLines"></a>

**TF_ReplaceInLines(Text, StartLine = 1, EndLine = 0, SearchText = "", ReplaceText = "")**

- Purpose: Find and Replace text on specific lines (using StringReplace)
- Parameters: Text, StartLine, EndLine, SearchText, ReplaceText


```autohotkey
TF_ReplaceInLines("!File.txt","1,3,9","","key","lock") ;  update source file, replace "key" with "lock" in lines 1, 3 and 9
```

<a name="TF_RegExReplace"></a>

**TF_RegExReplace(Text, NeedleRegEx = "", Replacement = "")**

- Purpose: Find and Replace text in entire file (using RegExReplace)
- Parameters: Text, NeedleRegEx, Replacement


```autohotkey
TF_RegExReplace("File.txt","im)^(.*)$","[$1]") ; pass on a file, wrap all lines in []
```

<a name="TF_RegExReplaceInLines"></a>

**TF_RegExReplaceInLines(Text, StartLine = 1, EndLine = 0, NeedleRegEx= "", Replacement = "")**

- Purpose: Find and Replace text in specific lines (using RegExReplace)
- Parameters: Text, StartLine, EndLine, NeedleRegEx, Replacement


```autohotkey
TF_RegExReplaceInLines("File.txt",3,8," [a-z]{3} "," lock ")  ; replace any three letter word with "lock" in lines 3 to 8
```

<a name="TF_RemoveLines"></a>

**TF_RemoveLines(Text, StartLine = 1, EndLine = 0)**

- Purpose: Remove specified lines from file
- Parameters: Text, StartLine, EndLine


```autohotkey
TF_RemoveLines("File.txt", 5, 10) ; remove lines 5 to 10 from file
```

Note: If you pass on a negative value for StartLine, example
TF_RemoveLines(Text, -5, .... it will remove these lines from the end
of Text, so -5 will remove the last five lines. The EndLine parameter is
ignored.


<a name="TF_RemoveBlankLines"></a>

**TF_RemoveBlankLines(Text, StartLine = 1, EndLine = 0)**

- Purpose: Remove blank lines from file (in specified section)
- Parameters: Text, StartLine, EndLine


Note: also removes lines with only tabs & spaces e.g. "white space"


```autohotkey
TF_RemoveBlankLines("File.txt") ; remove blanklines in entire file
```


<a name="TF_RemoveDuplicateLines"></a>

**TF_RemoveDuplicateLines(Text, StartLine = 1, Endline = 0, Consecutive= 0, CaseSensitive = false)**

- Purpose: Remove duplicate lines, optionally you can specify if they need to be Consecutive and / or Case sensitive
- Parameters: Text, StartLine, EndLine, Consecutive (0 or 1), CaseSensitive (true or false)


```autohotkey
TF_RemoveDuplicateLines("File.txt","","",1,false) ; remove only Consecutive duplicate lines
```

<a name="TF_InsertLine"></a>

**TF_InsertLine(Text, StartLine = 1, Endline = 0, InsertText = "")**

- Purpose: Insert text in specified lines
- Parameters: Text, StartLine, Endline, InsertText


```autohotkey
TF_InsertLine("File.txt","2,4,9",5,"---")     ; insert --- in lines 2 4 and 9. 5 is endline will be ignored
```

<a name="TF_ReplaceLine"></a>

**TF_ReplaceLine(Text, StartLine = 1, Endline = 0, ReplaceText = "")**

- Purpose: Replace specified lines with new text
- Parameters: Text, StartLine, Endline, ReplaceText


```autohotkey
TF_ReplaceLine("File.txt","1+3",8,"---")      ; replace lines 1 4 and 7. 8 is end line so no more lines are processed after 
```

<a name="TF_InsertPrefix"></a>

**TF_InsertPrefix(Text, StartLine = 1, EndLine = 0, Text = "")**

- Purpose: Insert a text at the BEGINNING of each of the specified lines
- Parameters: Text, StartLine, Endline, Text


```autohotkey
TF_InsertPrefix("File.txt",5,8, "Hello ")          ; Prefix Hello in lines 5 to 8
```

<a name="TF_InsertSuffix"></a>

**TF_InsertSuffix(Text, StartLine = 1, EndLine = 0 , Text = "")**

- Purpose: Append a text at the END of each of the specified lines
- Parameters: Text, StartLine, Endline, Text


```autohotkey
TF_InsertSuffix("File.txt","2-4,6-9","", " Hello")  ; Suffix Hello in lines 2 to 4 and 6 to 9
```

<a name="TF_TrimLeft"></a>

**TF_TrimLeft(Text, StartLine = 1, EndLine = 0, Count = 1)**

- Purpose: Trim number of specified columns from specified lines
- Parameters: Text, StartLine, Endline, Text, Count


```autohotkey
TF_TrimLeft("File.txt","","",25)                    ; Trim Left 25 Characters of all lines
```

<a name="TF_TrimRight"></a>

**TF_TrimRight(Text, StartLine = 1, EndLine = 0, Count = 1)**

- Purpose: Trim number of specified columns from specified lines
- Parameters: Text, StartLine, Endline, Text, Count


```autohotkey
TF_TrimRight("File.txt","4,6,8","",45)              ; Trim Right 45 Characters in lines 4, 6 and 8
```

<a name="TF_AlignLeft"></a>

**TF_AlignLeft(Text, StartLine = 1, EndLine = 0, Columns = 80, Padding = 0)**

- Purpose: Align lines according to number of specified columns
- Parameters: Text, StartLine, Endline, Columns, Padding
 - incl. ideas from SKAN

Note: Using the Align functions will not take into account any leading or trailing
spaces a line had BEFORE processing. So all white space before and after the text
is removed before the text is aligned.

~~~~
Padding = 0 Remove trailing white space
Padding = 1 Keep trailing white space
~~~~


```autohotkey
TF_AlignLeft("File.txt","","",90, 1)    ; AlignLeft all lines, keep trailing white space
```

<a name="TF_AlignCenter"></a>

**TF_AlignCenter(Text, StartLine = 1, EndLine = 0, Columns = 80, Padding = 0)**

- Purpose: Align lines according to number of specified columns
- Parameters: Text, Columns, StartLine, Endline, Text
- uses some of SKAN functions

Note: Using the Align functions will not take into account any leading or trailing
spaces a line had BEFORE processing. So all white space before and after the text
is removed before the text is aligned.

~~~~
Skip = 0 process empty lines, fill with spaces
Skip = 1 skip empty lines, do not fill with spaces
~~~~


```autohotkey
TF_AlignCenter("File.txt","","",150, 1) ; AlignCenter, all lines skip empty lines, do not fill with spaces
```

<a name="TF_AlignRight"></a>

**TF_AlignRight(Text, StartLine = 1, EndLine = 0, Columns = 80, Skip = 0)**

- Purpose: Align lines according to number of specified columns
- Parameters: Text, Columns, StartLine, Endline, Skip (0 or 1)
 - uses some of SKAN functions

Note: Using the Align functions will not take into account any leading
or trailing spaces a line had BEFORE processing. So all white space
before and after the text is removed before the text is aligned.

~~~~
Skip = 0 process empty lines, fill with spaces
Skip = 1 skip empty lines, do not fill with spaces
~~~~


```autohotkey
TF_AlignRight("File.txt","","", 190, 0) ; AlignRight, all lines, do not skip empty lines fill them with spaces
```

<a name="TF_LineNumber"></a>

**TF_LineNumber(Text, Leading = 0, Restart = 0, Char = 0)**

- Purpose: Insert line numbers before each line
- Parameters: Text, Leading, Restart, Char
- Credits: incl. ideas from ribbet.1

~~~~
Leading = 0 No padding with leading zeros
Leading >= 1 Include padding with leading zeros (001 v 1)
Restart = restart counter after X lines (starting again at 1)
Char = character to use for leading/padding. Default 0, but can be any character. Tip: use a space.
~~~~


```autohotkey
TF_LineNumber("File.txt",1,15,A_Space)  ; Add linenumbers, padding with spaces, linenumbers are aligned right because of space
```

<a name="TF_ConCat"></a>

**TF_ConCat(FirstTextFile, SecondTextFile, OutputFile, Blanks = 0, FirstPadMargin = 0, SecondPadMargin = 0)**

- Purpose: Join Text files side by side (line1-from-file1 line1-from-file2 newline line2-from-file1 line2-from-file2 newline etc)
- Parameters: FirstTextFile, SecondTextFile, OutputFile, Blanks, FirstPadMargin, SecondPadMargin

Based on: CONCATenate text files, ftp://garbo.uwasa.fi/pc/ts/tsfltc22.zip - Backup: http://www.retroarchive.org/garbo/pc/ts/

With TF_ConCat you can join Text files side by side. Blanks is number of blanks between lines.
You can pad blanks the right margin of either of the text files, for this use FirstPadMargin and SecondPadMargin.

<a name="TF_ColGet"></a>

**TF_ColGet(Text, StartLine = 1, EndLine = 0, StartColumn = 1, EndColumn = 1, Skip = 0)**

- Purpose: Get specified columns from text file
- Parameters: Text, StartLine, EndLine, StartColumn, EndColumn, Skip (0 or 1)

~~~~
Note: A TAB character is 1 character so for files with TABS the column might not be where you expect it to be
skip = 0, DO NOT skip lines shorter then startcolumn position
skip = 1, skip lines shorter then startcolumn position
TF_ColGet and TF_ColPut accept negative StartColumn parameter. This can be used to "Get" or "Put" text in the x-th "column" from the right
~~~~


```autohotkey
TF_ColGet("File.txt","","",2,13)    ; get columns 2 to 13 from all lines, so it removes all other columns from the file or variable
```

<a name="TF_ColPut"></a>

**TF_ColPut(Text, Startline = 1, EndLine = 0, StartColumn = 1, Text = "", Skip = 0)**

- Purpose: Insert text at specified columns in text file
- Parameters: Text, StartLine, EndLine, StartColumn, EndColumn, Skip (0 or 1)

Based on: COLPUT.EXE & CUT.EXE, ftp://garbo.uwasa.fi/pc/ts/tsfltc22.zip - Backup: http://www.retroarchive.org/garbo/pc/ts/

~~~~
Note: A TAB character is 1 character so for files with TABS the column might not be where you expect it to be
skip = 0, DO NOT skip lines shorter then startcolumn position
skip = 1, skip lines shorter then startcolumn position
TF_ColGet and TF_ColPut accept negative StartColumn parameter. This can be used to "Get" or "Put" text in the x-th "column" from the right
~~~~


```autohotkey
TF_ColPut("File.txt","","",5, "|", 0) ; insert | in column 5 of all lines including empty lines (= | will be at column 1)
```

<a name="TF_ColCut"></a>

**TF_ColCut(Text, StartLine = 1, EndLine = 0, StartColumn = 1, EndColumn = 1)**

- Purpose: Remove specified columns from text file
- Parameters: Text, StartLine, EndLine, StartColumn, EndColumn

Based on: COLPUT.EXE & CUT.EXE, ftp://garbo.uwasa.fi/pc/ts/tsfltc22.zip


```autohotkey
TF_ColCut("File.txt", "2+2", "", 4, 38)  ; remove columns 4 to 38 in lines 2 4 6 8 etc
```

<a name="TF_ReverseLines"></a>

**TF_ReverseLines(Text, StartLine = 1, EndLine = 0)**

- Purpose: Reverse the order of specified lines
- Parameters: Text, StartLine, EndLine

Note: Startline parameter can not use specific lines, sections or incremental values here


```autohotkey
TF_ReverseLines("File.txt",2,9) ; reverse lines 2 to 9
```

<a name="TF_Find"></a>

**TF_Find(Text, StartLine = 1, EndLine = 0, SearchText = "", ReturnFirst = 1, ReturnText = 0)**

- Purpose: Find text using RegExMatch, return line(s), text or lines and text
- Parameters: Text, StartLine, EndLine, SearchText, ReturnFirst, ReturnText

TF_Find(Lines) uses Regular Expressions - This means that if you use certain characters which
have a special meaning in a RegEx: **\\.\*?+[{|()\^\$** they must be preceded by a backslash
to be seen as literal. For example, **\\.** is a literal period and **\\\\** is a literal backslash.
Escaping can be avoided by using **\\Q**...**\\E**. For example: **\\QLiteral Text\\E**.
See [http://ahkscript.org/docs/commands/RegExMatch.htm](http://ahkscript.org/docs/commands/RegExMatch.htm)
for further information.

~~~~
ReturnFirst = 0 return multiple lines 
ReturnFirst = 1 return first line only

ReturnText = 0 return line numbers only
ReturnText = 1 return entire line (text). This simulates a basic grep feature
ReturnText = 2 return line numbers + entire line (text). This simulates a basic grep feature 
~~~~


```autohotkey
MsgBox % TF_Find("File.txt", "", "", "keys") ; return first line number with keys in it
MsgBox % TF_Find("File.txt", "", "", " c[a-z]+s ", 0, 1) ; find all lines with words that start with a c an end with an s
```

<a name="TF_SplitFileByLines"></a>

**TF_SplitFileByLines(Text, SplitAt, Prefix = "file", Extension = "txt", InFile = 1)**

- Purpose: Split a text file in to several others based on number of lines
- Parameters: Text, SplitAt (number), Prefix, Extension, InFile (0, 1 or 2)

~~~~
SplitAt = Number of lines (three methods, see below)
Prefix . Extension = filenameAUTOINCREMENT.Extension (Example: part_ . txt)
InFile = 0 skip line e.g. do not include the actual line in any of the output files
InFile = 1 include line IN current file
InFile = 2 include line IN next file
~~~~

Note: If you pass on a variable to TF_SplitFileByLines, the array size will be returned
as Prefix0 and the array elements as Prefix1, Prefix2 etc similar to the AHK StringSplit
command. The Extension parameter is ignored when using variables. Some characters are
illegal such as - + @ % & \* _ \\ / [ ] etc to use in Prefix, stick to a-z A-Z.

Options for "SplitAt" parameter:

~~~~
a) One numerical value, example TF_SplitFileByLines(Text, "25", .... 
   will split text every 25 lines until the end 
b) Split at rotating line lengths using a dash "-" as separator, example TF_SplitFileByLines(Text, "5-10-15", .... 
   will split text at 5 10 15 lines, until the end so 5 lines, 10 lines, 15 lines, 5 lines, 10 lines etc
c) Split at specific lines using a comma "," as separator, example TF_SplitFileByLines(Text, "5,81,135", .... 
   will split text at lines 5,81,135 until end of file (e.g. last file will be from line 135 until the end)
~~~~


```autohotkey
TF_SplitFileByLines("File.txt", 2, "part", "zec", 1) ; split source file every 2 lines, include 2nd line INFILE
; illustrate use of variables and returned arrays:
TF_SplitFileByLines(Variable, 2, "part", "zec", 1)
MsgBox % "Array size: " . Part0 . "`n1st array element: "  Part1
```

<a name="TF_SplitFileByText"></a>

**TF_SplitFileByText(Text, SplitAt, Prefix = "file", Extension = "txt", InFile = 1)**

- Purpose: Split a text file in to several others based on text
- Parameters: Text, SplitAt (text, can be RegEx), Prefix, Extension, InFile (0, 1 or 2)

~~~~
SplitAt = Text, can be RegEx
Prefix . Extension = filenameAUTOINCREMENT.Extension (Example: part_ . txt)
InFile = 0 skip line e.g. do not include the actual line in any of the output files
InFile = 1 include line IN current file
InFile = 2 include line IN next file
~~~~

Note: If you pass on a variable to TF_SplitFileByLines, the array size will be returned
as Prefix0 and the array elements as Prefix1, Prefix2 etc similar to the AHK StringSplit
command. The Extension parameter is ignored when using variables. Some characters are 
illegal such as - + @ % & \* _ \\ / [ ] etc to use in Prefix, stick to a-z A-Z.


```autohotkey
TF_SplitFileByText("File.txt", "button", "part", "zec", 1)   ; split source file on every line with the word button, include that line INFILE
; illustrate use of variables and returned arrays:
TF_SplitFileByText(Variable, "keyboard", "part", "zec", 1)
MsgBox % "Array size: " . Part0 . "`n1st array element: "  Part1
```

**TF_Merge(FileList, Separator = "\`n", FileName = "merged.txt")**

- Purpose: Merge several files into one
- Parameters: FileList, Separator (what to put between two files, newline by default), FileName (name of output file, Prefix with a ! to overwrite target file)

Example FileList:

~~~~
FileList=
(
file1.txt
file2.txt
file3.txt
)
~~~~

Note: TF_Merge, TF_Prepend, TF_Append currently do not support variables and only work with FILES.


```autohotkey
; using Loop (files & folders) to create one quickly if you want to merge all TXT files for example:
Loop, c:\*.txt
  FileList .= A_LoopFileFullPath "`n"
TF_Merge(FileList) ; will create merged.txt, you can use ! to overwrite an existing file if you want

; using FileSelectFile to select files to merged: (Thanks for asking Vitor, http://www.autohotkey.com/forum/viewtopic.php?p=335329#335329)
FileDelete merged.txt ; not required
FileList=
FileSelectFile, FileList, M 1,,, *.txt ; M allows you to select multiple files while holding down the left ctrl button
If (ErrorLevel = 1) or (FileList = "")
   ExitApp ; no files selected
Path:=TF_ReadLines(FileList,1,1,1) ; the first line holds the directory of the selected files, so read path
FileList:=TF_RemoveLines(FileList,1,1) ; remove path from filelist
FileList:=TF_InsertPrefix(FileList, "", "", Path . "\") ; make sure all files have full paths to file so the are read correctly
TF_Merge(FileList) ; will create a file in the current script dir called merged.txt you can also specify another filename, take into account the filedelete merged.txt above

; You could skip the Path:= step above by calling TF_ReadLines directly in TF_InsertPrefix, but you would have to delete the first line AFTER it like so:
;FileList:=TF_InsertPrefix(FileList, "", "", TF_ReadLines(FileList,1,1,1) . "\")
;FileList:=TF_RemoveLines(FileList,1,1)
```

<a name="TF_Prepend"></a>

**TF_Prepend(File1, File2)**

- Purpose: Prepend file1 to file2 (file2 is changed, uses TF_Merge)
- Parameters: File1, File2

Note: TF_Merge, TF_Prepend, TF_Append currently do not support variables and only work with FILES.

<a name="TF_Append"></a>

**TF_Append(File1, File2)**

- Purpose: Append file1 to file2 (file2 is changed, uses TF_Merge)
- Parameters: File1, File2

Note: TF_Merge, TF_Prepend, TF_Append currently do not support variables and only work with FILES.

<a name="TF_Wrap"></a>

**TF_Wrap(Text, Columns = 80, AllowBreak = 0, StartLine = 1, EndLine = 0)**

- Purpose: Wrap (specified) lines
- Parameters: Text, Columns, AllowBreak (0 or 1), StartLine, EndLine

~~~~
AllowBreak = 0 will not "break" words, so it will take into account whole words and not chop them off. 
AllowBreak = 1 will break words
~~~~


```autohotkey
TF_Wrap("File.txt",60)            ; wrap at col 60
```    

<a name="TF_WhiteSpace"></a>

**TF_WhiteSpace(Text, RemoveLeading = 1, RemoveTrailing = 1, StartLine = 1, EndLine = 0)**

- Purpose: Remove leading and/or trailing whitespace
- Parameters: Text, RemoveLeading (0 or 1), RemoveTrailing (0 or 1), StartLine, EndLine

~~~~
RemoveLeading = 0  Do not remove leading white space of lines
RemoveLeading = 1  Remove leading white space of lines
RemoveTrailing = 0 Do not remove trailing white space of lines
RemoveTrailing = 1 Remove trailing white space of lines       
~~~~


```autohotkey
TF_WhiteSpace("File.txt", 1, 0, "5-10") ; remove leading and keep trailing whitespace in lines 5 to 10
```    

<a name="TF_Substract"></a>

**TF_Substract(File1, File2, PartialMatch = 0)**

- Purpose: Delete lines from file1 in file2 (using StringReplace)
- Parameters: File1, File2, PartialMatch (0 or 1)

~~~~
File2: you can use !file2 to overwrite file2 otherwise output is file2_copy.txt
PartialMatch = 0 lines from file1 must appear as is, case insensitive 
PartialMatch = 1 allow for paRTIal match of line
PartialMatch = 2 allow for paRTIal match of line, but remove entire line (uses RegEx, new as of v3.4)
~~~~

<a name="TF_RangeReplace"></a>

**TF_RangeReplace(Text, SearchTextBegin, SearchTextEnd, ReplaceText = "", CaseSensitive = "False", KeepBegin = 0, KeepEnd = 0)**

- Purpose: A Range Replacement allows you to perform a replacement on text whose beginning and ending remains the same, but whose middle contents might change.
- Parameters: SearchTextBegin, SearchTextEnd, ReplaceText, CaseSensitive (True or False), KeepBegin (0 or 1), KeepEnd (0 or 1)

Note: Similar to "BK Replace EM" Range Replacement. It is basically an easier shortcut for a more complex RegExp.

~~~~
KeepBegin = 0 Remove SearchTextBegin from file
KeepBegin = 1 Do not remove SearchTextBegin from file (saves you the trouble of putting it back in via ReplaceText)
KeepEnd = 0 Remove SearchTextEnd from file
KeepEnd = 1 Do not remove SearchTextEnd from file (saves you the trouble of putting it back in via ReplaceText)

SearchTextBegin = "" e.g. empty means from START of File *1
SearchTextEnd = "" e.g. empty means until END of File *1
~~~~

\*1 if you pass on empty strings to SearchTextBegin and SearchTextEnd the entire file will 
replaced with the replacement text.
SearchTextBegin/End **can** be on the same line. Remember this LIB mainly operates on
a line by line basis. This Function only operates on the FIRST SearchTextBegin to 
the FIRST SearchTextEnd it finds.


```autohotkey
Range=[insert this`ntext for the`nrange replace Text`ntest function]
TF_RangeReplace("File.txt", "Create hotkeys for keyboard", "into an EXE file", Range)
```

<a name="TF_MakeFile"></a>

**TF_MakeFile(Text, Lines = 1, Columns = 1, Fill = " ")**

- Purpose: Create file of X lines and Y columns, fill with space or other character(s)
- Parameters: TextFile (new file to be created), Lines, Columns, Fill

Sometimes you need an "empty" file before you can start adding line numbers or putting data into a file.

<a name="TF_Tab2Spaces"></a>

**TF_Tab2Spaces(Text, TabStop = 4, StartLine = 1, EndLine = 0)**

- Purpose: Convert tabs to spaces, shorthand for TF_ReplaceInLines
- Parameters: Text, TabStop, StartLine, EndLine
- Credits: incl. ideas from infogulch

~~~~
TabStop = number of spaces to replace a TAB with, so 4 means a TAB will be replaced by 4 spaces
~~~~

<a name="TF_Spaces2Tab"></a>

**TF_Spaces2Tab(Text, TabStop = 4, StartLine = 1, EndLine = 0)**

- Purpose: Convert tabs to spaces, shorthand for TF_ReplaceInLines
- Parameters: Text, TabStop, StartLine, EndLine
- Credits: incl. ideas from infogulch

~~~~
TabStop = number of spaces to replace with a TAB, so 4 means 4 spaces will be replaced by a TAB
~~~~

<a name="TF_Sort"></a>

**TF_Sort(Text, SortOptions = "", StartLine = 1, EndLine = 0)**

- Purpose: Sort (section of) TextFile
- Parameters: Text, SortOptions, StartLine, EndLine

~~~~
SortOptions: use the SORT options http://ahkscript.org/docs/commands/Sort.htm
~~~~

Note: StartLine can not have multiple sections, increments or multiple lines in this case.
When dealing with variables/strings instead of text files the native AHK Sort command is
of course more useful.

<a name="TF_Join"></a>

**TF_Join(Text, StartLine = 1, EndLine = 0, SmartJoin = 0, Char = 0)**

- Purpose: Join lines (section of)
- Parameters: Text, StartLine, EndLine, SmartJoin, Char

~~~~
SmartJoin: Detect if CHAR(s) is/are already present at the end of the line before joining the next, this to prevent unnecessary double spaces for example.
Char: character(s) to use between new lines, defaults to a space. To use nothing use ""
~~~~

Note: StartLine can not use increments. It can have multiple sections.

<a name="TF_Save"></a>

**TF_Save(Text, FileName, OverWrite = 1)**

- Purpose: To save a variable to a file
- Parameters: Text, FileName, OverWrite (O or 1)

~~~~
OverWrite = 0 create filename_copy.ext if filename.ext exists
OverWrite = 1 will overwrite filename.ext if filename.ext exists (default)
~~~~

# TF Lib Errors

1. Error when using the ! prefix.  
   The correct syntax is "!file.txt". If you do this !"file.txt" (the ! is not within the quotes it will not be able to read a file)
2. If you pass on a single zero as text ```TF_TrimLeft("0",1,1,3)``` and there is no file with that name ```"0"``` it will shown an error. More as one zero as "text" is OK.

# History

**History v3.8, 11 December 2020**

- Prevent TF_Sort from removing last character from the last line https://github.com/hi5/TF/issues/11

**History v3.7, 16 April 2017**

- Added: additional check for passing on just zero/zeros as text to TF functions in TF_GetData() - see v3.6 below
- Fix: changed readme.md to fix rendering issue of MD files on GH - https://github.com/hi5/TF/issues/5

**History v3.6, 25 December 2014**

- Added: Added additional minor error check in TF_GetData - if "Text" is "false", there is nothing to process so Exit

**History v3.5, 2 August 2014**

- Changed A_ScriptDir to A_WorkingDir in TF_ReturnOutPut - https://github.com/hi5/TF/issues/1
- Fixed: TF_ColGet negative startcolumn and sections now work correctly

**History v3.4, 30 October 2010 - UNRELEASED at the time**

- New function: TF_Join: joining lines.
- Added: A_ThisFunc to helper function TF_MatchList, this may help with error messages such as: Invalid StartLine parameter (non numerical character) - Function used: A_ThisFunc
- Added/Change: New option for StartLine, Negative startline operate on last X lines was already the case for TF_removelines, applies to all TF functions
- Added: TF_Substract: Partialmatch = 2 now deletes entire line on partial match, see TF DOC for more info.
- Added: TF_ColGet and TF_ColPut now accept negative StartColumn parameter. Can be used to Get or Put text in the xth "column" from the right, see description above - TF_ColCut does **not** support this as you can use TF_TrimRight.
- Added: TF_ColGet, TF_ColPut and TF_ColCut can now process multiple columns in one go. Format: CSV or incremental (DO **not** use sections for TF_ColPut due to unexpected end results)
- Minor improvements to the documentation
- removed TF_FindLines from lib (was already deprecated)

**History v3.3, 16 April 2010**

- Fixed: If you used variables with any of the replacement functions or tried to remove empty lines from a VARIABLE in the following format: variable:=TF_..(variable,"search","replace") and the searchtext was NOT present in the variable it returned an empty variable (e.g. deleted the contents of "variable"). It affected TF_ReplaceInLines, TF_Replace, TF_RegExReplaceInLines, TF_RegExReplace, TF_RemoveBlankLines, TF_RangeReplace\. The built-in check only worked correctly for FILES and with the introduction of variables in TF 3 this didn't surface during testing.
- Fixed: documentation error for TF_Merge (separator and filename where swapped) and added examples on how to use TF_Merge in a Loop and with FileSelectFile

**History v3.2, 20 February 2010**

- Fixed: TF_AlignRight: due to a bug it didn't work as it should have, it prepended the number of spaces rather than aligning the text at the specified width.
- Changed: TF_GetData (helper function) should now avoid unnecessary IfNotExist for files, for scripts with many loops in combination with variables it should improve the speed slightly
- Changed: TF_SplitFileByLines: New options for SplitAt, now three methods available, see notes at function description
- Changed: TF_RemoveLines: New option for StartLine, if negative value is used it will remove the last X lines from file, see notes at function description

**History v3.1, 09 December 2009**

- Changed: Rewrite of TF_Find. Can now return multiple lines (like TF_Findlines used to), not only linenumbers but the entire line (text) of found lines so it can be used as a basic grep. Now uses RegExp, see compatibility notes.
- Deprecated: TF_FindLines (see change TF_Find). Kept in for backwards compatibility
- Updated and expanded documentation with examples and a hopefully "better" introduction highlighting some basic concepts

**History v3.0, 27 November 2009**

Complete overhaul of library, now accepts files & variables for input and output:

- Changed: New parameter for TF_Readlines & TF_Tail: trailing new line now optional
- Added: TF_Save, shorthand for filedelete+fileappend
- Added: TF_GetData, helper function to determine if a file or a variable is passed on to function
- Added: TF() To read a text file in global var, t by default - Credits various ...
- Added: TF_ReturnOutput has replaced: Overwrite, MakeCopy and the newly developed ReturnStr
- Changed: Complete rewrite of TF_Tail, new options - borrowed bits from Tuncay (Thanks!)
- Changed: MakeMatchList: Removed TF_Countlines (one less fileread), Pass on "String" and not a TextFile
- Fix: TF_ConCat & TF_MakeFile didn't write output file - same bug as splitby* earlier.

**History v2.5 fix, 01 November 2009**

- Unreleased, but available on request :-) Note that in 2.4a TF_MakeFile and TF_ConCat do not produce output files, easy to fix if you are determined to use an older version of TF.

**History v2.4a fix, 10 August 2009:**

- The fix for TF_SplitFileBy* functions of 2.3b wasn't complete, now  it should work correctly (it didn't write the last file)

**History v2.4 update, 06 August 2009:**

- Added: TF_Sort

**History v2.3b update, 03 August 2009:**

- Fixed: No output problem for TF_SplitFileBy* functions, "bug" introduced by 2.3a "Built in Check if TextFile actually exists"
- TF_RemoveBlankLines check if file has empty empty lines to start with, if not return and do nothing (does not create file_copy identical to file)
- TF_RangeReplace same fix as 2.3a

**History v2.3a update/bugfix (29 July 2009, HugoV, ribbet.1, Murp|e)**

Fixed/Changed:

- New features in TF_LineNumber: Restart + Choice of leading/padding character. Thanks for the idea ribbet.1, [http://www.autohotkey.com/forum/viewtopic.php?p=284687#284687](http://www.autohotkey.com/forum/viewtopic.php?p=284687#284687)
- Built in Check if TextFile actually exists, to prevent creation of empty file(s). Thanks for the idea Murp|e, [http://www.autohotkey.com/forum/viewtopic.php?p=284649#284649](http://www.autohotkey.com/forum/viewtopic.php?p=284687#284687)
- TF_Replace: if SearchText wasn't present in TextFile the function never returned (stuck in endless loop)
- TF_ReplaceInLines: if SearchText wasn't present in TextFile simple return an do nothing (faster, does not create file_copy)
- TF_RegExReplaceInLines: if NeedleRegEx wasn't present in TextFile simple return an do nothing (faster, does not create file_copy)

**History v2.3 (28 July 2009)**

- Added: TF_Tab2Spaces - Note: Thanks to infogulch
- Added: TF_Spaces2Tab - ditto
- Added: TF_RangeReplace
- Added: TF_MakeFile

Also thanks to Murp|e for pointing out some errors in the documentation (TF_COL\* functions).

**History v2.2 (10 July 2009)**

- Added: TF_Substract
- Added: TF_WhiteSpace
- Added: TF_Wrap

**History v2.1**

- Added: TF_Prepend
- Added: TF_Append

**History v2.0 (Overhaul by HugoV using TXT lib as starting point)**

- Renamed: TF_TotalLines to TF_CountLines (seemed more logical)
- Removed: TF_GetCSV, TF_SetCSV to keep the focus on TXT files, CSV requires a different library IMHO (HugoV)
- Introduced: _MakeMatchList_ and adjusted TF_ReadLines, TF_RemoveLines, TF_InsertPrefix, TF_InsertSuffix, TF_TrimLeft, TF_TrimRight, TF_ColGet, TF_ColPut, TF_ColCut accordingly (HugoV)
     - Make a MatchList which is used in various functions (listed above)
     - Using a MatchList gives greater flexibility so you can process multiple sections of lines in one go avoiding repetitive fileread/append actions
     - You can pass on multiple lines (sections) If you quote the StartLine parameter. Examples of StartLine, Endline:
     5 ; start from line 5 to the end (StartLine)
     5, 15 ; lines 5 to 15 (StartLine, EndLine)
     "5,13,45,67", 135) ; lines 5,13,45,67, ignore 135 ("StartLine", Endline - EndLine value is ignored)
     "5-13,45-51", 135) ; lines 5 to 13, 45 to 51 ignore 135 ("StartLine", Endline - EndLine value is ignored)
     - Incremental processing of lines, example startline, endline - endline will not be ignored
     "2+3", 150 ; start with line 2, increment 3 up-to line 150 so 2,5,8,11,14 etc
     "5+15" ; start with line 5, increment 15 until end of file so  5,20,35 etc
- Introduced: _OverWrite_ & _MakeCopy_ (Heresy / HugoV)
     - No longer necessary to pass on full path, e.g. "file.txt" works OK now (HugoV)
     - Reduces size of library by removing repeating code sections for saving Output (Heresy)
     - Backup files: If a subdirectory "backup" is present in the directory of TextFile a backup is made before overwriting the original file (both for file.txt and file_copy.txt) with the BAK extension (HugoV)
     - Includes fix to remove trailing \`n added by most functions (HugoV)
- Introduced: SetWidth/space by SKAN for TF_Align* Functions http://www.autohotkey.com/forum/viewtopic.php?p=45880#45880
- Added: TF_ReplaceInLines. Similar to TF_Replace
- Added: TF_RegExReplaceInLines. Similar to TF_RegExReplace
- Added: TF_SplitFileByLines (HugoV)
- Added: TF_SplitFileByText (HugoV)
- Added: TF_Merge (HugoV)
- Added: TF_Find (HugoV, based on example by olegbl)
- Added: TF_FindLines (HugoV, based on example by olegbl)
- Change: TF_TrimLeft. 1 - it now processes all lines of the TextFile. 2 - you can now omit EndLine e.g. will process until end of file (HugoV)
- Change: TF_TrimRight. 1 - it now processes all lines of the TextFile. 2 - you can now omit EndLine e.g. will process until end of file (HugoV)
- Change: TF_AlignCenter, TF_AlignLeft, TF_AlignRight. Now use SetWidth/space by SKAN, more reliable also adds spaces to RIGHT side of line
- Change: TF_AlignLeft. 1 - you can now use StartLine, EndLine parameters. 2 - StartLine and/or EndLine can be omitted (HugoV)
- Change: TF_AlignRight. 1 - you can now use StartLine, EndLine parameters. 2 - StartLine and/or EndLine can be omitted (HugoV)
- Change: TF_ReadLines you can now omit EndLine e.g. will read until end of file (HugoV)
- Change: TF_RemoveBlankLines you can now use StartLine, EndLine parameters. EndLine can be omitted (HugoV)
- Change: TF_InsertLine modified to accommodate line sections and incremental lines.
- Change: TF_ReplaceLine modified to accommodate line sections and incremental lines.
- Change: TF_RegExReplace no longer returns "Counts" (HugoV)
- Change: TF_RemoveDuplicateLines 1 - no longer returns "Removed". 2 - can use start/end line, 3 - no longer uses sort but keeps original order intact, 4 - to methods to check consecutive lines or unique files in file (HugoV)
- Change: TF_ColGet, TF_ColPut, TF_ColCut. 1 - order of parameters changed, 2 - now accept Startline, EndLine parameters. (HugoV)
- Fixed: TF_RemoveDuplicateLines always produced empty Output file (HugoV)

**History prior 2.0 - TXT library (Heresy, HugoV)**

- 2008-06-18 : added TF_AlignLeft(), TF_AlignCenter(),  TF_AlignRight() (Heresy)
- 2008-06-18 : added TF_Tail(), TF_ReverseLines() (HugoV)
- 2008-06-17 : added TF_GetCSV(), TF_SetCSV() (Heresy)
- 2008-06-17 : added TF_LineNumber(), TF_Concat(), TF_ColGet(), TF_ColPut(), TF_ColCut() (HugoV)
- 2008-06-16 : added TF_RegExReplace(), TF_RemoveLines(), TF_TrimLeft(), TF_TrimRight() (Heresy)
- 2008-06-15 : added TF_TotalLines(), TF_ReadLines(), TF_RemoveDuplicateLines() (Heresy)

## Credits

This library is based on the *Library for Text file manipulation* started by [Heresy](http://www.autohotkey.com/forum/profile.php?mode=viewprofile&u=8193). I have contributed a number of functions to that library, but that version also has some "bugs" which are hopefully resolved in this TF library.

Thanks to:
Heresy, SKAN! (countlines, setwidth, spaces), Olegbl, (suggestion for find*), infogulch (suggestion for tab \<-\> spaces),
Murp|e (suggestions for documentation, check if file exists), ribbet.1 (New features in TF_LineNumber), Tuncay (help with
TF() and borrowed ideas for TAIL).


================================================
FILE: tf.ahk
================================================
/*
Name          : TF: Textfile & String Library for AutoHotkey
Version       : 3.8
Documentation : https://github.com/hi5/TF
AutoHotkey.com: https://www.autohotkey.com/boards/viewtopic.php?f=6&t=576
AutoHotkey.com: http://www.autohotkey.com/forum/topic46195.html (Also for examples)
License       : see license.txt (GPL 2.0)

Credits & History: See documentation at GH above.

Structure of most functions:

TF_...(Text, other parameters)
	{
	 ; get the basic data we need for further processing and returning the output:
	 TF_GetData(OW, Text, FileName)
	 ; OW = 0 Copy inputfile
	 ; OW = 1 Overwrite inputfile
	 ; OW = 2 Return variable
	 ; Text : either contents of file or the var that was passed on
	 ; FileName : Used in case OW is 0 or 1 (=file), not used for OW=2 (variable)

	 ; Creates a matchlist for use in Loop below
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; A_ThisFunc useful for debugging your scripts

	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			{
			...
			}
		 Else
			{
			...
			}
		}
	 ; either copy or overwrite file or return variable
	 Return TF_ReturnOutPut(OW, OutPut, FileName, TrimTrailing, CreateNewFile)
	 ; OW 0 or 1 = file
	 ; Output = new content of file to save or variable to return
	 ; FileName
	 ; TrimTrailing: because of the loops used most functions will add trailing newline, this will remove it by default
	 ; CreateNewFile: To create a file that doesn't exist this parameter is needed, only used in few functions
	}

*/

TF_CountLines(Text)
	{
	 TF_GetData(OW, Text, FileName)
	 StringReplace, Text, Text, `n, `n, UseErrorLevel
	 Return ErrorLevel + 1
	}

TF_ReadLines(Text, StartLine = 1, EndLine = 0, Trailing = 0)
	{
	 TF_GetData(OW, Text, FileName)
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			OutPut .= A_LoopField "`n"
		 Else if (A_Index => EndLine)
			Break
		}
	 OW = 2 ; make sure we return variable not process file
	 Return TF_ReturnOutPut(OW, OutPut, FileName, Trailing)
	}

TF_ReplaceInLines(Text, StartLine = 1, EndLine = 0, SearchText = "", ReplaceText = "")
	{
	 TF_GetData(OW, Text, FileName)
	 IfNotInString, Text, %SearchText%
		Return Text ; SearchText not in TextFile so return and do nothing, we have to return Text in case of a variable otherwise it would empty the variable contents bug fix 3.3
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			{
			 StringReplace, LoopField, A_LoopField, %SearchText%, %ReplaceText%, All
			 OutPut .= LoopField "`n"
			}
		 Else
			OutPut .= A_LoopField "`n"
		}
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

TF_Replace(Text, SearchText, ReplaceText="")
	{
	 TF_GetData(OW, Text, FileName)
	 IfNotInString, Text, %SearchText%
		Return Text ; SearchText not in TextFile so return and do nothing, we have to return Text in case of a variable otherwise it would empty the variable contents bug fix 3.3
	 Loop
		{
		 StringReplace, Text, Text, %SearchText%, %ReplaceText%, All
		 if (ErrorLevel = 0) ; No more replacements needed.
			break
		}
	 Return TF_ReturnOutPut(OW, Text, FileName, 0)
	}

TF_RegExReplaceInLines(Text, StartLine = 1, EndLine = 0, NeedleRegEx = "", Replacement = "")
	{
	 options:="^[imsxADJUXPS]+\)" ; Hat tip to sinkfaze http://www.autohotkey.com/forum/viewtopic.php?t=60062
	 If RegExMatch(searchText,options,o)
		searchText := RegExReplace(searchText,options,(!InStr(o,"m") ? "m$0" : "$0"))
	 Else searchText := "m)" . searchText
	 TF_GetData(OW, Text, FileName)
		If (RegExMatch(Text, SearchText) < 1)
			Return Text ; SearchText not in TextFile so return and do nothing, we have to return Text in case of a variable otherwise it would empty the variable contents bug fix 3.3

	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			{
			 LoopField := RegExReplace(A_LoopField, NeedleRegEx, Replacement)
			 OutPut .= LoopField "`n"
			}
		 Else
			OutPut .= A_LoopField "`n"
		}
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

TF_RegExReplace(Text, NeedleRegEx = "", Replacement = "")
	{
	 options:="^[imsxADJUXPS]+\)" ; Hat tip to sinkfaze http://www.autohotkey.com/forum/viewtopic.php?t=60062
	 if RegExMatch(searchText,options,o)
		searchText := RegExReplace(searchText,options,(!InStr(o,"m") ? "m$0" : "$0"))
	 else searchText := "m)" . searchText
	 TF_GetData(OW, Text, FileName)
		If (RegExMatch(Text, SearchText) < 1)
			Return Text ; SearchText not in TextFile so return and do nothing, we have to return Text in case of a variable otherwise it would empty the variable contents bug fix 3.3
	 Text := RegExReplace(Text, NeedleRegEx, Replacement)
	 Return TF_ReturnOutPut(OW, Text, FileName, 0)
	}

TF_RemoveLines(Text, StartLine = 1, EndLine = 0)
	{
	 TF_GetData(OW, Text, FileName)
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			Continue
		 Else
			OutPut .= A_LoopField "`n"
		}
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

TF_RemoveBlankLines(Text, StartLine = 1, EndLine = 0)
	{
	 TF_GetData(OW, Text, FileName)
	 If (RegExMatch(Text, "[\S]+?\r?\n?") < 1)
	 	Return Text ; No empty lines so return and do nothing, we have to return Text in case of a variable otherwise it would empty the variable contents bug fix 3.3
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			OutPut .= (RegExMatch(A_LoopField,"[\S]+?\r?\n?")) ? A_LoopField "`n" :
		 Else
			OutPut .= A_LoopField "`n"
		}
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

TF_RemoveDuplicateLines(Text, StartLine = 1, Endline = 0, Consecutive = 0, CaseSensitive = false)
	{
	 TF_GetData(OW, Text, FileName)
	 If (StartLine = "")
	 	StartLine = 1
	 If (Endline = 0 OR Endline = "")
		EndLine := TF_Count(Text, "`n") + 1
	 Loop, Parse, Text, `n, `r
		{
		 If (A_Index < StartLine)
			Section1 .= A_LoopField "`n"
		 If A_Index between %StartLine% and %Endline%
			{
			 If (Consecutive = 1)
				{
				 If (A_LoopField <> PreviousLine) ; method one for consecutive duplicate lines
					 Section2 .= A_LoopField "`n"
				 PreviousLine:=A_LoopField
				}
			 Else
				{
				 If !(InStr(SearchForSection2,"__bol__" . A_LoopField . "__eol__",CaseSensitive)) ; not found
				 	{
				 	 SearchForSection2 .= "__bol__" A_LoopField "__eol__" ; this makes it unique otherwise it could be a partial match
					 Section2 .= A_LoopField "`n"
				 	}
				}
			}
		 If (A_Index > EndLine)
			Section3 .= A_LoopField "`n"
		}
	 Output .= Section1 Section2 Section3
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

TF_InsertLine(Text, StartLine = 1, Endline = 0, InsertText = "")
	{
	 TF_GetData(OW, Text, FileName)
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			Output .= InsertText "`n" A_LoopField "`n"
		 Else
			Output .= A_LoopField "`n"
		}
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

TF_ReplaceLine(Text, StartLine = 1, Endline = 0, ReplaceText = "")
	{
	 TF_GetData(OW, Text, FileName)
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			Output .= ReplaceText "`n"
		 Else
			Output .= A_LoopField "`n"
		}
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

TF_InsertPrefix(Text, StartLine = 1, EndLine = 0, InsertText = "")
	{
	 TF_GetData(OW, Text, FileName)
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			OutPut .= InsertText A_LoopField "`n"
		 Else
			OutPut .= A_LoopField "`n"
		}
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

TF_InsertSuffix(Text, StartLine = 1, EndLine = 0 , InsertText = "")
	{
	 TF_GetData(OW, Text, FileName)
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			OutPut .= A_LoopField InsertText "`n"
		 Else
			OutPut .= A_LoopField "`n"
		}
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

TF_TrimLeft(Text, StartLine = 1, EndLine = 0, Count = 1)
	{
	 TF_GetData(OW, Text, FileName)
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			{
			 StringTrimLeft, StrOutPut, A_LoopField, %Count%
			 OutPut .= StrOutPut "`n"
			}
		 Else
			OutPut .= A_LoopField "`n"
		}
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

TF_TrimRight(Text, StartLine = 1, EndLine = 0, Count = 1)
	{
	 TF_GetData(OW, Text, FileName)
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			{
			 StringTrimRight, StrOutPut, A_LoopField, %Count%
			 OutPut .= StrOutPut "`n"
			}
		 Else
			OutPut .= A_LoopField "`n"
		}
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

TF_AlignLeft(Text, StartLine = 1, EndLine = 0, Columns = 80, Padding = 0)
	{
	 Trim:=A_AutoTrim ; store trim settings
	 AutoTrim, On ; make sure AutoTrim is on
	 TF_GetData(OW, Text, FileName)
	 If (Endline = 0 OR Endline = "")
		EndLine := TF_Count(Text, "`n") + 1
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			{
			 LoopField = %A_LoopField% ; Make use of AutoTrim, should be faster then a RegExReplace. Trims leading and trailing spaces!
			 SpaceNum := Columns-StrLen(LoopField)-1
			 If (SpaceNum > 0) and (Padding = 1) ; requires padding + keep padding
				{
				 Left:=TF_SetWidth(LoopField,Columns, 0) ; align left
				 OutPut .= Left "`n"
				}
			 Else
				OutPut .= LoopField "`n"
			}
		 Else
		 	OutPut .= A_LoopField "`n"
		}
	 AutoTrim, %Trim%	; restore original Trim
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

TF_AlignCenter(Text, StartLine = 1, EndLine = 0, Columns = 80, Padding = 0)
	{
	 Trim:=A_AutoTrim ; store trim settings
	 AutoTrim, On ; make sure AutoTrim is on
	 TF_GetData(OW, Text, FileName)
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			{
			 LoopField = %A_LoopField% ; Make use of AutoTrim, should be faster then a RegExReplace
			 SpaceNum := (Columns-StrLen(LoopField)-1)/2
			 If (Padding = 1) and (LoopField = "") ; skip empty lines, do not fill with spaces
				{
				 OutPut .= "`n"
				 Continue
				}
			 If (StrLen(LoopField) >= Columns)
				{
				 OutPut .= LoopField "`n" ; add as is
				 Continue
				}
			 Centered:=TF_SetWidth(LoopField,Columns, 1) ; align center using set width
			 OutPut .= Centered "`n"
			}
		 Else
			OutPut .= A_LoopField "`n"
		}
	 AutoTrim, %Trim%	; restore original Trim
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

TF_AlignRight(Text, StartLine = 1, EndLine = 0, Columns = 80, Skip = 0)
	{
	 Trim:=A_AutoTrim ; store trim settings
	 AutoTrim, On ; make sure AutoTrim is on
	 TF_GetData(OW, Text, FileName)
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			{
			 LoopField = %A_LoopField% ; Make use of AutoTrim, should be faster then a RegExReplace
			 If (Skip = 1) and (LoopField = "") ; skip empty lines, do not fill with spaces
				{
				 OutPut .= "`n"
				 Continue
				}
			 If (StrLen(LoopField) >= Columns)
				{
				 OutPut .= LoopField "`n" ; add as is
				 Continue
				}
			 Right:=TF_SetWidth(LoopField,Columns, 2) ; align right using set width
			 OutPut .= Right "`n"
			}
		 Else
			OutPut .= A_LoopField "`n"
		}
	 AutoTrim, %Trim%	; restore original Trim
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

; Based on: CONCATenate text files, ftp://garbo.uwasa.fi/pc/ts/tsfltc22.zip
TF_ConCat(FirstTextFile, SecondTextFile, OutputFile = "", Blanks = 0, FirstPadMargin = 0, SecondPadMargin = 0)
	{
	 If (Blanks > 0)
		Loop, %Blanks%
			InsertBlanks .= A_Space
	 If (FirstPadMargin > 0)
		Loop, %FirstPadMargin%
			PaddingFile1 .= A_Space
	 If (SecondPadMargin > 0)
		Loop, %SecondPadMargin%
			PaddingFile2 .= A_Space
	 Text:=FirstTextFile
	 TF_GetData(OW, Text, FileName)
	 StringSplit, Str1Lines, Text, `n, `r
	 Text:=SecondTextFile
	 TF_GetData(OW, Text, FileName)
	 StringSplit, Str2Lines, Text, `n, `r
	 Text= ; clear mem

	 ; first we need to determine the file with the most lines for our loop
	 If (Str1Lines0 > Str2Lines0)
		MaxLoop:=Str1Lines0
	 Else
		MaxLoop:=Str2Lines0
	 Loop, %MaxLoop%
		{
		 Section1:=Str1Lines%A_Index%
		 Section2:=Str2Lines%A_Index%
		 OutPut .= Section1 PaddingFile1 InsertBlanks Section2 PaddingFile2 "`n"
		 Section1= ; otherwise it will remember the last line from the shortest file or var
		 Section2=
		}
	 OW=1 ; it is probably 0 so in that case it would create _copy, so set it to 1
	 If (OutPutFile = "") ; if OutPutFile is empty return as variable
		OW=2
	 Return TF_ReturnOutPut(OW, OutPut, OutputFile, 1, 1)
	}

TF_LineNumber(Text, Leading = 0, Restart = 0, Char = 0) ; HT ribbet.1
	{
	 global t
	 TF_GetData(OW, Text, FileName)
	 Lines:=TF_Count(Text, "`n") + 1
	 Padding:=StrLen(Lines)
	 If (Leading = 0) and (Char = 0)
		Char := A_Space
	 Loop, %Padding%
		PadLines .= Char
	 Loop, Parse, Text, `n, `r
		{
		 If Restart = 0
			MaxNo = %A_Index%
		 Else
			{
			 MaxNo++
			 If MaxNo > %Restart%
				MaxNo = 1
			}
		 LineNumber:= MaxNo
		 If (Leading = 1)
			{
			 LineNumber := Padlines LineNumber ; add padding
			 StringRight, LineNumber, LineNumber, StrLen(Lines) ; remove excess padding
			}
		 If (Leading = 0)
			{
			 LineNumber := LineNumber Padlines ; add padding
			 StringLeft, LineNumber, LineNumber, StrLen(Lines) ; remove excess padding
			}
		 OutPut .= LineNumber A_Space A_LoopField "`n"
		}
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

; skip = 1, skip shorter lines (e.g. lines shorter startcolumn position)
; modified in TF 3.4, fixed in 3.5
TF_ColGet(Text, StartLine = 1, EndLine = 0, StartColumn = 1, EndColumn = 1, Skip = 0)
	{
	 TF_GetData(OW, Text, FileName)
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 If (StartColumn < 0)
		{
		 StartColumn++
		 Loop, Parse, Text, `n, `r ; parsing file/var
			{
			 If A_Index in %TF_MatchList%
				{
				 output .= SubStr(A_LoopField,StartColumn) "`n"
				}
			 else
				 output .= A_LoopField "`n"
			}
		 Return TF_ReturnOutPut(OW, OutPut, FileName)
		}
	 if RegExMatch(StartColumn, ",|\+|-")
		{
		 StartColumn:=_MakeMatchList(Text, StartColumn, 1, 1)
		 Loop, Parse, Text, `n, `r ; parsing file/var
			{
			 If A_Index in %TF_MatchList%
				{
				 loop, parse, A_LoopField ; parsing LINE char by char
					{
					 If A_Index in %StartColumn% ; if col in index get char
						output .= A_LoopField
					}
				 output .= "`n"
				}
			 else
				 output .= A_LoopField "`n"
			}
		 output .= A_LoopField "`n"
		}
	 else
		{
		 EndColumn:=(EndColumn+1)-StartColumn
		 Loop, Parse, Text, `n, `r
			{
			 If A_Index in %TF_MatchList%
				{
				 StringMid, Section, A_LoopField, StartColumn, EndColumn
				 If (Skip = 1) and (StrLen(A_LoopField) < StartColumn)
					Continue
				 OutPut .= Section "`n"
				}
			}
		}
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

; Based on: COLPUT.EXE & CUT.EXE, ftp://garbo.uwasa.fi/pc/ts/tsfltc22.zip
; modified in TF 3.4
TF_ColPut(Text, Startline = 1, EndLine = 0, StartColumn = 1, InsertText = "", Skip = 0)
	{
	 TF_GetData(OW, Text, FileName)
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 If RegExMatch(StartColumn, ",|\+")
		{
		 StartColumn:=_MakeMatchList(Text, StartColumn, 0, 1)
		 Loop, Parse, Text, `n, `r ; parsing file/var
			{
			 If A_Index in %TF_MatchList%
				{
				 loop, parse, A_LoopField ; parsing LINE char by char
					{
					 If A_Index in %StartColumn% ; if col in index insert text
						output .= InsertText A_LoopField
					 Else
						output .= A_LoopField
					}
				 output .= "`n"
				}
			 else
				 output .= A_LoopField "`n"
			}
		 output .= A_LoopField "`n"
		}
	 else
		{
		 StartColumn--
		 Loop, Parse, Text, `n, `r
			{
			 If A_Index in %TF_MatchList%
				{
				 If (StartColumn > 0)
					{
					 StringLeft, Section1, A_LoopField, StartColumn
					 StringMid, Section2, A_LoopField, StartColumn+1
					 If (Skip = 1) and (StrLen(A_LoopField) < StartColumn)
						OutPut .= Section1 Section2 "`n"
					}
				 Else
					{
					 Section1:=SubStr(A_LoopField, 1, StrLen(A_LoopField) + StartColumn + 1)
					 Section2:=SubStr(A_LoopField, StrLen(A_LoopField) + StartColumn + 2)
					 If (Skip = 1) and (A_LoopField = "")
						OutPut .= Section1 Section2 "`n"
					}
				 OutPut .= Section1 InsertText Section2 "`n"
				}
			 Else
				OutPut .= A_LoopField "`n"
			}
		}
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

; modified TF 3.4
TF_ColCut(Text, StartLine = 1, EndLine = 0, StartColumn = 1, EndColumn = 1)
	{
	 TF_GetData(OW, Text, FileName)
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 If RegExMatch(StartColumn, ",|\+|-")
		{
		 StartColumn:=_MakeMatchList(Text, StartColumn, EndColumn, 1)
		 Loop, Parse, Text, `n, `r ; parsing file/var
			{
			 If A_Index in %TF_MatchList%
				{
				 loop, parse, A_LoopField ; parsing LINE char by char
					{
					 If A_Index not in %StartColumn% ; if col not in index get char
						output .= A_LoopField
					}
				 output .= "`n"
				}
			 else
				 output .= A_LoopField "`n"
			}
		 output .= A_LoopField "`n"
		}
	 else
		{
		 StartColumn--
		 EndColumn++
		 Loop, Parse, Text, `n, `r
			{
			 If A_Index in %TF_MatchList%
				{
				 StringLeft, Section1, A_LoopField, StartColumn
				 StringMid, Section2, A_LoopField, EndColumn
				 OutPut .= Section1 Section2 "`n"
				}
			 Else
				OutPut .= A_LoopField "`n"
			}
		}
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

TF_ReverseLines(Text, StartLine = 1, EndLine = 0)
	{
	 TF_GetData(OW, Text, FileName)
	 StringSplit, Line, Text, `n, `r ; line0 is number of lines
	 If (EndLine = 0 OR EndLine = "")
		EndLine:=Line0
	 If (EndLine > Line0)
		EndLine:=Line0
	 CountDown:=EndLine+1
	 Loop, Parse, Text, `n, `r
		{
		 If (A_Index < StartLine)
			Output1 .= A_LoopField "`n" ; section1
		 If A_Index between %StartLine% and %Endline%
			{
			 CountDown--
			 Output2 .= Line%CountDown% "`n" section2
			}
		 If (A_Index > EndLine)
			 Output3 .= A_LoopField "`n"
		}
	 OutPut.= Output1 Output2 Output3
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

;TF_SplitFileByLines
;example:
;TF_SplitFileByLines("TestFile.txt", "4", "sfile_", "txt", "1") ; split file every 3 lines
; InFile = 0 skip line e.g. do not include the actual line in any of the output files
; InFile = 1 include line IN current file
; InFile = 2 include line IN next file
TF_SplitFileByLines(Text, SplitAt, Prefix = "file", Extension = "txt", InFile = 1)
	{
	 LineCounter=1
	 FileCounter=1
	 Where:=SplitAt
	 Method=1
	 ; 1 = default, splitat every X lines,
	 ; 2 = splitat: - rotating if applicable
	 ; 3 = splitat: specific lines comma separated
	 TF_GetData(OW, Text, FileName)

	 IfInString, SplitAt, `- ; method 2
		{
		 StringSplit, Split, SplitAt, `-
		 Part=1
		 Where:=Split%Part%
		 Method=2
		}
	 IfInString, SplitAt, `, ; method 3
		{
		 StringSplit, Split, SplitAt, `,
		 Part=1
		 Where:=Split%Part%
		 Method=3
		}
	 Loop, Parse, Text, `n, `r
		{
		 OutPut .= A_LoopField "`n"
		 If (LineCounter = Where)
			{
			 If (InFile = 0)
				{
				 StringReplace, CheckOutput, PreviousOutput, `n, , All
				 StringReplace, CheckOutput, CheckOutput, `r, , All
				 If (CheckOutput <> "") and (OW <> 2) ; skip empty files
					TF_ReturnOutPut(1, PreviousOutput, Prefix FileCounter "." Extension, 0, 1)
				 If (CheckOutput <> "") and (OW = 2) ; skip empty files
					 TF_SetGlobal(Prefix FileCounter,PreviousOutput)
				 Output:=
				}
			 If (InFile = 1)
				{
				 StringReplace, CheckOutput, Output, `n, , All
				 StringReplace, CheckOutput, CheckOutput, `r, , All
				 If (CheckOutput <> "") and (OW <> 2) ; skip empty files
				 	 TF_ReturnOutPut(1, Output, Prefix FileCounter "." Extension, 0, 1)
				 If (CheckOutput <> "") and (OW = 2) ; skip empty files
				 	 TF_SetGlobal(Prefix FileCounter,Output)
				 Output:=
				}
			 If (InFile = 2)
				{
				 OutPut := PreviousOutput
				 StringReplace, CheckOutput, Output, `n, , All
				 StringReplace, CheckOutput, CheckOutput, `r, , All
				 If (CheckOutput <> "") and (OW <> 2) ; skip empty files
					 TF_ReturnOutPut(1, Output, Prefix FileCounter "." Extension, 0, 1)
				 If (CheckOutput <> "") and (OW = 2) ; output to array
				 	 TF_SetGlobal(Prefix FileCounter,Output)
				 OutPut := A_LoopField "`n"
				}
			 If (Method <> 3)
				 LineCounter=0 ; reset
			 FileCounter++ ; next file
			 Part++
			 If (Method = 2) ; 2 = splitat: - rotating if applicable
			 	{
			 If (Part > Split0)
					{
					 Part=1
					}
				 Where:=Split%Part%
				}
			 If (Method = 3) ; 3 = splitat: specific lines comma separated
				{
				 If (Part > Split0)
					Where:=Split%Split0%
				 Else
					Where:=Split%Part%
				}
			}
		 LineCounter++
		 PreviousOutput:=Output
		 PreviousLine:=A_LoopField
		}
	 StringReplace, CheckOutput, Output, `n, , All
	 StringReplace, CheckOutput, CheckOutput, `r, , All
	 If (CheckOutPut <> "") and (OW <> 2) ; skip empty files
		TF_ReturnOutPut(1, Output, Prefix FileCounter "." Extension, 0, 1)
	 If (CheckOutput <> "") and (OW = 2) ; output to array
		{
		 TF_SetGlobal(Prefix FileCounter,Output)
		 TF_SetGlobal(Prefix . "0" , FileCounter)
		}
	}

; TF_SplitFileByText("TestFile.txt", "button", "sfile_", "txt") ; split file at every line with button in it, can be regexp
; InFile = 0 skip line e.g. do not include the actual line in any of the output files
; InFile = 1 include line IN current file
; InFile = 2 include line IN next file
TF_SplitFileByText(Text, SplitAt, Prefix = "file", Extension = "txt", InFile = 1)
	{
	 LineCounter=1
	 FileCounter=1
	 TF_GetData(OW, Text, FileName)
	 SplitPath, TextFile,, Dir
	 Loop, Parse, Text, `n, `r
		{
		 OutPut .= A_LoopField "`n"
		 FoundPos:=RegExMatch(A_LoopField, SplitAt)
		 If (FoundPos > 0)
			{
			 If (InFile = 0)
				{
				 StringReplace, CheckOutput, PreviousOutput, `n, , All
				 StringReplace, CheckOutput, CheckOutput, `r, , All
				 If (CheckOutput <> "") and (OW <> 2) ; skip empty files
					TF_ReturnOutPut(1, PreviousOutput, Prefix FileCounter "." Extension, 0, 1)
				 If (CheckOutput <> "") and (OW = 2) ; output to array
					TF_SetGlobal(Prefix FileCounter,PreviousOutput)
				 Output:=
				}
			 If (InFile = 1)
				{
				 StringReplace, CheckOutput, Output, `n, , All
				 StringReplace, CheckOutput, CheckOutput, `r, , All
				 If (CheckOutput <> "") and (OW <> 2) ; skip empty files
					TF_ReturnOutPut(1, Output, Prefix FileCounter "." Extension, 0, 1)
				 If (CheckOutput <> "") and (OW = 2) ; output to array
					TF_SetGlobal(Prefix FileCounter,Output)
				 Output:=
				}
			 If (InFile = 2)
				{
				 OutPut := PreviousOutput
				 StringReplace, CheckOutput, Output, `n, , All
				 StringReplace, CheckOutput, CheckOutput, `r, , All
				 If (CheckOutput <> "") and (OW <> 2) ; skip empty files
					TF_ReturnOutPut(1, Output, Prefix FileCounter "." Extension, 0, 1)
				 If (CheckOutput <> "") and (OW = 2) ; output to array
					TF_SetGlobal(Prefix FileCounter,Output)
				 OutPut := A_LoopField "`n"
				}
			 LineCounter=0 ; reset
			 FileCounter++ ; next file
			}
		 LineCounter++
		 PreviousOutput:=Output
		 PreviousLine:=A_LoopField
		}
	 StringReplace, CheckOutput, Output, `n, , All
	 StringReplace, CheckOutput, CheckOutput, `r, , All
	 If (CheckOutPut <> "") and (OW <> 2) ; skip empty files
		TF_ReturnOutPut(1, Output, Prefix FileCounter "." Extension, 0, 1)
	 If (CheckOutput <> "") and (OW = 2) ; output to array
		{
		 TF_SetGlobal(Prefix FileCounter,Output)
		 TF_SetGlobal(Prefix . "0" , FileCounter)
		}
	}

TF_Find(Text, StartLine = 1, EndLine = 0, SearchText = "", ReturnFirst = 1, ReturnText = 0)
	{
	 options:="^[imsxADJUXPS]+\)"
	 if RegExMatch(searchText,options,o)
		searchText:=RegExReplace(searchText,options,(!InStr(o,"m") ? "m$0(*ANYCRLF)" : "$0"))
	 else searchText:="m)(*ANYCRLF)" searchText
	 options:="^[imsxADJUXPS]+\)" ; Hat tip to sinkfaze, see http://www.autohotkey.com/forum/viewtopic.php?t=60062
	 if RegExMatch(searchText,options,o)
		searchText := RegExReplace(searchText,options,(!InStr(o,"m") ? "m$0" : "$0"))
	 else searchText := "m)" . searchText

	 TF_GetData(OW, Text, FileName)
	 If (RegExMatch(Text, SearchText) < 1)
		Return "0" ; SearchText not in file or error, so do nothing

	 TF_GetData(OW, Text, FileName)
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			{
			 If (RegExMatch(A_LoopField, SearchText) > 0)
				{
				 If (ReturnText = 0)
					Lines .= A_Index "," ; line number
				 Else If (ReturnText = 1)
					Lines .= A_LoopField "`n" ; text of line
				 Else If (ReturnText = 2)
					Lines .= A_Index ": " A_LoopField "`n" ; add line number
				 If (ReturnFirst = 1) ; only return first occurrence
					Break
				}
			}
		}
	 If (Lines <> "")
		StringTrimRight, Lines, Lines, 1 ; trim trailing , or `n
	 Else
		Lines = 0 ; make sure we return 0
	 Return Lines
	}

TF_Prepend(File1, File2)
	{
FileList=
(
%File1%
%File2%
)
TF_Merge(FileList,"`n", "!" . File2)
Return
	}

TF_Append(File1, File2)
	{
FileList=
(
%File2%
%File1%
)
TF_Merge(FileList,"`n", "!" . File2)
Return
	}

; For TF_Merge You will need to create a Filelist variable, one file per line,
; to pass on to the function:
; FileList=
; (
; c:\file1.txt
; c:\file2.txt
; )
; use Loop (files & folders) to create one quickly if you want to merge all TXT files for example
;
; Loop, c:\*.txt
;   FileList .= A_LoopFileFullPath "`n"
;
; By default, a new line is used as a separator between two text files
; !merged.txt deletes target file before starting to merge files
TF_Merge(FileList, Separator = "`n", FileName = "merged.txt")
	{
	 OW=0
	 Loop, Parse, FileList, `n, `r
		{
		 Append2File= ; Just make sure it is empty
		 IfExist, %A_LoopField%
			{
			 FileRead, Append2File, %A_LoopField%
			 If not ErrorLevel ; Successfully loaded
				Output .= Append2File Separator
			}
		}

	 If (SubStr(FileName,1,1)="!") ; check if we want to delete the target file before we start
		{
		 FileName:=SubStr(FileName,2)
		 OW=1
		}
	 Return TF_ReturnOutPut(OW, OutPut, FileName, 0, 1)
	}

TF_Wrap(Text, Columns = 80, AllowBreak = 0, StartLine = 1, EndLine = 0)
	{
	 TF_GetData(OW, Text, FileName)
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 If (AllowBreak = 1)
		Break=
	 Else
		Break=[ \r?\n]
	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			{
			 If (StrLen(A_LoopField) > Columns)
				{
				 LoopField := A_LoopField " " ; just seems to work better by adding a space
				 OutPut .= RegExReplace(LoopField, "(.{1," . Columns . "})" . Break , "$1`n")
				}
			 Else
				OutPut .= A_LoopField "`n"
			}
		 Else
			 OutPut .= A_LoopField "`n"
		}
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

TF_WhiteSpace(Text, RemoveLeading = 1, RemoveTrailing = 1, StartLine = 1, EndLine = 0) {
	 TF_GetData(OW, Text, FileName)
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc) ; create MatchList
	 Trim:=A_AutoTrim ; store trim settings
	 AutoTrim, On ; make sure AutoTrim is on
	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			{
			 If (RemoveLeading = 1) AND (RemoveTrailing = 1)
				{
				 LoopField = %A_LoopField%
				 Output .= LoopField "`n"
					 Continue
				}
			 If (RemoveLeading = 1) AND (RemoveTrailing = 0)
				{
				 LoopField := A_LoopField . "."
				 LoopField = %LoopField%
				 StringTrimRight, LoopField, LoopField, 1
				 Output .= LoopField "`n"
					 Continue
				}
			 If (RemoveLeading = 0) AND (RemoveTrailing = 1)
				{
				 LoopField := "." A_LoopField
				 LoopField = %LoopField%
				 StringTrimLeft, LoopField, LoopField, 1
				 Output .= LoopField "`n"
					 Continue
				}
			 If (RemoveLeading = 0) AND (RemoveTrailing = 0)
				{
				 Output .= A_LoopField "`n"
					 Continue
				}
			}
		 Else
			Output .= A_LoopField "`n"
		}
	AutoTrim, %Trim%	; restore original Trim
	Return TF_ReturnOutPut(OW, OutPut, FileName)
}

; Delete lines from file1 in file2 (using StringReplace)
; Partialmatch = 2 added in 3.4
TF_Substract(File1, File2, PartialMatch = 0) {
	Text:=File1
	TF_GetData(OW, Text, FileName)
	Str1:=Text
	Text:=File2
	TF_GetData(OW, Text, FileName)
		OutPut:=Text
	If (OW = 2)
		File1= ; free mem in case of var/text
	OutPut .= "`n" ; just to make sure the StringReplace will work

	If (PartialMatch = 2)
		{
		 Loop, Parse, Str1, `n, `r
			{
			 IfInString, Output, %A_LoopField%
				{
				 Output:= RegExReplace(Output, "im)^.*" . A_LoopField . ".*\r?\n?", replace)
				}
			}
		}
	Else If (PartialMatch = 1) ; allow paRTIal match
		{
		 Loop, Parse, Str1, `n, `r
			StringReplace, Output, Output, %A_LoopField%, , All ; remove lines from file1 in file2
		}
	Else If (PartialMatch = 0)
		{
		 search:="m)^(.*)$"
		 replace=__bol__$1__eol__
		 Output:=RegExReplace(Output, search, replace)
		 StringReplace, Output, Output, `n__eol__,__eol__ , All ; strange fix but seems to be needed.
		 Loop, Parse, Str1, `n, `r
			StringReplace, Output, Output, __bol__%A_LoopField%__eol__, , All ; remove lines from file1 in file2
		}
	If (PartialMatch = 0)
		{
		 StringReplace, Output, Output, __bol__, , All
		 StringReplace, Output, Output, __eol__, , All
		}

	; Remove all blank lines from the text in a variable:
	Loop
		{
		 StringReplace, Output, Output, `r`n`r`n, `r`n, UseErrorLevel
		 if (ErrorLevel = 0) or (ErrorLevel = 1) ; No more replacements needed.
			break
		}
	Return TF_ReturnOutPut(OW, OutPut, FileName, 0)
}

; Similar to "BK Replace EM" RangeReplace
TF_RangeReplace(Text, SearchTextBegin, SearchTextEnd, ReplaceText = "", CaseSensitive = "False", KeepBegin = 0, KeepEnd = 0)
	{
	 TF_GetData(OW, Text, FileName)
	 IfNotInString, Text, %SearchText%
		Return Text ; SearchTextBegin not in TextFile so return and do nothing, we have to return Text in case of a variable otherwise it would empty the variable contents bug fix 3.3
	 Start = 0
	 End = 0
	 If (KeepBegin = 1)
		 KeepBegin:=SearchTextBegin
	 Else
		 KeepBegin=
	 If (KeepEnd = 1)
		 KeepEnd:= SearchTextEnd
	 Else
		 KeepEnd=
	 If (SearchTextBegin = "")
		 Start=1
	 If (SearchTextEnd = "")
		 End=2

	 Loop, Parse, Text, `n, `r
		{
		 If (End = 1) ; end has been found already, replacement made simply continue to add all lines
			{
			 Output .= A_LoopField "`n"
				 Continue
			}
		 If (Start = 0) ; start hasn't been found
			{
			 If (InStr(A_LoopField,SearchTextBegin,CaseSensitive)) ; start has been found
				{
				 Start = 1
				 KeepSection := SubStr(A_LoopField, 1, InStr(A_LoopField, SearchTextBegin)-1)
				 EndSection := SubStr(A_LoopField, InStr(A_LoopField, SearchTextBegin)-1)
				 ; check if SearchEndText is in second part of line
				 If (InStr(EndSection,SearchTextEnd,CaseSensitive)) ; end found
					{
					 EndSection := ReplaceText KeepEnd SubStr(EndSection, InStr(EndSection, SearchTextEnd) + StrLen(SearchTextEnd) ) "`n"
					 If (End <> 2)
						End=1
					 If (End = 2)
					 	EndSection=
					}
				 Else
					EndSection=
				 Output .= KeepSection KeepBegin EndSection
				 Continue
				}
			 Else
				Output .= A_LoopField "`n" ; if not found yet simply add
				}
		 If (Start = 1) and (End <> 2) ; start has been found, now look for end if end isn't an empty string
			{
			 If (InStr(A_LoopField,SearchTextEnd,CaseSensitive)) ; end found
				{
				 End = 1
				 Output .= ReplaceText KeepEnd SubStr(A_LoopField, InStr(A_LoopField, SearchTextEnd) + StrLen(SearchTextEnd) ) "`n"
				}
			}
		}
	 If (End = 2)
		Output .= ReplaceText
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

; Create file of X lines and Y columns, fill with space or other character(s)
TF_MakeFile(Text, Lines = 1, Columns = 1, Fill = " ")
	{
	 OW=1
	 If (Text = "") ; if OutPutFile is empty return as variable
		OW=2
	 Loop, % Columns
		Cols .= Fill
	 Loop, % Lines
		Output .= Cols "`n"
	 Return TF_ReturnOutPut(OW, OutPut, Text, 1, 1)
	}

; Convert tabs to spaces, shorthand for TF_ReplaceInLines
TF_Tab2Spaces(Text, TabStop = 4, StartLine = 1, EndLine =0)
	{
	 Loop, % TabStop
		Replace .= A_Space
	 Return TF_ReplaceInLines(Text, StartLine, EndLine, A_Tab, Replace)
	}

; Convert spaces to tabs, shorthand for TF_ReplaceInLines
TF_Spaces2Tab(Text, TabStop = 4, StartLine = 1, EndLine =0)
	{
	 Loop, % TabStop
		Replace .= A_Space
	 Return TF_ReplaceInLines(Text, StartLine, EndLine, Replace, A_Tab)
	}

; Sort (section of) a text file
TF_Sort(Text, SortOptions = "", StartLine = 1, EndLine = 0) ; use the SORT options http://www.autohotkey.com/docs/commands/Sort.htm
	{
	 TF_GetData(OW, Text, FileName)
	 If StartLine contains -,+,`, ; no sections, incremental or multiple line input
		Return
	 If (StartLine = 1) and (Endline = 0) ; process entire file
		{
		 Output:=Text
		 Sort, Output, %SortOptions%
		}
	 Else
		{
		 Output := TF_ReadLines(Text, 1, StartLine-1) ; get first section
		 ToSort := TF_ReadLines(Text, StartLine, EndLine) ; get section to sort
		 Sort, ToSort, %SortOptions%
		 OutPut .= ToSort
		 OutPut .= TF_ReadLines(Text, EndLine+1) ; append last section
		}
	 Return TF_ReturnOutPut(OW, OutPut, FileName, 0) ; https://github.com/hi5/TF/issues/11
	}

TF_Tail(Text, Lines = 1, RemoveTrailing = 0, ReturnEmpty = 1)
	{
	 TF_GetData(OW, Text, FileName)
	 Neg = 0
	 If (Lines < 0)
		{
		 Neg=1
		 Lines:= Lines * -1
		}
	 If (ReturnEmpty = 0) ; remove blank lines first so we can't return any blank lines anyway
		{
		 Loop, Parse, Text, `n, `r
			OutPut .= (RegExMatch(A_LoopField,"[\S]+?\r?\n?")) ? A_LoopField "`n" :
		 StringTrimRight, OutPut, OutPut, 1 ; remove trailing `n added by loop above
		 Text:=OutPut
		 OutPut=
	}
	 If (Neg = 1) ; get only one line!
		{
		 Lines++
		 Output:=Text
		 StringGetPos, Pos, Output, `n, R%Lines% ; These next two Lines by Tuncay see
		 StringTrimLeft, Output, Output, % ++Pos ; http://www.autoHotkey.com/forum/viewtopic.php?p=262375#262375
		 StringGetPos, Pos, Output, `n
		 StringLeft, Output, Output, % Pos
		 Output .= "`n"
		}
	 Else
		{
		 Output:=Text
		 StringGetPos, Pos, Output, `n, R%Lines% ; These next two Lines by Tuncay see
		 StringTrimLeft, Output, Output, % ++Pos ; http://www.autoHotkey.com/forum/viewtopic.php?p=262375#262375
		 Output .= "`n"
		}
	 OW = 2 ; make sure we return variable not process file
	 Return TF_ReturnOutPut(OW, OutPut, FileName, RemoveTrailing)
	}

TF_Count(String, Char)
	{
	StringReplace, String, String, %Char%,, UseErrorLevel
	Return ErrorLevel
	}

TF_Save(Text, FileName, OverWrite = 1) { ; HugoV write file
	Return TF_ReturnOutPut(OverWrite, Text, FileName, 0, 1)
	}

TF(TextFile, CreateGlobalVar = "T") { ; read contents of file in output and %output% as global var ...  http://www.autohotkey.com/forum/viewtopic.php?p=313120#313120
	 global
	 FileRead, %CreateGlobalVar%, %TextFile%
	 Return, (%CreateGlobalVar%)
	}

; TF_Join
; SmartJoin: Detect if CHAR(s) is/are already present at the end of the line before joining the next, this to prevent unnecessary double spaces for example.
; Char: character(s) to use between new lines, defaults to a space. To use nothing use ""
TF_Join(Text, StartLine = 1, EndLine = 0, SmartJoin = 0, Char = 0)
	{
	 If ( (InStr(StartLine,",") > 0) AND (InStr(StartLine,"-") = 0) ) OR (InStr(StartLine,"+") > 0)
		Return Text ; can't do multiplelines, only multiple sections of lines e.g. "1,5" bad "1-5,15-10" good, "2+2" also bad
	 TF_GetData(OW, Text, FileName)
	 If (InStr(Text,"`n") = 0)
		Return Text ; there are no lines to join so just return Text
	 If (InStr(StartLine,"-") > 0)	; OK, we need some counter-intuitive string mashing to substract ONE from the "endline" parameter
		{
		 Loop, Parse, StartLine, CSV
			{
			 StringSplit, part, A_LoopField, -
			 NewStartLine .= part1 "-" (part2-1) ","
			}
		 StringTrimRight, StartLine, NewStartLine, 1
		}
	 If (Endline > 0)
		Endline--
	 TF_MatchList:=_MakeMatchList(Text, StartLine, EndLine, 0, A_ThisFunc)
	 If (Char = 0)
		Char:=A_Space
	 Char_Org:=Char
	 GetRightLen:=StrLen(Char)-1
	 Loop, Parse, Text, `n, `r
		{
		 If A_Index in %TF_MatchList%
			{
			 If (SmartJoin = 1)
				{
				 GetRightText:=SubStr(A_LoopField,0)
				 If (GetRightText = Char)
					Char=
				}
			 Output .= A_LoopField Char
			 Char:=Char_Org
			}
		 Else
			Output .= A_LoopField "`n"
		}
	 Return TF_ReturnOutPut(OW, OutPut, FileName)
	}

;----- Helper functions ----------------

TF_SetGlobal(var, content = "") ; helper function for TF_Split* to return array and not files, credits Tuncay :-)
	{
	 global
	 %var% := content
	}

; Helper function to determine if VAR/TEXT or FILE is passed to TF
; Update 11 January 2010 (skip filecheck if `n in Text -> can't be file)
TF_GetData(byref OW, byref Text, byref FileName)
	{
	 If (text = 0 "") ; v3.6 -> v3.7 https://github.com/hi5/TF/issues/4 and https://autohotkey.com/boards/viewtopic.php?p=142166#p142166 in case user passes on zero/zeros ("0000") as text - will error out when passing on one 0 and there is no file with that name
		{
		 IfNotExist, %Text% ; additional check to see if a file 0 exists
			{
			 MsgBox, 48, TF Lib Error, % "Read Error - possible reasons (see documentation):`n- Perhaps you used !""file.txt"" vs ""!file.txt""`n- A single zero (0) was passed on to a TF function as text"
			 ExitApp
			}
		}
	 OW=0 ; default setting: asume it is a file and create file_copy
	 IfNotInString, Text, `n ; it can be a file as the Text doesn't contact a newline character
		{
		 If (SubStr(Text,1,1)="!") ; first we check for "overwrite"
			{
			 Text:=SubStr(Text,2)
			 OW=1 ; overwrite file (if it is a file)
			}
		 IfNotExist, %Text% ; now we can check if the file exists, it doesn't so it is a var
			{
			 If (OW=1) ; the variable started with a ! so we need to put it back because it is variable/text not a file
				Text:= "!" . Text
			 OW=2 ; no file, so it is a var or Text passed on directly to TF
			}
		}
	 Else ; there is a newline character in Text so it has to be a variable
		{
		 OW=2
		}
	 If (OW = 0) or (OW = 1) ; it is a file, so we have to read into var Text
		{
		 Text := (SubStr(Text,1,1)="!") ? (SubStr(Text,2)) : Text
		 FileName=%Text% ; Store FileName
		 FileRead, Text, %Text% ; Read file and return as var Text
		 If (ErrorLevel > 0)
			{
			 MsgBox, 48, TF Lib Error, % "Can not read " FileName
			 ExitApp
			}
		}
	 Return
	}

; Skan - http://www.autohotkey.com/forum/viewtopic.php?p=45880#45880
; SetWidth() : SetWidth increases a String's length by adding spaces to it and aligns it Left/Center/Right. ( Requires Space() )
TF_SetWidth(Text,Width,AlignText)
	{
	 If (AlignText!=0 and AlignText!=1 and AlignText!=2)
		AlignText=0
	 If AlignText=0
		{
		 RetStr= % (Text)TF_Space(Width)
		 StringLeft, RetText, RetText, %Width%
		}
	 If AlignText=1
		{
		 Spaces:=(Width-(StrLen(Text)))
		 RetStr= % TF_Space(Round(Spaces/2))(Text)TF_Space(Spaces-(Round(Spaces/2)))
		}
	 If AlignText=2
		{
		 RetStr= % TF_Space(Width)(Text)
		 StringRight, RetStr, RetStr, %Width%
		}
	 Return RetStr
	}

; Skan - http://www.autohotkey.com/forum/viewtopic.php?p=45880#45880
TF_Space(Width)
	{
	 Loop,%Width%
		Space=% Space Chr(32)
	 Return Space
	}

; Write to file or return variable depending on input
TF_ReturnOutPut(OW, Text, FileName, TrimTrailing = 1, CreateNewFile = 0) {
	If (OW = 0) ; input was file, file_copy will be created, if it already exist file_copy will be overwritten
		{
		 IfNotExist, % FileName ; check if file Exist, if not return otherwise it would create an empty file. Thanks for the idea Murp|e
			{
			 If (CreateNewFile = 1) ; CreateNewFile used for TF_SplitFileBy* and others
				{
				 OW = 1
				 Goto CreateNewFile
				}
			 Else
				Return
			}
		 If (TrimTrailing = 1)
			 StringTrimRight, Text, Text, 1 ; remove trailing `n
		 SplitPath, FileName,, Dir, Ext, Name
		 If (Dir = "") ; if Dir is empty Text & script are in same directory
			Dir := A_WorkingDir
		 IfExist, % Dir "\backup" ; if there is a backup dir, copy original file there
			FileCopy, % Dir "\" Name "_copy." Ext, % Dir "\backup\" Name "_copy.bak", 1
		 FileDelete, % Dir "\" Name "_copy." Ext
		 FileAppend, %Text%, % Dir "\" Name "_copy." Ext
		 Return Errorlevel ? False : True
		}
	 CreateNewFile:
	 If (OW = 1) ; input was file, will be overwritten by output
		{
		 IfNotExist, % FileName ; check if file Exist, if not return otherwise it would create an empty file. Thanks for the idea Murp|e
			{
			If (CreateNewFile = 0) ; CreateNewFile used for TF_SplitFileBy* and others
				Return
			}
		 If (TrimTrailing = 1)
			 StringTrimRight, Text, Text, 1 ; remove trailing `n
		 SplitPath, FileName,, Dir, Ext, Name
		 If (Dir = "") ; if Dir is empty Text & script are in same directory
			Dir := A_WorkingDir
		 IfExist, % Dir "\backup" ; if there is a backup dir, copy original file there
			FileCopy, % Dir "\" Name "." Ext, % Dir "\backup\" Name ".bak", 1
		 FileDelete, % Dir "\" Name "." Ext
		 FileAppend, %Text%, % Dir "\" Name "." Ext
		 Return Errorlevel ? False : True
		}
	If (OW = 2) ; input was var, return variable
		{
		 If (TrimTrailing = 1)
			StringTrimRight, Text, Text, 1 ; remove trailing `n
		 Return Text
		}
	}

; _MakeMatchList()
; Purpose:
; Make a MatchList which is used in various functions
; Using a MatchList gives greater flexibility so you can process multiple
; sections of lines in one go avoiding repetitive fileread/append actions
; For TF 3.4 added COL = 0/1 option (for TF_Col* functions) and CallFunc for
; all TF_* functions to facilitate bug tracking
_MakeMatchList(Text, Start = 1, End = 0, Col = 0, CallFunc = "Not available")
	{
	 ErrorList=
	 (join|
Error 01: Invalid StartLine parameter (non numerical character)`nFunction used: %CallFunc%
Error 02: Invalid EndLine parameter (non numerical character)`nFunction used: %CallFunc%
Error 03: Invalid StartLine parameter (only one + allowed)`nFunction used: %CallFunc%
	 )
	 StringSplit, ErrorMessage, ErrorList, |
	 Error = 0

	 If (Col = 1)
		{
		 LongestLine:=TF_Stat(Text)
		 If (End > LongestLine) or (End = 1) ; FIXITHERE BUG
			End:=LongestLine
		}

	 TF_MatchList= ; just to be sure
	 If (Start = 0 or Start = "")
		Start = 1

	 ; some basic error checking

	 ; error: only digits - and + allowed
	 If (RegExReplace(Start, "[ 0-9+\-\,]", "") <> "")
		 Error = 1

	 If (RegExReplace(End, "[0-9 ]", "") <> "")
		 Error = 2

	 ; error: only one + allowed
	 If (TF_Count(Start,"+") > 1)
		 Error = 3

	 If (Error > 0 )
		{
		 MsgBox, 48, TF Lib Error, % ErrorMessage%Error%
		 ExitApp
		}

	 ; Option #0 [ added 30-Oct-2010 ]
	 ; Startline has negative value so process X last lines of file
	 ; endline parameter ignored

	 If (Start < 0) ; remove last X lines from file, endline parameter ignored
		{
		 Start:=TF_CountLines(Text) + Start + 1
		 End=0 ; now continue
		}

	 ; Option #1
	 ; StartLine has + character indicating startline + incremental processing.
	 ; EndLine will be used
	 ; Make TF_MatchList

	 IfInString, Start, `+
		{
		 If (End = 0 or End = "") ; determine number of lines
			End:= TF_Count(Text, "`n") + 1
		 StringSplit, Section, Start, `, ; we need to create a new "TF_MatchList" so we split by ,
		 Loop, %Section0%
			{
			 StringSplit, SectionLines, Section%A_Index%, `+
			 LoopSection:=End + 1 - SectionLines1
			 Counter=0
			 	 TF_MatchList .= SectionLines1 ","
			 Loop, %LoopSection%
				{
				 If (A_Index >= End) ;
					Break
				 If (Counter = (SectionLines2-1)) ; counter is smaller than the incremental value so skip
					{
					 TF_MatchList .= (SectionLines1 + A_Index) ","
					 Counter=0
					}
				 Else
					Counter++
				}
			}
		 StringTrimRight, TF_MatchList, TF_MatchList, 1 ; remove trailing ,
		 Return TF_MatchList
		}

	 ; Option #2
	 ; StartLine has - character indicating from-to, COULD be multiple sections.
	 ; EndLine will be ignored
	 ; Make TF_MatchList

	 IfInString, Start, `-
		{
		 StringSplit, Section, Start, `, ; we need to create a new "TF_MatchList" so we split by ,
		 Loop, %Section0%
			{
			 StringSplit, SectionLines, Section%A_Index%, `-
			 LoopSection:=SectionLines2 + 1 - SectionLines1
			 Loop, %LoopSection%
				{
				 TF_MatchList .= (SectionLines1 - 1 + A_Index) ","
				}
			}
		 StringTrimRight, TF_MatchList, TF_MatchList, 1 ; remove trailing ,
		 Return TF_MatchList
		}

	 ; Option #3
	 ; StartLine has comma indicating multiple lines.
	 ; EndLine will be ignored

	 IfInString, Start, `,
		{
		 TF_MatchList:=Start
		 Return TF_MatchList
		}

	 ; Option #4
	 ; parameters passed on as StartLine, EndLine.
	 ; Make TF_MatchList from StartLine to EndLine

	 If (End = 0 or End = "") ; determine number of lines
			End:= TF_Count(Text, "`n") + 1
	 LoopTimes:=End-Start
	 Loop, %LoopTimes%
		{
		 TF_MatchList .= (Start - 1 + A_Index) ","
		}
	 TF_MatchList .= End ","
	 StringTrimRight, TF_MatchList, TF_MatchList, 1 ; remove trailing ,
	 Return TF_MatchList
	}

; added for TF 3.4 col functions - currently only gets longest line may change in future
TF_Stat(Text)
	{
	 TF_GetData(OW, Text, FileName)
	 Sort, Text, f _AscendingLinesL
	 Pos:=InStr(Text,"`n")-1
	 Return pos
	}

_AscendingLinesL(a1, a2) ; used by TF_Stat
	{
	 Return StrLen(a2) - StrLen(a1)
	}

/* -------------- */
Download .txt
gitextract_bq5uzlh1/

├── license.txt
├── readme.md
└── tf.ahk
Condensed preview — 3 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (121K chars).
[
  {
    "path": "license.txt",
    "chars": 15177,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Fr"
  },
  {
    "path": "readme.md",
    "chars": 52079,
    "preview": "# TF: Textfile & String Library for AutoHotkey [lib] - v3.8\n\n__A \"Swiss Army Knife\" library for Text (files)__\n\n## Intro"
  },
  {
    "path": "tf.ahk",
    "chars": 45958,
    "preview": "/*\nName          : TF: Textfile & String Library for AutoHotkey\nVersion       : 3.8\nDocumentation : https://github.com/h"
  }
]

About this extraction

This page contains the full source code of the hi5/TF GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 3 files (110.6 KB), approximately 33.2k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!