Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C++

GrepWrap -- A Windows Wrapper for GNU Grep

Rate me:
Please Sign up or sign in to vote.
4.78/5 (14 votes)
1 Mar 2010CPOL10 min read 72.3K   914   47   33
A GUI wrapper for the GNU command-line tool, Grep, used to search folders of text/code files
Main Interface for GrepWrap

Table of Contents

Revision History

  • Versions 1.0 to 1.2 -- Personal use only versions
  • Version 1.3 -- Initial inflicted upon release to CodeProject
  • Version 1.4 -- Replaced demo.zip with an installable (and new name)
  • Version 1.5 -- Added 'Programs' menu item to indicate location of grep.exe, misc cleanup
  • Version 1.6 -- Fixed a bug with files that don't have a default system handler

Introduction

I have always found the command-line utility grep to be useful for brute-force searching of patterns in text files (especially code source!), but the number of options is daunting and when the search is finished, I always have to manually find the matching file and open it up and scroll to the correct line. It just seemed that I was missing something.

Running Grep from the command line

The above is slightly more easily read as:

grep --with-filename --line-number -C2  --devices=skip --color=always \
--binary-files=without-match -P --recursive -i --exclude-dir='Debug' \
--exclude-dir='Release' --regexp='toolbar.*color' --include="*.cpp"  \
--include="*.c"  --include="*.h" .

followed by the output (with coloring).

This is a recursive search in the current directory for C/C++ files, avoiding the Release and Debug directories, for the pattern 'toolbar.*color'. The output has coloring and context to help identify the match. There was a match found on line 76 of MainFrm.cpp. I find this clear as mud.

So my solution is GrepWrap, which is just a simple wrapper around GNU Grep. Note: in the original version of this article, I was using the name GrepWin, which is the same as another tool by TortoisesVN.

It has nothing to do with other programs with similar names, such as TortoisesVN grepWin or Windows Grep. In fact, until I started to write this article, I was only vaguely aware of some of these others. However, I have looked at them and they do not meet my particular needs, although they seem to be fine utilities.

Note: I do not include grep.exe in this article as it is freely available by installing Cygwin. I strongly suggest installing Cygwin even if you have no interest in GrepWrap as it has hundreds of incredibly useful utilities running from the mundane awk program to a complete X-Windows server! And the price is right...

Background

I come from a Unix background where the use of command-line tools rather than a GUI was the normal mode of operation. When I changed jobs to my current employer, I found myself in a Microsoft shop; I was a fish out of water. I had MKS Toolkit on my home PC but my company wasn't interested in spending that kind of money on me at work. I subsequently discovered Cygwin--a large collection of Unix tools ported to the Windows environment. (Cygwin now says that they are emulating a Linux environment, but it looks like Unix to me). So I have my productivity enhancing toolset back again. But staying in the Windows environment, I became very fond of the tendency to have a GUI for everything. There's even a GUI for changing directories! (File Explorer)

The closest GUI I could find for searching through files was Windows Desktop Search. This was agreeably fast, but had its problems such as no regular expressions, no context preview of matching lines, no way to open the matching file at the line of interest, etc. I found a number of programs here on Code Project that emulated grep or called grep from the command line, but while intriguing, were not what I wanted.

So I decided to write my own wrapper. How hard could that be? Just double-park, blast out the trivial amount of code, and be done with it. Famous last words, right?

Using the Code

Using GrepWrap is easy enough. Basically, you select the starting folder for searching for files. If you have been there before, you can select the folder from a drop-down list. (Sorry, only one starting folder at a time and no wildcards.) You can Cut & Paste the folder name, or browse for it using the Select Folder button.

Browse for a folder

The "Search" pattern is a Perl-style regular expression. If you have used the pattern before, it is available in the drop-down list.

"Include Files" lists just those file patterns (Window-style file patterns) to search. If you tend to scan through a tree of source code, a pattern like *.c;*.cpp;*.h may do the job. Previously used patterns are available through the drop-down list. If no files are listed, then all are searched (except binaries and devices).

If you want to exclude certain file patterns, you can do that too. More importantly, if there are directories that you do not want to recurse into, you can list them also. For instance, when searching source code, I generally don't want to recurse into the Debug or Release directories. Or the backup directory. Or Eclipse's .metadata directory, etc., aud nauseum. This can really speed up the search. Binary files are skipped automatically; this tool is intended for text files. It just isn't any good for searching through Word (*.doc) or Excel (*.xls) files. For that, you need something else.

Finally, there are the options. Usually, the only one of interest is "Caseless" mode for those times when you cannot remember if the line of interest had, for instance, "posix" or "POSIX".

At long last, press the "Search" button to start the search. Note: By design, entering a <Carriage-Return> in a field will not start the search. Too many times, I accidentally started a search before I had set everything up.

For example, suppose I want to setup stuff for the toolbar and I know I have some code that does this. So I search for "TOOLBAR" and there it is:

Search for 'TOOLBAR'

The match to the regular expression is shown in red and lines that have a "--" show a context. I can double-click on the line of interest (either context or matching line) and the default application will open. If that application is one of the ones GrepWrap knows about, the cursor is placed on the line of interest.

Applications Known to GrepWrap

There are also a number of other applications known to GrepWrap, and accessible via the context menu. The list is short but it is easy enough to add new ones via code modification.

Right now, the list of applications that GrepWrap will tailor is fairly limited:

  • devenv.exe (Visual Studio 2003/2005/2008, and probably 2010)
  • XEmacs.exe (yes, I'm one of those...)
  • CodeWright.exe
  • UltraEdit.exe
  • Notepad.exe (ignores the line number, sorry)

Note: The default locations for these programs are hard-coded, but can be overridden in the registry.

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Scientific Atlanta\GrepWrap\Settings]
"Grep Pathname"="\"C:\\cygwin\\bin\\grep.exe\""
"XEmacs Pathname"="\"C:\\Program Files\\XEmacs\\
	XEmacs-21.4.22\\i586-pc-win32\\gnuclientw.exe\""
"UltraEdit Pathname"="\"C:\\Program Files\\UltraEdit\\uedit32.exe\""
"Notepad Pathname"="\"C:\\WINDOWS\\system32\\notepad.exe\""

Or you can drag the line of interest to a target and the file will be opened with that target (assuming it supports Drag & Drop, of course). In the case of dropping on File Explorer, a copy of the file is made into the directory that was displayed.

For the specific case of setting the location of grep.exe, use the Programs menu item:

Select grep.exe location

Adding New Targets/Applications

It is easy enough to tailor the behavior for other applications. Just look at the message map and add appropriate declarations for the function to open the application and the function to handle the context menu:

C++
ON_COMMAND(ID_EDIT_OPEN_SYSTEM_DEFAULT, 	  &CGrepWrapView::OnEditOpenSystemDefault)
ON_COMMAND(ID_EDIT_OPEN_XEMACS,         	  &CGrepWrapView::OnEditOpenXEmacs)
ON_COMMAND(ID_EDIT_OPEN_ULTRAEDIT,      	  &CGrepWrapView::OnEditOpenUltraEdit)
ON_COMMAND(EDIT_EDIT_OPEN_NOTEPAD,      	  &CGrepWrapView::OnEditOpenNotepad)
ON_COMMAND(ID_EDIT_OPENUSINGCODEWRIGHT, 	  &CGrepWrapView::OnEditOpenUsingCodeWright)

ON_UPDATE_COMMAND_UI(ID_EDIT_OPEN_SYSTEM_DEFAULT,
			&CGrepWrapView::OnUpdateEditOpenSystemDefault)
ON_UPDATE_COMMAND_UI(ID_EDIT_OPEN_XEMACS,         &CGrepWrapView::OnUpdateEditOpenXEmacs)
ON_UPDATE_COMMAND_UI(ID_EDIT_OPEN_ULTRAEDIT,
			&CGrepWrapView::OnUpdateEditOpenUltraEdit)
ON_UPDATE_COMMAND_UI(EDIT_EDIT_OPEN_NOTEPAD,      &CGrepWrapView::OnUpdateEditOpenNotepad)
ON_UPDATE_COMMAND_UI(ID_EDIT_OPENUSINGCODEWRIGHT,
			&CGrepWrapView::OnUpdateEditOpenUsingCodeWright)

Default pathnames to the program also need to be defined.

C++
// configurable pathnames for common programs to open a file.
// These show up in the context menu
CString m_grepPath;
CString m_xemacsPath;
CString m_ultraeditPath;
CString m_notepadPath;
CString m_codewrightPath;

And the code to get the latest version of the pathnames:

C++
m_grepPath       = AfxGetApp()->GetProfileString("Settings", "Grep Pathname",
			"\"C:\\cygwin\\bin\\grep.exe\"");
m_xemacsPath     = AfxGetApp()->GetProfileString("Settings", "XEmacs Pathname",
		"\"C:\\bin\\XEmacs\\XEmacs-21.4.19\\i586-pc-win32\\gnuclientw.exe\"");
m_ultraeditPath  = AfxGetApp()->GetProfileString("Settings", "UltraEdit Pathname",
			"\"C:\\Program Files\\UltraEdit\\uedit32.exe\"");
m_notepadPath    = AfxGetApp()->GetProfileString("Settings", "Notepad Pathname",
			"\"C:\\WINDOWS\\system32\\notepad.exe\"");
m_codewrightPath = AfxGetApp()->GetProfileString("Settings", "CodeWright Pathname",
			"\"C:\\Program Files\\CodeWright\\cw32.exe\"");

// Ensure there are entries in the registry so we can change them as needed.
AfxGetApp()->WriteProfileString("Settings", "Grep Pathname",       m_grepPath);
AfxGetApp()->WriteProfileString("Settings", "XEmacs Pathname",     m_xemacsPath);
AfxGetApp()->WriteProfileString("Settings", "UltraEdit Pathname",  m_ultraeditPath);
AfxGetApp()->WriteProfileString("Settings", "Notepad Pathname",    m_notepadPath);
AfxGetApp()->WriteProfileString("Settings", "CodeWright Pathname", m_codewrightPath);

Then define the appropriate code to open the application. Here is the code for CodeWright:

C++
void CGrepWrapView::OnEditOpenUsingCodeWright()
{
    CString filename;	// name of the file to be opened, if any
    int linenumber;
    CString execName;	// full pathname to file to execute
    CString baseName;	// "devenv.exe" or "vb6.exe" or the like
    CString fileBase;	// myprog.cpp or the like
    if(!m_GetExecParameters(filename, linenumber, execName, baseName, fileBase))
    {
        return;
    }

    filename.Replace("/", "\\");	// use Windows-style separators

    CString params;
    params.Format("-G%d \"%s\"", linenumber, filename);
    m_RunShellExec(filename, m_codewrightPath, params);// filename is ignored here
}

Here is the code for handling the context menu for CodeWright:

C++
void CGrepWrapView::OnUpdateEditOpenUsingCodeWright(CCmdUI *pCmdUI)
{
    CString filename;	// name of the file to be opened, if any
    int linenumber;
    CString execName;	// full pathname to file to execute
    CString baseName;	// "devenv.exe" or "vb6.exe" or the like
    CString fileBase;	// myprog.cpp or the like
    m_GetExecParameters(filename, linenumber, execName, baseName, fileBase);

    if(filename.IsEmpty())
    {
        pCmdUI->SetText("Open using:  CodeWright -Glinenumber filename");
        pCmdUI->Enable(FALSE);
        return;
    }

    CString msg;
    msg.Format("Open using:  CodeWright -G%d %s", linenumber, fileBase);
    pCmdUI->SetText(msg);
    pCmdUI->Enable(TRUE);
}

Bugs

Are you kidding? Of course there are bugs. But I prefer to call them To Do items.

To Do

  • Add a mechanism to allow the user to add arbitrary target applications
  • Add the ability to trim or clear the history in the drop-down lists
  • Add configuration context size (currently fixed at 3 lines before and after match)

Points of Interest

As with any double-park-and-code project, there were some things I wanted to try just to see if I could.

Regular Expressions in VBScript

The use of the regular expression engine in the Microsoft-supplied VBScript component I had seen but never used. I found regexp.h and regexp.cpp on the internet (attributed above) and it worked like a charm. It is not full Perl-style regular expressions, but it does the job and doesn't add a lot of weight to the application. I tend to use regular expressions even when other mechanisms are faster just because I am so used to them.

For instance, to help parse the output of grep (with color coding enabled), I have a series of simple regular expressions that I can use for testing. This one is used to distinguish a line that does not contain a match but is context (this is a paraphrasing of the actual code):

C++
// When we have context lines instead of lines with a pattern match, the files
// are separated by cyan -- lines. Check for that first
m_dashRe = new RegExp;
m_dashRe->compile("^\033\\[36m--\033\\[m");

...

if(m_dashRe->exec(*line))	// compare against the current line from grep
{
	...

This may not be clear if you do not speak regex or termcap, but the pattern just looks for the embedded escape code to turn on the cyan color, followed by -- followed by the escape code to disable coloring.

Piping Output from a Process

Using a separate process to perform grunt work and passing the results to the "main" process is something I used to do in Unix (before threads), but Joseph M Newcomer's Process.h/cpp, with minor modifications allowed me to do this almost transparently here. This is how grep is wrapped. I did have to add some code to throttle output from grep if I was still updating the screen, but that was not terribly difficult.

Drag & Drop

I've always wanted to try out Drag & Drop but didn't have a clue on how. Here, it was almost embarrassingly easy to drag from GrepWrap to other applications. Thanks to "benbuck" (attributed above) for dragdrop.h/.cpp which worked with few modifications.

Rich Edit Control

However, the most effort was expended in the Rich Edit Control. I needed to have a constant-width font and the ability to highlight the matching characters for the regular expression. I could probably have hacked something with an embedded Explorer control, but I've done that and not been particular happy with that. It seemed grossly over-powered for my needs. So I had to learn about TOM (IRichEditOle stuff and the Text Object Model). I also had to deal with being able to scroll the results even as new results were being added. If I didn't do it correctly, the whole screen would start to "quiver" and shake. Not a pretty sight.

The gotchas for the RichEditCtrl were:

  1. Appending text without losing all previous color-formatting
  2. Allowing cursor position to change while still appending matching text

These turned out to be very hard to do. I am not at all convinced that I have got it right, but it seems to work OK.

Parsing the output of grep was tedious, but I was able to turn on output coloring and parse the special escape sequences to identify the various parts of the output: filename, line number, matching text (of which there might be several!) and context lines (leading and trailing). The number of colors that grep can output is surprising, but I felt that just black and red were needed for this wrapper.

Acknowledgements and References

I am a big fan of code theft re-use so I have code from several sources, some of which is seriously mangled from the original. I hope I have not left anybody out.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Thales Visionix
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Generalfinding grep.exe on a computer Pin
Martin081519-Jan-10 23:32
professionalMartin081519-Jan-10 23:32 
AnswerRe: finding grep.exe on a computer [modified] Pin
Harold Bamford20-Jan-10 6:26
Harold Bamford20-Jan-10 6:26 
Generalthe original grepWin Pin
SteveKing19-Jan-10 19:59
SteveKing19-Jan-10 19:59 
AnswerRe: the original grepWin Pin
Harold Bamford20-Jan-10 6:22
Harold Bamford20-Jan-10 6:22 
GeneralRe: the original grepWin Pin
SteveKing20-Jan-10 23:41
SteveKing20-Jan-10 23:41 
GeneralRe: the original grepWin Pin
Dave Cross21-Jan-10 3:34
professionalDave Cross21-Jan-10 3:34 
GeneralRe: the original grepWin Pin
Galatei23-Jan-10 15:13
Galatei23-Jan-10 15:13 
GeneralRe: the original grepWin Pin
Harold Bamford25-Jan-10 6:34
Harold Bamford25-Jan-10 6:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.