* Imported Tracker Grep 5.1 source code

* Renamed it to TextSearch
* Cleaned up the code to match our coding style (already was almost compliant
  and the code is very clean and well designed, a pleasure to work with!)
* Fixed memory leaks and potential memory leaks in error codepaths
* Checked the success of most allocations (GrepWindow is missing) and
  implemented error code paths
* Simplified the code in a few places
* Fixed bugs in code that iterated over the selected list items and assumed
  it found a top level item already while it may not have


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26737 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus
2008-08-02 18:43:22 +00:00
parent d172ee84c3
commit 6c3234ba0b
17 changed files with 3269 additions and 0 deletions
Binary file not shown.
+1
View File
@@ -3,4 +3,5 @@ SubDir HAIKU_TOP src add-ons tracker ;
SubInclude HAIKU_TOP src add-ons tracker zipomatic ;
SubInclude HAIKU_TOP src add-ons tracker filetype ;
SubInclude HAIKU_TOP src add-ons tracker mark_as ;
SubInclude HAIKU_TOP src add-ons tracker text_search ;
@@ -0,0 +1,11 @@
/*
* Copyright (C) 2008 Stephan Aßmus <[email protected]>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#ifndef GLOBAL_DEFS_H
#define GLOBAL_DEFS_H
#define APP_SIGNATURE "application/x-vnd.mahlzeit.trackergrep"
#define APP_NAME "TextSearch"
#endif // GLOBAL_DEFS_H
+133
View File
@@ -0,0 +1,133 @@
/*
* Copyright (c) 1998-2007 Matthijs Hollemans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "GrepApp.h"
#include <stdio.h>
#include <Entry.h>
#include "GlobalDefs.h"
#include "GrepWindow.h"
#include "Model.h"
GrepApp::GrepApp()
: BApplication(APP_SIGNATURE),
fGotArgvOnStartup(false),
fGotRefsOnStartup(false),
fQuitter(NULL)
{
}
GrepApp::~GrepApp()
{
delete fQuitter;
}
void
GrepApp::ArgvReceived(int32 argc, char** argv)
{
fGotArgvOnStartup = true;
BMessage message(B_REFS_RECEIVED);
int32 refCount = 0;
for (int32 i = 1; i < argc; i++) {
BEntry entry(argv[i]);
entry_ref ref;
entry.GetRef(&ref);
if (entry.Exists()) {
message.AddRef("refs", &ref);
refCount += 1;
} else
printf("%s: File not found: %s\n", argv[0], argv[i]);
}
if (refCount > 0)
RefsReceived(&message);
}
void
GrepApp::RefsReceived(BMessage* message)
{
if (IsLaunching())
fGotRefsOnStartup = true;
new GrepWindow(message);
}
void
GrepApp::ReadyToRun()
{
if (!fGotArgvOnStartup && !fGotRefsOnStartup)
_NewUnfocusedGrepWindow();
// TODO: stippi: I don't understand what this is supposed to do:
if (fGotArgvOnStartup && !fGotRefsOnStartup)
PostMessage(B_QUIT_REQUESTED);
}
void
GrepApp::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_SILENT_RELAUNCH:
_NewUnfocusedGrepWindow();
break;
case MSG_TRY_QUIT:
_TryQuit();
break;
default:
BApplication::MessageReceived(message);
break;
}
}
void
GrepApp::_TryQuit()
{
if (CountWindows() == 0)
PostMessage(B_QUIT_REQUESTED);
if (CountWindows() == 1 && fQuitter == NULL) {
fQuitter = new BMessageRunner(be_app_messenger,
new BMessage(MSG_TRY_QUIT), 200000, -1);
}
}
void
GrepApp::_NewUnfocusedGrepWindow()
{
BMessage emptyMessage;
new GrepWindow(&emptyMessage);
}
+48
View File
@@ -0,0 +1,48 @@
/*
* Copyright (c) 1998-2007 Matthijs Hollemans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <Application.h>
#include <MessageRunner.h>
#ifndef GREP_APP_H
#define GREP_APP_H
class GrepApp : public BApplication {
public:
GrepApp();
virtual ~GrepApp();
virtual void ArgvReceived(int32 argc, char** argv);
virtual void RefsReceived(BMessage* message);
virtual void MessageReceived(BMessage* message);
virtual void ReadyToRun();
private:
void _TryQuit();
void _NewUnfocusedGrepWindow();
bool fGotArgvOnStartup;
bool fGotRefsOnStartup;
BMessageRunner* fQuitter;
};
#endif // GREP_APP_H
@@ -0,0 +1,42 @@
/*
* Copyright (c) 1998-2007 Matthijs Hollemans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "GrepListView.h"
#include <Path.h>
ResultItem::ResultItem(const entry_ref& ref)
: BStringItem("", 0, false),
ref(ref)
{
BEntry entry(&ref);
BPath path(&entry);
SetText(path.Path());
}
GrepListView::GrepListView()
: BOutlineListView(BRect(0, 0, 40, 80), "SearchResults",
B_MULTIPLE_SELECTION_LIST, B_FOLLOW_ALL_SIDES,
B_WILL_DRAW | B_NAVIGABLE)
{
}
@@ -0,0 +1,43 @@
/*
* Copyright (c) 1998-2007 Matthijs Hollemans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef GREP_LIST_VIEW_H
#define GREP_LIST_VIEW_H
#include <Entry.h>
#include <OutlineListView.h>
#include <StringItem.h>
class ResultItem : public BStringItem {
public:
ResultItem(const entry_ref& ref);
entry_ref ref;
};
class GrepListView : public BOutlineListView {
public:
GrepListView();
};
#endif // GREP_LIST_VIEW_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,138 @@
/*
* Copyright (c) 1998-2007 Matthijs Hollemans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef GREP_WINDOW_H
#define GREP_WINDOW_H
#include <InterfaceKit.h>
#include <FilePanel.h>
#include "Model.h"
#include "GrepListView.h"
class Grepper;
class GrepWindow : public BWindow {
public:
GrepWindow(BMessage* message);
virtual ~GrepWindow();
virtual void FrameResized(float width, float height);
virtual void FrameMoved(BPoint origin);
virtual void MenusBeginning();
virtual void MenusEnded();
virtual void MessageReceived(BMessage* message);
virtual void Quit();
private:
void _InitRefsReceived(entry_ref* directory,
BMessage* message);
void _SetWindowTitle();
void _CreateMenus();
void _CreateViews();
void _LayoutViews();
void _TileIfMultipleWindows();
void _LoadPrefs();
void _SavePrefs();
void _OnStartCancel();
void _OnSearchFinished();
void _OnReportFileName(BMessage* message);
void _OnReportResult(BMessage* message);
void _OnReportError(BMessage* message);
void _OnRecurseLinks();
void _OnRecurseDirs();
void _OnSkipDotDirs();
void _OnEscapeText();
void _OnCaseSensitive();
void _OnTextOnly();
void _OnInvokePe();
void _OnCheckboxShowLines();
void _OnMenuShowLines();
void _OnInvokeItem();
void _OnSearchText();
void _OnHistoryItem(BMessage* message);
void _OnTrimSelection();
void _OnCopyText();
void _OnSelectInTracker();
void _OnQuitNow();
void _OnAboutRequested();
void _OnFileDrop(BMessage* message);
void _OnRefsReceived(BMessage* message);
void _OnOpenPanel();
void _OnOpenPanelCancel();
void _OnSelectAll(BMessage* message);
void _OnNewWindow();
bool _OpenInPe(const entry_ref& ref, int32 lineNum);
void _RemoveFolderListDuplicates(BList* folderList);
status_t _OpenFoldersInTracker(BList* folderList);
bool _AreAllFoldersOpenInTracker(BList* folderList);
status_t _SelectFilesInTracker(BList* folderList,
BMessage* refsMessage);
private:
BTextControl* fSearchText;
GrepListView* fSearchResults;
BMenuBar* fMenuBar;
BMenu* fFileMenu;
BMenuItem* fNew;
BMenuItem* fOpen;
BMenuItem* fClose;
BMenuItem* fAbout;
BMenuItem* fQuit;
BMenu* fActionMenu;
BMenuItem* fSelectAll;
BMenuItem* fSearch;
BMenuItem* fTrimSelection;
BMenuItem* fCopyText;
BMenuItem* fSelectInTracker;
BMenuItem* fOpenSelection;
BMenu* fPreferencesMenu;
BMenuItem* fRecurseLinks;
BMenuItem* fRecurseDirs;
BMenuItem* fSkipDotDirs;
BMenuItem* fCaseSensitive;
BMenuItem* fEscapeText;
BMenuItem* fTextOnly;
BMenuItem* fInvokePe;
BMenuItem* fShowLinesMenuitem;
BMenu* fHistoryMenu;
BMenu* fEncodingMenu;
BMenuItem* fUTF8;
BMenuItem* fShiftJIS;
BMenuItem* fEUC;
BMenuItem* fJIS;
BCheckBox* fShowLinesCheckbox;
BButton* fButton;
Grepper* fGrepper;
BString fOldPattern;
Model* fModel;
BFilePanel* fFilePanel;
};
#endif // GREP_WINDOW_H
+496
View File
@@ -0,0 +1,496 @@
/*
* Copyright (c) 1998-2007 Matthijs Hollemans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "Grepper.h"
#include <new>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Directory.h>
#include <List.h>
#include <NodeInfo.h>
#include <Path.h>
#include <UTF8.h>
using std::nothrow;
// TODO: stippi: Check if this is a the best place to maintain a global
// list of files and folders for node monitoring. It should probably monitor
// every file that was grepped, as well as every visited (sub) folder.
// For the moment I don't know the life cycle of the Grepper object.
char*
strdup_to_utf8(uint32 encode, const char* src, int32 length)
{
int32 srcLen = length;
int32 dstLen = 2 * srcLen;
// TODO: stippi: Why the duplicate copy? Why not just return
// dst (and allocate with malloc() instead of new)? Is 2 * srcLen
// enough space? Check return value of convert_to_utf8 and keep
// converting if it didn't fit?
char* dst = new (nothrow) char[dstLen + 1];
if (dst == NULL)
return NULL;
int32 cookie = 0;
convert_to_utf8(encode, src, &srcLen, dst, &dstLen, &cookie);
dst[dstLen] = '\0';
char* dup = strdup(dst);
delete[] dst;
if (srcLen != length) {
fprintf(stderr, "strdup_to_utf8(%ld, %ld) dst allocate smalled(%ld)\n",
encode, length, dstLen);
}
return dup;
}
char*
strdup_from_utf8(uint32 encode, const char* src, int32 length)
{
int32 srcLen = length;
int32 dstLen = srcLen;
char* dst = new (nothrow) char[dstLen + 1];
if (dst == NULL)
return NULL;
int32 cookie = 0;
convert_from_utf8(encode, src, &srcLen, dst, &dstLen, &cookie);
// TODO: See above.
dst[dstLen] = '\0';
char* dup = strdup(dst);
delete[] dst;
if (srcLen != length) {
fprintf(stderr, "strdup_from_utf8(%ld, %ld) dst allocate "
"smalled(%ld)\n", encode, length, dstLen);
}
return dup;
}
Grepper::Grepper(const char* pattern, Model* model)
: fDirectories(new (nothrow) BList(10)),
fCurrentDir(new (nothrow) BDirectory(&model->fDirectory)),
fCurrentRef(0),
fPattern(NULL),
fModel(model),
fThreadId(-1),
fMustQuit(false)
{
if (!fCurrentDir || !fDirectories || !fDirectories->AddItem(fCurrentDir)) {
// init error
delete fCurrentDir;
fCurrentDir = NULL;
delete fDirectories;
fDirectories = NULL;
return;
}
if (fModel->fEncoding) {
char *src = strdup_from_utf8(fModel->fEncoding, pattern,
strlen(pattern));
_SetPattern(src);
free(src);
} else
_SetPattern(pattern);
}
Grepper::~Grepper()
{
Cancel();
free(fPattern);
// If the thread terminated normally, then there is only
// one object in the list: the initial directory. But if
// the user aborted the search, there may be more.
if (fDirectories) {
for (int32 i = fDirectories->CountItems() - 1; i >= 0; i--)
delete static_cast<BDirectory*>(fDirectories->ItemAt(i));
delete fDirectories;
}
}
bool
Grepper::IsValid() const
{
return fPattern != NULL && fDirectories != NULL && fCurrentDir != NULL;
}
void
Grepper::Start()
{
Cancel();
fMustQuit = false;
fThreadId = spawn_thread(
_SpawnThread, "_GrepperThread", B_NORMAL_PRIORITY, this);
resume_thread(fThreadId);
}
void
Grepper::Cancel()
{
if (fThreadId < 0)
return;
fMustQuit = true;
int32 exitValue;
wait_for_thread(fThreadId, &exitValue);
fThreadId = -1;
}
// #pragma mark - private
int32
Grepper::_SpawnThread(void* cookie)
{
Grepper* self = static_cast<Grepper*>(cookie);
return self->_GrepperThread();
}
int32
Grepper::_GrepperThread()
{
BMessage message;
char fileName[B_PATH_NAME_LENGTH];
char tempString[B_PATH_NAME_LENGTH];
char command[B_PATH_NAME_LENGTH + 32];
BPath tempFile;
sprintf(fileName, "/boot/var/tmp/SearchText%ld", fThreadId);
tempFile.SetTo(fileName);
while (!fMustQuit && _GetNextName(fileName)) {
message.MakeEmpty();
message.what = MSG_REPORT_FILE_NAME;
message.AddString("filename", fileName);
fModel->fTarget->PostMessage(&message);
message.MakeEmpty();
message.what = MSG_REPORT_RESULT;
message.AddString("filename", fileName);
BEntry entry(fileName);
entry_ref ref;
entry.GetRef(&ref);
message.AddRef("ref", &ref);
if (!_EscapeSpecialChars(fileName, B_PATH_NAME_LENGTH)) {
sprintf(tempString, "%s: Not enough room to escape the filename.",
fileName);
message.MakeEmpty();
message.what = MSG_REPORT_ERROR;
message.AddString("error", tempString);
fModel->fTarget->PostMessage(&message);
continue;
}
sprintf(command, "grep -hn %s %s \"%s\" > \"%s\"",
fModel->fCaseSensitive ? "" : "-i", fPattern, fileName,
tempFile.Path());
int res = system(command);
if (res == 0 || res == 1) {
FILE *results = fopen(tempFile.Path(), "r");
if (results != NULL) {
while (fgets(tempString, B_PATH_NAME_LENGTH, results) != 0) {
if (fModel->fEncoding) {
char *tempdup = strdup_to_utf8(fModel->fEncoding,
tempString, strlen(tempString));
message.AddString("text", tempdup);
free(tempdup);
} else
message.AddString("text", tempString);
}
if (message.HasString("text"))
fModel->fTarget->PostMessage(&message);
fclose(results);
continue;
}
}
sprintf(tempString, "%s: There was a problem running grep.", fileName);
message.MakeEmpty();
message.what = MSG_REPORT_ERROR;
message.AddString("error", tempString);
fModel->fTarget->PostMessage(&message);
}
// We wait with removing the temporary file until after the
// entire search has finished, to prevent a lot of flickering
// if the Tracker window for /boot/var/tmp/ might be open.
remove(tempFile.Path());
message.MakeEmpty();
message.what = MSG_SEARCH_FINISHED;
fModel->fTarget->PostMessage(&message);
return 0;
}
void
Grepper::_SetPattern(const char* src)
{
if (src == NULL)
return;
if (!fModel->fEscapeText) {
fPattern = strdup(src);
return;
}
// We will simply guess the size of the memory buffer
// that we need. This should always be large enough.
fPattern = (char*)malloc((strlen(src) + 1) * 3 * sizeof(char));
if (fPattern == NULL)
return;
const char* srcPtr = src;
char* dstPtr = fPattern;
// Put double quotes around the pattern, so separate
// words are considered to be part of a single string.
*dstPtr++ = '"';
while (*srcPtr != '\0') {
char c = *srcPtr++;
// Put a backslash in front of characters
// that should be escaped.
if ((c == '.') || (c == ',')
|| (c == '[') || (c == ']')
|| (c == '?') || (c == '*')
|| (c == '+') || (c == '-')
|| (c == ':') || (c == '^')
|| (c == '\'') || (c == '"')) {
*dstPtr++ = '\\';
} else if ((c == '\\') || (c == '$')) {
// Some characters need to be escaped
// with *three* backslashes in a row.
*dstPtr++ = '\\';
*dstPtr++ = '\\';
*dstPtr++ = '\\';
}
// Note: we do not have to escape the
// { } ( ) < > and | characters.
*dstPtr++ = c;
}
*dstPtr++ = '"';
*dstPtr = '\0';
}
bool
Grepper::_EscapeSpecialChars(char* buffer, ssize_t bufferSize)
{
char* copy = strdup(buffer);
char* start = buffer;
uint32 len = strlen(copy);
bool result = true;
for (uint32 count = 0; count < len; ++count) {
if (copy[count] == '"' || copy[count] == '$')
*buffer++ = '\\';
if (buffer - start == bufferSize - 1) {
result = false;
break;
}
*buffer++ = copy[count];
}
*buffer = '\0';
free(copy);
return result;
}
bool
Grepper::_GetNextName(char* buffer)
{
BEntry entry;
struct stat fileStat;
while (true) {
// Traverse the directory to get a new BEntry.
// _GetNextEntry returns false if there are no
// more entries, and we exit the loop.
if (!_GetNextEntry(entry))
return false;
// If the entry is a subdir, then add it to the
// list of directories and continue the loop.
// If the entry is a file and we can grep it
// (i.e. it is a text file), then we're done
// here. Otherwise, continue with the next entry.
if (entry.GetStat(&fileStat) == B_OK) {
if (S_ISDIR(fileStat.st_mode)) {
// subdir
_ExamineSubdir(entry);
} else {
// file or a (non-traversed) symbolic link
if (_ExamineFile(entry, buffer))
return true;
}
}
}
}
bool
Grepper::_GetNextEntry(BEntry& entry)
{
if (fDirectories->CountItems() == 1)
return _GetTopEntry(entry);
else
return _GetSubEntry(entry);
}
bool
Grepper::_GetTopEntry(BEntry& entry)
{
// If the user selected one or more files, we must look
// at the "refs" inside the message that was passed into
// our add-on's process_refs(). If the user didn't select
// any files, we will simply read all the entries from the
// current working directory.
entry_ref fileRef;
if (fModel->fSelectedFiles.FindRef("refs",
fCurrentRef, &fileRef) == B_OK) {
entry.SetTo(&fileRef, fModel->fRecurseLinks);
++fCurrentRef;
return true;
} else if (fCurrentRef > 0) {
// when we get here, we have processed
// all the refs from the message
return false;
} else {
// examine the whole directory
return fCurrentDir->GetNextEntry(&entry,
fModel->fRecurseLinks) == B_OK;
}
}
bool
Grepper::_GetSubEntry(BEntry& entry)
{
if (!fCurrentDir)
return false;
if (fCurrentDir->GetNextEntry(&entry, fModel->fRecurseLinks) == B_OK)
return true;
// If we get here, there are no more entries in
// this subdir, so return to the parent directory.
fDirectories->RemoveItem(fCurrentDir);
delete fCurrentDir;
fCurrentDir = (BDirectory*)fDirectories->LastItem();
return _GetNextEntry(entry);
}
void
Grepper::_ExamineSubdir(BEntry& entry)
{
if (!fModel->fRecurseDirs)
return;
if (fModel->fSkipDotDirs) {
char nameBuf[B_FILE_NAME_LENGTH];
if (entry.GetName(nameBuf) == B_OK) {
if (*nameBuf == '.')
return;
}
}
BDirectory* dir = new (nothrow) BDirectory(&entry);
if (dir == NULL || dir->InitCheck() != B_OK
|| !fDirectories->AddItem(dir)) {
// clean up
delete dir;
return;
}
fCurrentDir = dir;
}
bool
Grepper::_ExamineFile(BEntry& entry, char* buffer)
{
BPath path;
if (entry.GetPath(&path) != B_OK)
return false;
strcpy(buffer, path.Path());
if (!fModel->fTextOnly)
return true;
BNode node(&entry);
BNodeInfo nodeInfo(&node);
char mimeTypeString[B_MIME_TYPE_LENGTH];
if (nodeInfo.GetType(mimeTypeString) == B_OK) {
BMimeType mimeType(mimeTypeString);
BMimeType superType;
if (mimeType.GetSupertype(&superType) == B_OK) {
if (strcmp("text", superType.Type()) == 0
|| strcmp("message", superType.Type()) == 0) {
return true;
}
}
}
return false;
}
+94
View File
@@ -0,0 +1,94 @@
/*
* Copyright (c) 1998-2007 Matthijs Hollemans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef GREPPER_H
#define GREPPER_H
#include "Model.h"
// Executes "grep" in a background thread.
class Grepper {
public:
Grepper(const char* pattern, Model* model);
virtual ~Grepper();
bool IsValid() const;
void Start();
void Cancel();
private:
// Spawns the real grepper thread.
static int32 _SpawnThread(void* cookie);
// The thread function that does the actual grepping.
int32 _GrepperThread();
// Remembers, and possibly escapes, the search pattern.
void _SetPattern(const char* source);
// Prepends all quotes, dollars and backslashes with at backslash
// to prevent the shell from misinterpreting them.
bool _EscapeSpecialChars(char* buffer,
ssize_t bufferSize);
// Returns the full path name of the next file.
bool _GetNextName(char* buffer);
// Looks for the next entry.
bool _GetNextEntry(BEntry& entry);
// Looks for the next entry in the top-level dir.
bool _GetTopEntry(BEntry& entry);
// Looks for the next entry in a subdir.
bool _GetSubEntry(BEntry& entry);
// Determines whether we can add a subdir.
void _ExamineSubdir(BEntry& entry);
// Determines whether we can grep a file.
bool _ExamineFile(BEntry& entry, char* buffer);
private:
// Contains pointers to BDirectory objects.
BList* fDirectories;
// The directory we are currently looking at.
BDirectory* fCurrentDir;
// The ref number we are currently looking at.
int32 fCurrentRef;
// The (escaped) search pattern.
char* fPattern;
// The directory or files to grep on.
Model* fModel;
// Our thread's ID.
thread_id fThreadId;
// Whether our thread must quit.
volatile bool fMustQuit;
};
#endif // GREPPER_H
+18
View File
@@ -0,0 +1,18 @@
SubDir HAIKU_TOP src add-ons tracker text_search ;
SetSubDirSupportedPlatformsBeOSCompatible ;
# TODO: does not seem to work:
AddResources FileType-F : FileType.rdef ;
Application TextSearch-G :
GrepApp.cpp
GrepListView.cpp
Grepper.cpp
GrepWindow.cpp
Model.cpp
TextSearch.cpp
: be tracker textencoding
: TextSearch.rdef
;
+355
View File
@@ -0,0 +1,355 @@
/*
* Copyright (c) 1998-2007 Matthijs Hollemans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "Model.h"
#include <new>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Directory.h>
#include <Entry.h>
#include <File.h>
#include <FindDirectory.h>
#include <List.h>
#include <MenuItem.h>
#include <Path.h>
using std::nothrow;
Model::Model()
: fDirectory(),
fSelectedFiles(0UL),
fRecurseDirs(true),
fRecurseLinks(false),
fSkipDotDirs(true),
fCaseSensitive(false),
fEscapeText(true),
fTextOnly(true),
fInvokePe(false),
fShowContents(false),
fFrame(100, 100, 500, 400),
fTarget(NULL),
fState(STATE_IDLE),
fFilePanelPath(""),
fEncoding(0)
{
BPath path;
status_t status = find_directory(B_USER_DIRECTORY, &path);
if (status == B_OK)
fFilePanelPath = path.Path();
else
fFilePanelPath = "/boot/home";
}
status_t
Model::LoadPrefs()
{
BFile file;
status_t status = _OpenFile(&file, PREFS_FILE, B_READ_ONLY,
B_USER_SETTINGS_DIRECTORY, NULL);
if (status != B_OK)
return status;
status = file.Lock();
if (status != B_OK)
return status;
int32 value;
if (file.ReadAttr("RecurseDirs", B_INT32_TYPE, 0, &value,
sizeof(int32)) > 0)
fRecurseDirs = (value != 0);
if (file.ReadAttr("RecurseLinks", B_INT32_TYPE, 0, &value,
sizeof(int32)) > 0)
fRecurseLinks = (value != 0);
if (file.ReadAttr("SkipDotDirs", B_INT32_TYPE, 0, &value,
sizeof(int32)) > 0)
fSkipDotDirs = (value != 0);
if (file.ReadAttr("CaseSensitive", B_INT32_TYPE, 0, &value,
sizeof(int32)) > 0)
fCaseSensitive = (value != 0);
if (file.ReadAttr("EscapeText", B_INT32_TYPE, 0, &value,
sizeof(int32)) > 0)
fEscapeText = (value != 0);
if (file.ReadAttr("TextOnly", B_INT32_TYPE, 0, &value,
sizeof(int32)) > 0)
fTextOnly = (value != 0);
if (file.ReadAttr("InvokePe", B_INT32_TYPE, 0, &value,
sizeof(int32)) > 0)
fInvokePe = (value != 0);
if (file.ReadAttr("ShowContents", B_INT32_TYPE, 0, &value,
sizeof(int32)) > 0)
fShowContents = (value != 0);
char buffer [B_PATH_NAME_LENGTH+1];
int32 length = file.ReadAttr("FilePanelPath", B_STRING_TYPE, 0, &buffer,
sizeof(buffer));
if (length > 0) {
buffer[length] = '\0';
fFilePanelPath = buffer;
}
file.ReadAttr("WindowFrame", B_RECT_TYPE, 0, &fFrame, sizeof(BRect));
if (file.ReadAttr("Encoding", B_INT32_TYPE, 0, &value, sizeof(int32)) > 0)
fEncoding = value;
file.Unlock();
return B_OK;
}
status_t
Model::SavePrefs()
{
BFile file;
status_t status = _OpenFile(&file, PREFS_FILE,
B_CREATE_FILE | B_WRITE_ONLY, B_USER_SETTINGS_DIRECTORY, NULL);
if (status != B_OK)
return status;
status = file.Lock();
if (status != B_OK)
return status;
int32 value = 2;
file.WriteAttr("Version", B_INT32_TYPE, 0, &value, sizeof(int32));
value = fRecurseDirs ? 1 : 0;
file.WriteAttr("RecurseDirs", B_INT32_TYPE, 0, &value, sizeof(int32));
value = fRecurseLinks ? 1 : 0;
file.WriteAttr("RecurseLinks", B_INT32_TYPE, 0, &value, sizeof(int32));
value = fSkipDotDirs ? 1 : 0;
file.WriteAttr("SkipDotDirs", B_INT32_TYPE, 0, &value, sizeof(int32));
value = fCaseSensitive ? 1 : 0;
file.WriteAttr("CaseSensitive", B_INT32_TYPE, 0, &value, sizeof(int32));
value = fEscapeText ? 1 : 0;
file.WriteAttr("EscapeText", B_INT32_TYPE, 0, &value, sizeof(int32));
value = fTextOnly ? 1 : 0;
file.WriteAttr("TextOnly", B_INT32_TYPE, 0, &value, sizeof(int32));
value = fInvokePe ? 1 : 0;
file.WriteAttr("InvokePe", B_INT32_TYPE, 0, &value, sizeof(int32));
value = fShowContents ? 1 : 0;
file.WriteAttr("ShowContents", B_INT32_TYPE, 0, &value, sizeof(int32));
file.WriteAttr("WindowFrame", B_RECT_TYPE, 0, &fFrame, sizeof(BRect));
file.WriteAttr("FilePanelPath", B_STRING_TYPE, 0, fFilePanelPath.String(),
fFilePanelPath.Length() + 1);
file.WriteAttr("Encoding", B_INT32_TYPE, 0, &fEncoding, sizeof(int32));
file.Sync();
file.Unlock();
return B_OK;
}
void
Model::AddToHistory(const char* text)
{
BList* items = _LoadHistory();
if (items == NULL)
return;
BString* string = new (nothrow) BString(text);
if (string == NULL || !items->AddItem(string)) {
delete string;
return;
}
int32 count = items->CountItems() - 1;
// don't check last item, since that's the one we just added
for (int32 t = 0; t < count; ++t) {
// If the same text is already in the list,
// then remove it first. Case-sensitive.
BString* string = static_cast<BString*>(items->ItemAt(t));
if (*string == text) {
delete static_cast<BString*>(items->RemoveItem(t));
break;
}
}
if (items->CountItems() == HISTORY_LIMIT)
delete static_cast<BString*>(items->RemoveItem(0L));
_SaveHistory(items);
_FreeHistory(items);
}
void
Model::FillHistoryMenu(BMenu* menu)
{
BList* items = _LoadHistory();
if (items == NULL)
return;
for (int32 t = items->CountItems() - 1; t >= 0; --t) {
BString* item = static_cast<BString*>(items->ItemAt(t));
BMessage* message = new BMessage(MSG_SELECT_HISTORY);
message->AddString("text", item->String());
menu->AddItem(new BMenuItem(item->String(), message));
}
_FreeHistory(items);
}
// #pragma mark - private
BList*
Model::_LoadHistory()
{
BList* items = new (nothrow) BList();
if (items == NULL)
return NULL;
BFile file;
status_t status = _OpenFile(&file, PREFS_FILE, B_READ_ONLY,
B_USER_SETTINGS_DIRECTORY, NULL);
if (status != B_OK)
return items;
status = file.Lock();
if (status != B_OK)
return items;
BMessage message;
status = message.Unflatten(&file);
if (status != B_OK)
return items;
file.Unlock();
BString string;
for (int32 x = 0; message.FindString("string", x, &string) == B_OK; x++) {
BString* copy = new (nothrow) BString(string);
if (copy == NULL || !items->AddItem(copy)) {
delete copy;
break;
}
}
return items;
}
status_t
Model::_SaveHistory(BList* items)
{
BFile file;
status_t status = _OpenFile(&file, PREFS_FILE,
B_CREATE_FILE | B_WRITE_ONLY,
B_USER_SETTINGS_DIRECTORY, NULL);
if (status != B_OK)
return status;
status = file.Lock();
if (status != B_OK)
return status;
BMessage message;
for (int32 x = 0; ; x++) {
BString* string = static_cast<BString*>(items->ItemAt(x));
if (string == NULL)
break;
if (message.AddString("string", string->String()) != B_OK)
break;
}
status = message.Flatten(&file);
file.SetSize(message.FlattenedSize());
file.Sync();
file.Unlock();
return status;
}
void
Model::_FreeHistory(BList* items)
{
for (int32 t = items->CountItems() - 1; t >= 0; --t)
delete static_cast<BString*>((items->RemoveItem(t)));
delete items;
}
status_t
Model::_OpenFile(BFile* file, const char* name, uint32 openMode = B_READ_ONLY,
directory_which which = B_USER_SETTINGS_DIRECTORY, BVolume* volume = NULL)
{
if (file == NULL)
return B_BAD_VALUE;
BPath path;
status_t status = find_directory(which, &path, true, volume);
if (status != B_OK)
return status;
status = path.Append(PREFS_FILE);
if (status != B_OK)
return status;
status = file->SetTo(path.Path(), openMode);
if (status != B_OK)
return status;
status = file->InitCheck();
if (status != B_OK)
return status;
return B_OK;
}
+147
View File
@@ -0,0 +1,147 @@
/*
* Copyright (c) 1998-2007 Matthijs Hollemans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef MODEL_H
#define MODEL_H
#include <Entry.h>
#include <FindDirectory.h>
#include <List.h>
#include <Looper.h>
#include <Menu.h>
#include <Message.h>
#include <Rect.h>
#include <String.h>
#define PREFS_FILE "TrackerGrepSettings"
#define HISTORY_LIMIT 20
#define TRACKER_SIGNATURE "application/x-vnd.Be-TRAK"
#define PE_SIGNATURE "application/x-vnd.beunited.pe"
enum {
MSG_START_CANCEL = 1000,
MSG_RECURSE_LINKS,
MSG_RECURSE_DIRS,
MSG_SKIP_DOT_DIRS,
MSG_CASE_SENSITIVE,
MSG_ESCAPE_TEXT,
MSG_TEXT_ONLY,
MSG_INVOKE_PE,
MSG_MENU_SHOW_LINES,
MSG_CHECKBOX_SHOW_LINES,
MSG_SEARCH_TEXT,
MSG_INVOKE_ITEM,
MSG_SELECT_HISTORY,
MSG_REPORT_FILE_NAME,
MSG_REPORT_RESULT,
MSG_REPORT_ERROR,
MSG_SEARCH_FINISHED,
MSG_NEW_WINDOW,
MSG_OPEN_PANEL,
MSG_REFS_RECEIVED,
MSG_TRY_QUIT,
MSG_QUIT_NOW,
MSG_TRIM_SELECTION,
MSG_COPY_TEXT,
MSG_SELECT_IN_TRACKER,
MSG_SELECT_ALL,
MSG_OPEN_SELECTION
};
enum state_t {
STATE_IDLE = 0,
STATE_SEARCH,
STATE_CANCEL
};
class Model {
public:
Model();
status_t LoadPrefs();
status_t SavePrefs();
void AddToHistory(const char* text);
void FillHistoryMenu(BMenu* menu);
// The directory we were invoked from.
entry_ref fDirectory;
// The selected files we were invoked upon.
BMessage fSelectedFiles;
// Whether we need to look into subdirectories.
bool fRecurseDirs;
// Whether we need to follow symbolic links.
bool fRecurseLinks;
// Whether we should skip subdirectories that start with a dot.
bool fSkipDotDirs;
// Whether the search is case sensitive.
bool fCaseSensitive;
// Whether the search pattern will be escaped.
bool fEscapeText;
// Whether we look at text files only.
bool fTextOnly;
// Whether we open the item in Pe and jump to the correct line.
bool fInvokePe;
// Whether to show the contents of matching files.
bool fShowContents;
// The dimensions of the window.
BRect fFrame;
// The looper that will receive notifications.
BLooper* fTarget;
// What are we doing.
state_t fState;
// Current directory of the filepanel
BString fFilePanelPath;
// Grep string encoding ?
uint32 fEncoding;
private:
BList* _LoadHistory();
status_t _SaveHistory(BList* items);
void _FreeHistory(BList* items);
status_t _OpenFile(BFile* file, const char* name,
uint32 openMode = B_READ_ONLY,
directory_which which
= B_USER_SETTINGS_DIRECTORY,
BVolume* volume = NULL);
};
#endif // MODEL_H
@@ -0,0 +1,86 @@
/*
* Copyright (c) 1998-2007 Matthijs Hollemans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "GrepApp.h"
#include <AppFileInfo.h>
#include <Entry.h>
#include <Roster.h>
#include <TrackerAddOn.h>
#include "GlobalDefs.h"
int
main()
{
GrepApp app;
app.Run();
return 0;
}
void
process_refs(entry_ref dirRef, BMessage *message, void* /*reserved*/)
{
// Tracker calls this function when the user invokes the add-on.
// "dir_ref" contains the entry_ref of the current directory.
// "message" is a standard B_REFS_RECEIVED BMessage whose "refs"
// array contains the entry_refs of the selected files. The last
// argument, "reserved", is currently unused.
// This version of TrackerGrep is a Tracker add-on, but primarily
// it is a stand-alone application. The add-on launches the app
// on the set of files you had selected in Tracker. That way you
// get the benefits of the Tracker add-on with the benefits of the
// stand-alone application.
message->AddRef("dir_ref", &dirRef);
// get the path of the Tracker add-on
image_info image;
int32 cookie = 0;
status_t status = B_OK;
status = get_next_image_info(0, &cookie, &image);
while (status == B_OK) {
if (((char*)process_refs >= (char*)image.text
&& (char*)process_refs <= (char*)image.text + image.text_size)
|| ((char*)process_refs >= (char*)image.data
&& (char*)process_refs <= (char*)image.data + image.data_size))
break;
status = get_next_image_info(0, &cookie, &image);
}
entry_ref addonRef;
if (get_ref_for_path(image.name, &addonRef) == B_OK) {
// It's better to launch the application by its entry
// than by its application signature. There may be
// multiple instances and we would not be certain
// that the desired one is launched - the one which was
// loaded into Tracker and whose process_refs() was called.
be_roster->Launch(&addonRef, message);
} else
be_roster->Launch(APP_SIGNATURE, message);
}
@@ -0,0 +1,46 @@
/*
* TextSearch.rdef
*/
resource app_signature "application/x-vnd.mahlzeit.trackergrep";
resource app_version {
major = 5,
middle = 1,
minor = 1,
variety = B_APPV_FINAL,
internal = 0,
short_info = "Context search using Grep",
long_info = "Context search in Tracker, a GUI wrapper of /bin/grep."
};
resource app_flags B_SINGLE_LAUNCH;
resource file_types message {
"types" = "text",
"types" = "application/x-vnd.Be-directory",
"types" = "application/x-vnd.Be-symlink"
};
resource vector_icon {
$"6E6369660A0200060338D2F73CD163BF82B23B84A94B88504870C900FFEFA5BD"
$"FFFCC0FFFFF8900501020106023E49240000000000003CAAAA4940004A3000FF"
$"FFFCC07CF1B706040192020016023A55A6BAC2293F0DA33E958646C2EB47A1D6"
$"0001FF9E03010000020012023B98000000000000003C44004AD8004ADA000001"
$"0FFF0180020106023C00000000000000003C00004A30004A500000DCF4FFFF60"
$"94AA02001603360FF5B7B2B23AD6A4392A794ABC0B4AF035FF55C285005005FF"
$"0E0606AE0BB40BBF4D33C3AFB75DC173BDEFC607C13EC804CA28BD82C118B920"
$"C51BBB40BF07B8083AB6BC0605AE02B57D3EB9B9C3EFB7BB44BBB751BD75C936"
$"CA8EC1B1402F0A093B593D5BBFCDC93E455BC516C5F160465B435D4544510A04"
$"5A425E3F5A3D574008022E40BDB53308023142BE34BC0308023444C0E5BB5108"
$"023746C0D8BD7508023A48C270BDB508023D4A4CBD820802404CC408BFE60605"
$"7A024658565D5B5CCA15CB54CB0FCA5A584A540204423AC2BF3ABE583A384438"
$"BF2438C38B424EBE584EC2BF4E4C444CC38B4CBF240606BA0A4C51565B585AC9"
$"1CCA4EC983C9E959584F4E4B4D0E0A03020203000A0101011001178400040A02"
$"0101000A0101001001178400040A000100000A04040604080A1815FF01178100"
$"040A04040604080A18001501178200040A04030907051815FF01178100040A06"
$"010B000A05010D1001178400040A08010D000A05010C1001178400040A07010C"
$"000A09010C023C00000000000000003C000048B00048F000"
};
@@ -0,0 +1,15 @@
/*
* Public domain, baby!
*
* Localization for Zeta users
*
*/
#ifndef TRANSLATION_H
#define TRANSLATION_H
#ifndef _T
# define _T(x) x
#endif
#endif // TRANSLATION_H