===============================================================================
 LinuxUltraGrep — for the LinuxDoors AppImage Store
===============================================================================

 Name:          LinuxUltraGrep
 Price:         $0.00
 Author:        Stan Switaj
 Provider:      GoodsAppStore.com
 Created:       September 17, 2026
 Updated:       September 18, 2026
 Kind:          A LinuxDoors AppImage Software Product
 File:          LinuxUltraGrep.AppImage
 Size:          37M   (single file, download and run)
 SHA256:        d9c23552914f5dab300ff5f329ba9adb9a71b5061740d2eca00b10bcc0ba9d0b
 Bug reports:   bug-reports-linuxultragrep@linuxdoors.com

 CARD DESCRIPTION (the one line the store shows under the name)

     Search inside files, and search for files, with the speed of grep.


-------------------------------------------------------------------------------
 DESCRIPTION
-------------------------------------------------------------------------------

 A search program. Type what you are looking for, say where to look, press
 Find. It looks inside files the way grep does, and it looks for files by
 name the way find does, and it can replace what it finds across a thousand
 files after showing you exactly what it intends to change.

 It does what grepWin does on Windows, which is the program people miss most
 when they come to Linux and discover that the searching is all on the
 command line. Everything grepWin offers is here — text or regular
 expression, match case, whole words, inverse search, searching again inside
 what was found, file name patterns, folders to skip, size and date limits,
 hidden and binary files, symbolic links, replace with backups, saved
 searches, a regular expression tester and an export.

 Two searches, two tabs. Contents looks inside files. File names looks at
 the names themselves. They are separate because they are separate
 questions, and mixing them means half your results are always the kind you
 did not ask for. They share the filters underneath, so switching tabs does
 not mean setting it all up again.

 Everything it needs travels with it. There is no Python to install first
 and no Qt to match, and nothing on the machine for it to disagree with —
 which is the point of shipping it as one file.

 Written for LinuxDoors, though nothing in it is specific to LinuxDoors — it
 runs on Debian and Ubuntu type systems, and on any other Linux distribution
 that can run an AppImage.


-------------------------------------------------------------------------------
 What does the searching, and why
-------------------------------------------------------------------------------

 Whichever is quickest, and the answer is not always the same program.
 Measured over /usr/include — 4,692 files — best of three runs, warm caches:

     GNU grep across 10 processes            0.029s
     GNU grep -r                             0.081s
     python, 10 processes                    0.168s
     python, one thread                      0.406s
     python, 10 threads                      0.628s

 Two things in that table decided how the program is built.

 The first is that threads make Python searching *slower* — slower than not
 using them at all. Python's regular expression engine holds the interpreter
 lock while it works, so ten threads take turns rather than run together.
 The pool here is a process pool and never a thread pool.

 The second is that the two engines did not agree on the answer. grep
 counted 32,580 where Python counted 36,770, because `grep -c` counts *lines
 that match* while Python was counting *matches* — a line with three hits is
 one line and three matches. A search that reports a different number
 depending on which program happened to run is worse than a slow one.

 So the fast program is never asked for a count. It is asked only *which
 lines match*, which is the part it is extraordinarily good at, and the
 exact matches within those few lines are worked out afterwards, in one
 place, in one way. The status bar names the engine that ran, and
 `--check` runs every engine over the same folder and fails if any of them
 disagree by so much as one.

 On a machine with no grep at all there is still the built-in engine, and it
 is not a toy: it maps large files rather than reading them, stops at the
 first null byte that says a file is not text, and runs across every core
 but two. Nothing here *requires* an external program.


-------------------------------------------------------------------------------
 Looking inside files
-------------------------------------------------------------------------------

     Text or regular expression      Python's own, which is very close to
                                     what grepWin gives through boost
     Match case                      exactly as typed
     Whole words                     needle, not needless
     Inverse search                  the files with no match at all
     Search within the results       narrow a search rather than start over
     Dot matches newline             for patterns that span lines

 The results are a file, then every line in it that matched, with the
 matched part picked out and the line number beside it. Double-click and the
 file opens in your editor at that line — and at the column the match starts
 on, not merely the top of the file.


-------------------------------------------------------------------------------
 Looking for files
-------------------------------------------------------------------------------

 A pattern matches the file name. Put a slash in it — test/fixtures — and it
 matches the path instead. Wildcards * ? [ ] work, or tick Regular
 expression for the full thing.

 The folder you are searching in is never part of what is matched. It is
 part of every result, so if its name happened to contain what you typed,
 everything would match — and turning on Match case would appear to fix it
 while fixing nothing.

 Include folders, in the filters, matches the folders themselves and not
 only what is inside them. It is a question you can only ask of names —
 there is no such thing as grepping a directory — so it greys out on the
 Contents tab rather than disappearing, which would leave you hunting for
 where it went.

 Three of the file filters deliberately do not apply to folders. Name
 matches would wipe out every folder the moment it said *.py, which is
 what it says nearly always; size and age are about files. All three would
 read as the option being broken rather than as a filter doing its job.
 What does apply is what is about folders: hidden ones, skipped ones, and
 whether subfolders are being walked at all.

 A folder in the results has an arrow. Open it and it lists what is in it,
 folders first, and those open too, so a match can be walked down into
 rather than only glanced at. What is inside is read when you open it and
 not when you search: a name search can match thousands of folders, and
 reading every one of them to fill rows nobody has scrolled to would turn
 a search of a disk into that search plus thousands of directory reads.
 Expand all reads the first two hundred and says so; the rest read
 themselves as they are opened.


-------------------------------------------------------------------------------
 Which files get looked at
-------------------------------------------------------------------------------

     Name matches            *.py, *.txt — or a regular expression
     Skip folders            .git, node_modules and the rest, and editable
     Include subfolders      or just the one
     Include hidden files    off by default
     Include binary files    off by default; a null byte in the first few
                             kilobytes is what says a file is not text
     Follow symbolic links   off by default, and a loop of links will not
                             hang it
     Size                    smaller or bigger than a figure you set
     Changed                 before or after a date

 The panel comes back the way you left it, open or closed, with every box in
 it as it was. A filter that quietly resets itself overnight is a fine way
 to wonder why the same search now finds something else.


-------------------------------------------------------------------------------
 Replacing
-------------------------------------------------------------------------------

 Replace shows you what it will do before it does it: every match, the line
 as it stands and the line as it would become, with a tick beside each one.
 Untick what you would rather keep. Nothing is written until you press the
 button, and what is written is only what is still ticked.

 Backups are on by default, beside the file or in a folder of your choosing.
 File dates can be kept, because changing a thousand modification times is a
 rotten way to find out you meant a different folder.

 \1 and \2 put the groups from the search into the replacement.


-------------------------------------------------------------------------------
 The command line
-------------------------------------------------------------------------------

 The same search, without the window. The short flags are grep's, because
 anybody reaching for this already knows them.

     LinuxUltraGrep.AppImage -r 'def .*self' ~/code
     LinuxUltraGrep.AppImage -rl --include '*.py' TODO ~/code
     LinuxUltraGrep.AppImage --files '*.conf' /etc
     LinuxUltraGrep.AppImage --files test --folders ~/code
     LinuxUltraGrep.AppImage -r --replace new old ~/code --dry-run
     LinuxUltraGrep.AppImage --column -o -E '\bclass \w+' ~/code

     -i -s -w -v -F -E        case, whole words, inverse, fixed, regex
     -l -c -h -q -o           names only, counts, no file name, quiet,
                              just the matched part
     -r --no-recursive        into subfolders, or not
     --include --exclude-dir  which files, which folders to skip
     --hidden --binary --follow
     --column                 file:line:column:text
     --files                  search names rather than contents
     --folders                match folder names too; they print with
                              a trailing slash, the way ls -F writes them
     --replace --dry-run      change things, or see what would change
     --open --open-with       open every match in an editor
     --engine                 auto, grep or python
     --check --bench          prove the engines agree; time them

 With no pattern and no --files, the window opens.


-------------------------------------------------------------------------------
 The rest of it
-------------------------------------------------------------------------------

 A regular expression tester. Try a pattern against text you type, see every
 match and what each group caught, before turning it loose on ten thousand
 files.

 Saved searches. Keep a search you use often and load it back in one click,
 with its pattern, its folder and its filters.

 A results toolbar. Expand all, Minimize all, Search within, Export and
 Clear results, with a box that narrows the list you already have without
 searching again.

 Export. The results to a text file or a CSV, file and line and text.

 How results open. It uses whichever editor it finds — LinuxNoteText first
 — and opens at the line and column of the match. If you would rather it ran
 something else, say so once: a command of your own with {file}, {line},
 {column}, {dir} and {name} in it, quoted as a shell would, so a path with a
 space in it stays one argument.

 Text size. Tools, Text size — a slider rather than a pair of buttons,
 because the question is "how big", not "one step bigger": with buttons you
 find the size you want by pressing until it looks right, and with a slider
 you put it where you want it. It applies as it slides, and it reaches the
 results already on screen rather than only the next ones.

 Light and dark. Every colour pair that sits on top of another is measured
 against the WCAG contrast standard rather than judged by eye, in both
 themes, and the program will tell you so itself — Tools, Check the colours.


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

 Built by LinuxDoors — this is our own application, not a mirror and not a
 repackaging of somebody else's build. The SHA-256 above is authoritative.

 It carries its own Python and its own Qt, so the window and the search work
 on a computer with nothing installed on it. The filters, the two engines,
 the replacing and the matching are all the program's own code. GNU grep is
 used when it is on the machine and is faster, and never trusted to do the
 counting.


-------------------------------------------------------------------------------
 Running it — nothing to install
-------------------------------------------------------------------------------

     chmod +x LinuxUltraGrep.AppImage
     ./LinuxUltraGrep.AppImage

 Or give it a folder to start in:

     ./LinuxUltraGrep.AppImage ~/code

 On LinuxDoors, which has no FUSE, run it the way LinuxDoors runs any
 AppImage:

     ./LinuxUltraGrep.AppImage --appimage-extract-and-run

 or through linuxdoors-run-appimage, which does that for you and unpacks it
 once rather than on every launch.

 Settings live in ~/.linuxultragrep.save, written mode 0600 when the program
 closes. Nothing secret goes in it — there is nothing secret to put — but it
 holds where you have been looking and what you have been looking for, which
 is nobody else's business. Deleting that file puts everything back as it
 started.
