#include "xedit.h"
#include "util.h"
#include "re.h"
#include <unistd.h>
typedef struct _TagsEntry TagsEntry;
typedef struct _RegexEntry RegexEntry;
struct _TagsEntry {
hash_key *symbol;
TagsEntry *next;
int nentries;
hash_entry **filenames;
char **patterns;
};
struct _RegexEntry {
hash_key *pattern;
RegexEntry *next;
re_cod regex;
};
struct _XeditTagsInfo {
hash_key *pathname;
XeditTagsInfo *next;
hash_table *entries;
hash_table *filenames;
hash_table *patterns;
Boolean visited;
Boolean incwd;
XeditTagsInfo *tags;
TagsEntry *entry;
int offset;
Widget textwindow;
XawTextPosition position;
};
static XeditTagsInfo *LoadTagsFile(char *tagsfile);
static XeditTagsInfo *DoLoadTagsFile(char *tagsfile, int length);
static void FindTagFirst(XeditTagsInfo *tags, char *symbol, int length);
static void FindTagNext(XeditTagsInfo *tags,
Widget window, XawTextPosition position);
static void FindTag(XeditTagsInfo *tags);
extern Widget texts[3];
static hash_table *ht_tags;
void
TagsAction(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
xedit_flist_item *item;
char buffer[1024];
XawTextPosition position, left, right;
XawTextBlock block;
int length;
Widget source;
source = XawTextGetSource(w);
item = FindTextSource(source, NULL);
if (item->tags == NULL)
SearchTagsFile(item);
if (item->tags) {
position = XawTextGetInsertionPoint(w);
XawTextGetSelectionPos(w, &left, &right);
if (right > left) {
XawTextSourceRead(source, left, &block, right - left);
length = block.length + 1;
if (length >= sizeof(buffer))
length = sizeof(buffer);
XmuSnprintf(buffer, length, "%s", block.ptr);
item->tags->textwindow = w;
item->tags->position = position;
FindTagFirst(item->tags, buffer, length - 1);
}
else
FindTagNext(item->tags, w, position);
}
else
Feep();
}
void
SearchTagsFile(xedit_flist_item *item)
{
if (app_resources.loadTags) {
char buffer[BUFSIZ];
char *ptr, *tagsfile;
int length;
Boolean exists;
FileAccess file_access;
tagsfile = NULL;
if (app_resources.tagsName[0] == '/')
tagsfile = ResolveName(app_resources.tagsName);
else {
if (item->filename[0] != '/') {
ptr = ResolveName(app_resources.tagsName);
strncpy(buffer, ptr ? ptr : "", sizeof(buffer));
}
else
strncpy(buffer, item->filename, sizeof(buffer));
buffer[sizeof(buffer) - 1] = '\0';
ptr = buffer + strlen(buffer);
for (;;) {
while (ptr > buffer && ptr[-1] != '/')
--ptr;
if (ptr <= buffer)
break;
length = ptr - buffer;
if (length >= sizeof(buffer))
length = sizeof(buffer);
strncpy(ptr, app_resources.tagsName,
sizeof(buffer) - length);
buffer[sizeof(buffer) - 1] = '\0';
tagsfile = ResolveName(buffer);
if (tagsfile != NULL) {
file_access = CheckFilePermissions(tagsfile, &exists);
if (exists &&
(file_access == READ_OK || file_access == WRITE_OK))
break;
else
tagsfile = NULL;
}
*--ptr = '\0';
}
}
if (tagsfile)
item->tags = LoadTagsFile(tagsfile);
else {
XeditPrintf("No tags file found."
" Run \"ctags -R\" to build a tags file.\n");
item->tags = NULL;
}
}
}
static void
FindTagFirst(XeditTagsInfo *tags, char *symbol, int length)
{
char *ptr;
TagsEntry *entry;
char buffer[BUFSIZ];
ptr = symbol;
while (*ptr) {
if (*ptr == ' ' || *ptr == '\t' || *ptr == '\n' || *ptr == '\r' ||
*ptr == '(' || *ptr == ')') {
Feep();
return;
}
ptr++;
}
tags->tags = tags;
entry = (TagsEntry *)hash_check(tags->entries, symbol, length);
if (entry == NULL) {
strncpy(buffer, tags->pathname->value, tags->pathname->length);
buffer[tags->pathname->length] = '\0';
ptr = buffer + tags->pathname->length - 1;
for (tags->tags = (XeditTagsInfo *)hash_iter_first(ht_tags);
tags->tags;
tags->tags = (XeditTagsInfo *)hash_iter_next(ht_tags))
tags->tags->visited = False;
tags->visited = True;
while (ptr > buffer && entry == NULL) {
--ptr;
while (ptr > buffer && ptr[-1] != '/')
--ptr;
if (ptr <= buffer)
break;
*ptr = '\0';
tags->tags = (XeditTagsInfo *)
hash_check(ht_tags, buffer, ptr - buffer);
if (tags->tags) {
tags->tags->visited = True;
entry = (TagsEntry *)
hash_check(tags->tags->entries, symbol, length);
}
}
if (entry == NULL) {
for (tags->tags = (XeditTagsInfo *)hash_iter_first(ht_tags);
tags->tags;
tags->tags = (XeditTagsInfo *)hash_iter_next(ht_tags)) {
if (tags->tags->visited == False) {
entry = (TagsEntry *)
hash_check(tags->tags->entries, symbol, length);
if (entry != NULL)
break;
}
}
}
if (entry == NULL) {
XeditPrintf("Symbol %s not in tags\n", symbol);
Feep();
return;
}
}
tags->entry = entry;
tags->offset = 0;
FindTag(tags);
}
static void
FindTagNext(XeditTagsInfo *tags, Widget window, XawTextPosition position)
{
if (window != tags->textwindow || position != tags->position)
Feep();
else {
if (tags->entry->nentries > 1) {
if (++tags->offset >= tags->entry->nentries)
tags->offset = 0;
FindTag(tags);
}
else
Feep();
}
}
static XeditTagsInfo *
LoadTagsFile(char *tagsfile)
{
XeditTagsInfo *tags;
int length;
if (ht_tags == NULL)
ht_tags = hash_new(11, NULL);
length = strlen(tagsfile) - strlen(app_resources.tagsName);
tags = (XeditTagsInfo *)hash_check(ht_tags, tagsfile, length);
return (tags ? tags : DoLoadTagsFile(tagsfile, length));
}
static XeditTagsInfo *
DoLoadTagsFile(char *tagsfile, int length)
{
char *ptr;
FILE *file;
XeditTagsInfo *tags;
TagsEntry *entry;
hash_entry *file_entry;
char buffer[BUFSIZ];
char *symbol, *filename, *pattern;
file = fopen(tagsfile, "r");
if (file) {
char *cwd;
tags = XtNew(XeditTagsInfo);
cwd = getcwd(buffer, sizeof(buffer));
tags->incwd = cwd &&
(strlen(cwd) == length - 1 &&
memcmp(cwd, tagsfile, length - 1) == 0);
tags->pathname = XtNew(hash_key);
tags->pathname->value = XtMalloc(length + 1);
tags->pathname->length = length;
memcpy(tags->pathname->value, tagsfile, length);
tags->pathname->value[length] = '\0';
tags->next = NULL;
tags->entries = hash_new(809, NULL);
tags->filenames = hash_new(31, NULL);
tags->patterns = hash_new(47, NULL);
tags->tags = tags;
tags->entry = NULL;
tags->offset = 0;
tags->textwindow = NULL;
tags->position = 0;
while (fgets(buffer, sizeof(buffer) - 1, file)) {
if (isspace(buffer[0]) || buffer[0] == '!')
continue;
symbol = ptr = buffer;
while (*ptr && !isspace(*ptr))
ptr++;
*ptr++ = '\0';
while (isspace(*ptr))
ptr++;
filename = ptr;
while (*ptr && !isspace(*ptr))
ptr++;
*ptr++ = '\0';
while (isspace(*ptr))
ptr++;
pattern = ptr;
if (*pattern == '/' || *pattern == '?') {
ptr++;
while (*ptr && *ptr != *pattern) {
if (*ptr == '\\') {
if (ptr[1] == *pattern || ptr[1] == '\\') {
memmove(ptr, ptr + 1, strlen(ptr));
}
else {
++ptr;
if (!*ptr)
break;
}
}
ptr++;
}
if (*ptr != *pattern)
continue;
++pattern;
if (*pattern == '^' && ptr[-1] == '$') {
++pattern;
--ptr;
}
}
else if (isdigit(*ptr)) {
while (isdigit(*ptr))
ptr++;
}
else
continue;
*ptr = '\0';
length = strlen(symbol);
entry = (TagsEntry *)hash_check(tags->entries,
symbol, length);
if (entry == NULL) {
entry = XtNew(TagsEntry);
entry->symbol = XtNew(hash_key);
entry->symbol->value = XtNewString(symbol);
entry->symbol->length = length;
entry->next = NULL;
entry->nentries = 0;
entry->filenames = NULL;
entry->patterns = NULL;
hash_put(tags->entries, (hash_entry *)entry);
}
length = strlen(filename);
file_entry = hash_check(tags->filenames, filename, length);
if (file_entry == NULL) {
file_entry = XtNew(hash_entry);
file_entry->key = XtNew(hash_key);
file_entry->key->value = XtNewString(filename);
file_entry->key->length = length;
file_entry->next = NULL;
hash_put(tags->filenames, file_entry);
}
if ((entry->nentries % 4) == 0) {
entry->filenames = (hash_entry **)
XtRealloc((char *)entry->filenames,
sizeof(hash_entry *) *
(entry->nentries + 4));
entry->patterns = (char **)
XtRealloc((char *)entry->patterns,
sizeof(char *) *
(entry->nentries + 4));
}
entry->filenames[entry->nentries] = file_entry;
entry->patterns[entry->nentries] = XtNewString(pattern);
++entry->nentries;
}
fclose(file);
hash_put(ht_tags, (hash_entry *)tags);
XeditPrintf("Tags file %s loaded\n", tagsfile);
}
else {
XeditPrintf("Failed to load tags file %s\n", tagsfile);
tags = NULL;
}
return (tags);
}
static void
FindTag(XeditTagsInfo *tags)
{
static String params[] = { "vertical", NULL };
char buffer[BUFSIZ];
char *pattern;
int length;
char *line;
char *text;
RegexEntry *regex;
re_mat match;
XawTextPosition position, left, right, last;
Widget source;
XawTextBlock block;
int size;
int lineno;
Boolean found;
xedit_flist_item *item;
Widget otherwindow;
XmuSnprintf(buffer, sizeof(buffer), "%s%s", tags->tags->pathname->value,
tags->entry->filenames[tags->offset]->key->value);
pattern = tags->entry->patterns[tags->offset];
if (isdigit(*pattern)) {
lineno = atoi(pattern);
regex = NULL;
}
else {
lineno = 0;
length = strlen(pattern);
regex = (RegexEntry *)hash_check(tags->patterns, pattern, length);
if (regex == NULL) {
regex = XtNew(RegexEntry);
regex->pattern = XtNew(hash_key);
regex->pattern->value = XtNewString(pattern);
regex->pattern->length = length;
regex->next = NULL;
if (recomp(®ex->regex, pattern, RE_NOSUB | RE_NOSPEC)) {
XeditPrintf("Failed to compile regex %s\n", pattern);
Feep();
return;
}
hash_put(tags->patterns, (hash_entry *)regex);
}
}
if (!XtIsManaged(texts[1]))
XtCallActionProc(textwindow, "split-window", NULL, params, 1);
XtCallActionProc(textwindow, "other-window", NULL, NULL, 0);
if (!LoadFileInTextwindow(tags->incwd ?
tags->entry->filenames[tags->offset]->key->value :
buffer, buffer))
return;
otherwindow = textwindow;
item = FindTextSource(XawTextGetSource(textwindow), NULL);
source = item->source;
left = XawTextSourceScan(source, 0, XawstAll, XawsdLeft, 1, True);
found = False;
if (lineno) {
right = RSCAN(left, lineno, False);
left = LSCAN(right, 1, False);
found = True;
}
else {
right = RSCAN(left, 1, True);
last = XawTextSourceScan(source, 0, XawstAll, XawsdRight, 1, True);
text = buffer;
size = sizeof(buffer);
for (;;) {
length = right - left;
match.rm_so = 0;
match.rm_eo = length;
XawTextSourceRead(source, left, &block, right - left);
if (block.length >= length)
line = block.ptr;
else {
if (length > size) {
if (text == buffer)
text = XtMalloc(length);
else
text = XtRealloc(text, length);
size = length;
}
line = text;
memcpy(line, block.ptr, block.length);
length = block.length;
for (position = left + length;
position < right;
position += block.length) {
XawTextSourceRead(source, position, &block, right - position);
memcpy(line + length, block.ptr, block.length);
length += block.length;
}
}
if (right < last ||
(right > left && line[match.rm_eo - 1] == '\n')) {
--match.rm_eo;
length = match.rm_eo;
}
if (reexec(®ex->regex, line, 1, &match, RE_STARTEND) == 0 &&
match.rm_eo > match.rm_so &&
match.rm_so == 0 && match.rm_eo == length) {
right = left + match.rm_so + (match.rm_eo - match.rm_so);
found = True;
break;
}
else if (right >= last) {
XeditPrintf("Failed to match regex %s\n", pattern);
Feep();
break;
}
else {
left = LSCAN(right + 1, 1, False);
right = RSCAN(left, 1, True);
}
}
if (text != buffer)
XtFree(text);
}
XtCallActionProc(otherwindow, "other-window", NULL, NULL, 0);
if (found) {
if (source != XawTextGetSource(tags->textwindow) ||
right < tags->position || left > tags->position) {
XawTextSetInsertionPoint(otherwindow, left);
XawTextSetSelection(otherwindow, left, right);
}
}
}