#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#include "tags.h"
#include "textBuf.h"
#include "text.h"
#include "nedit.h"
#include "window.h"
#include "file.h"
#include "preferences.h"
#include "search.h"
#include "selection.h"
#include "calltips.h"
#include "../util/DialogF.h"
#include "../util/fileUtils.h"
#include "../util/misc.h"
#include "../util/utils.h"
#include "../util/refString.h"
#include "../util/nedit_malloc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/param.h>
#include <Xm/PrimitiveP.h>
#include <Xm/Xm.h>
#include <Xm/SelectioB.h>
#include <X11/Xatom.h>
#ifdef HAVE_DEBUG_H
#include "../debug.h"
#endif
#define MAXLINE 2048
#define MAX_TAG_LEN 256
#define MAXDUPTAGS 100
#define MAX_TAG_INCLUDE_RECURSION_LEVEL 5
#define TIP_DEFAULT_LINES 4
#define STRSAVE(a) (NEditStrdup(a!=
NULL?a:
""))
typedef struct _tag {
struct _tag *next;
const char *path;
const char *name;
const char *file;
const char *searchString;
int posInf;
short language;
short index;
} tag;
enum searchDirection {
FORWARD,
BACKWARD};
static int loadTagsFile(
const char *tagSpec,
int index,
int recLevel);
static void findDefCB(Widget widget, XtPointer closure, Atom *sel,
Atom *type, XtPointer value,
unsigned long *length,
int *format);
static void setTag(tag *t,
const char *name,
const char *file,
int language,
const char *searchString,
int posInf,
const char * tag);
static int fakeRegExSearch(WindowInfo *window,
char *buffer,
const char *searchString,
int *startPos,
int *endPos);
static void updateMenuItems(
void);
static int addTag(
const char *name,
const char *file,
int lang,
const char *search,
int posInf,
const char *path,
int index);
static int delTag(
const char *name,
const char *file,
int lang,
const char *search,
int posInf,
int index);
static tag *getTag(
const char *name,
int search_type);
static int findDef(WindowInfo *window,
const char *value,
int search_type);
static int findAllMatches(WindowInfo *window,
const char *string);
static void findAllCB(Widget parent, XtPointer client_data, XtPointer call_data);
static Widget createSelectMenu(Widget parent,
char *label,
int nArgs,
char *args[]);
static void editTaggedLocation( Widget parent,
int i );
static void showMatchingCalltip( Widget parent,
int i );
static int searchLine(
char *line,
const char *regex);
static void rstrip(
char *dst,
const char *src );
static int nextTFBlock(
FILE *fp,
char *header,
char **tiptext,
int *lineAt,
int *lineNo);
static int loadTipsFile(
const char *tipsFile,
int index,
int recLevel);
static tag **Tags =
NULL;
static unsigned DefTagHashSize = 100000u;
tagFile *TagsFileList =
NULL;
static tag **Tips =
NULL;
tagFile *TipsFileList =
NULL;
static int searchMode =
TAG;
static const char *tagName;
static char tagFiles[
MAXDUPTAGS][
MAXPATHLEN];
static char tagSearch[
MAXDUPTAGS][
MAXPATHLEN];
static int tagPosInf[
MAXDUPTAGS];
static Boolean globAnchored;
static int globPos;
static int globHAlign;
static int globVAlign;
static int globAlignMode;
static int tagsShowCalltip( WindowInfo *window,
char *text ) {
if (text)
return ShowCalltip( window, text, globAnchored, globPos, globHAlign,
globVAlign, globAlignMode);
else
return 0;
}
static tagFile *setFileListHead(tagFile *t,
int file_type )
{
if (file_type ==
TAG)
TagsFileList = t;
else
TipsFileList = t;
return t;
}
static tag *getTag(
const char *name,
int search_type)
{
static char lastName[
MAXLINE];
static tag *t =
NULL;
tag **table;
if (search_type ==
TIP)
table = Tips;
else
table = Tags;
if (table ==
NULL)
return NULL;
if (name) {
unsigned const addr = StringHashAddr(name) % DefTagHashSize;
t = table[addr];
strcpy(lastName,name);
}
else if (t) {
name = lastName;
t = t->next;
}
else return NULL;
for (;t; t = t->next)
if (!strcmp(name,t->name))
return t;
return NULL;
}
static tag* matchTagRec(tag
const* tpl, tag* bkt)
{
tag *t = bkt;
for (; t; t = t->next)
if ((tpl->language == t->language) && (tpl->posInf == t->posInf) &&
(tpl->name == t->name) && (tpl->searchString == t->searchString) &&
(tpl->file == t->file))
return t;
return NULL;
}
static int addTag(
const char *name,
const char *file,
int lang,
const char *search,
int posInf,
const char *path,
int index)
{
char newfile[
MAXPATHLEN];
tag **table;
if (searchMode ==
TIP) {
if (Tips ==
NULL)
Tips = (tag **)NEditCalloc(DefTagHashSize,
sizeof(tag*));
table = Tips;
}
else {
if (Tags ==
NULL)
Tags = (tag **)NEditCalloc(DefTagHashSize,
sizeof(tag*));
table = Tags;
}
if (*file ==
'/')
strncpy(newfile, file,
MAXPATHLEN);
else
snprintf(newfile,
MAXPATHLEN,
"%s%s", path, file);
newfile[
MAXPATHLEN -
1] =
'\0';
NormalizePathname(newfile);
{
tag tpl;
setTag(&tpl, name, newfile, lang, search, posInf, path);
{
unsigned const addr = StringHashAddr(tpl.name) % DefTagHashSize;
tag *t = matchTagRec(&tpl, table[addr]);
if (t)
return 0;
t = NEditNew(tag);
*t = tpl;
t->index = index;
t->next = table[addr];
table[addr] = t;
}
}
return 1;
}
static int delTag(
const char *name,
const char *file,
int lang,
const char *search,
int posInf,
int index)
{
tag *t, *last;
int start,finish,i,del=
0;
tag **table;
if (searchMode ==
TIP)
table = Tips;
else
table = Tags;
if (table ==
NULL)
return FALSE;
if (name)
start = finish = StringHashAddr(name) % DefTagHashSize;
else {
start =
0;
finish = DefTagHashSize;
}
for (i = start; i<finish; i++) {
for (last =
NULL, t = table[i]; t; last = t, t = t?t->next:table[i]) {
if (index && index != t->index)
continue;
if (posInf != t->posInf)
continue;
if (lang >=
PLAIN_LANGUAGE_MODE && lang != t->language)
continue;
if (name && strcmp(name,t->name))
continue;
if (file && strcmp(file,t->file))
continue;
if (search && strcmp(search,t->searchString))
continue;
if (last)
last->next = t->next;
else
table[i] = t->next;
RefStringFree(t->name);
RefStringFree(t->file);
RefStringFree(t->searchString);
RefStringFree(t->path);
NEditFree(t);
t =
NULL;
del++;
}
}
return del>
0;
}
static int tagFileIndex =
0;
int AddRelTagsFile(
const char *tagSpec,
const char *windowPath,
int file_type)
{
tagFile *t;
int added=
0;
struct stat statbuf;
char *filename;
char pathName[
MAXPATHLEN];
char *tmptagSpec;
tagFile *FileList;
searchMode = file_type;
if (searchMode ==
TAG)
FileList = TagsFileList;
else
FileList = TipsFileList;
tmptagSpec = NEditStrdup(tagSpec);
for (filename = strtok(tmptagSpec,
":"); filename; filename = strtok(
NULL,
":")){
if (*filename ==
'/' || *filename ==
'~')
continue;
if (windowPath && *windowPath) {
strcpy(pathName, windowPath);
}
else {
strcpy(pathName, GetCurrentDir());
}
strcat(pathName,
"/");
strcat(pathName, filename);
NormalizePathname(pathName);
for (t = FileList; t && strcmp(t->filename, pathName); t = t->next);
if (t) {
added=
1;
continue;
}
if (stat(pathName, &statbuf) !=
0)
continue;
t = (tagFile *) NEditMalloc(
sizeof(tagFile));
t->filename =
STRSAVE(pathName);
t->loaded =
0;
t->date = statbuf.st_mtime;
t->index = ++tagFileIndex;
t->next = FileList;
FileList = setFileListHead(t, file_type);
added=
1;
}
NEditFree(tmptagSpec);
updateMenuItems();
if (added)
return TRUE;
else
return FALSE;
}
int AddTagsFile(
const char *tagSpec,
int file_type)
{
tagFile *t;
int added=
1;
struct stat statbuf;
char *filename;
char pathName[
MAXPATHLEN];
char *tmptagSpec;
tagFile *FileList;
if (tagSpec ==
NULL) {
fprintf(stderr,
"xnedit: Internal Error!\n"
" Passed NULL pointer to AddTagsFile!\n");
return FALSE;
}
searchMode = file_type;
if (searchMode ==
TAG)
FileList = TagsFileList;
else
FileList = TipsFileList;
tmptagSpec = NEditStrdup(tagSpec);
for (filename = strtok(tmptagSpec,
":"); filename; filename = strtok(
NULL,
":")) {
if (*filename !=
'/') {
strcpy(pathName, GetCurrentDir());
strcat(pathName,
"/");
strcat(pathName,filename);
}
else {
strcpy(pathName,filename);
}
NormalizePathname(pathName);
for (t = FileList; t && strcmp(t->filename,pathName); t = t->next);
if (t) {
++(t->refcount);
added=
1;
continue;
}
if (stat(pathName,&statbuf) !=
0) {
added =
0;
continue;
}
t = (tagFile *) NEditMalloc(
sizeof(tagFile));
t->filename =
STRSAVE(pathName);
t->loaded =
0;
t->date = statbuf.st_mtime;
t->index = ++tagFileIndex;
t->next = FileList;
t->refcount =
1;
FileList = setFileListHead(t, file_type );
}
NEditFree(tmptagSpec);
updateMenuItems();
if (added)
return TRUE;
else
return FALSE;
}
int DeleteTagsFile(
const char *tagSpec,
int file_type, Boolean force_unload)
{
tagFile *t, *last;
tagFile *FileList;
char pathName[
MAXPATHLEN], *tmptagSpec, *filename;
int removed;
if (tagSpec ==
NULL) {
fprintf(stderr,
"xnedit: Internal Error: Passed NULL pointer to DeleteTagsFile!\n");
return FALSE;
}
searchMode = file_type;
if (searchMode ==
TAG)
FileList = TagsFileList;
else
FileList = TipsFileList;
tmptagSpec = NEditStrdup(tagSpec);
removed=
1;
for (filename = strtok(tmptagSpec,
":"); filename;
filename = strtok(
NULL,
":")) {
if (*filename !=
'/') {
strcpy(pathName, GetCurrentDir());
strcat(pathName,
"/");
strcat(pathName,filename);
}
else {
strcpy(pathName,filename);
}
NormalizePathname(pathName);
for (last=
NULL,t = FileList; t; last = t,t = t->next) {
if (strcmp(t->filename, pathName))
continue;
if (searchMode ==
TIP && !force_unload && --t->refcount >
0) {
break;
}
if (t->loaded)
delTag(
NULL,
NULL,-
2,
NULL,-
2,t->index);
if (last) last->next = t->next;
else FileList = setFileListHead(t->next, file_type);
NEditFree(t->filename);
NEditFree(t);
updateMenuItems();
break;
}
if (!t)
removed =
0;
}
if (removed)
return TRUE;
else
return FALSE;
}
static void updateMenuItems(
void)
{
WindowInfo *w;
Boolean tipStat=
FALSE, tagStat=
FALSE;
if (TipsFileList) tipStat=
TRUE;
if (TagsFileList) tagStat=
TRUE;
for (w=WindowList; w!=
NULL; w=w->next) {
if (!IsTopDocument(w))
continue;
XtSetSensitive(w->showTipItem, tipStat || tagStat);
XtSetSensitive(w->unloadTipsMenuItem, tipStat);
XtSetSensitive(w->findDefItem, tagStat);
XtSetSensitive(w->unloadTagsMenuItem, tagStat);
}
}
static int scanCTagsLine(
char *line,
const char *tagPath,
int index)
{
char *name = line, *searchString =
NULL;
char *file =
NULL;
char *posTagREEnd =
NULL, *posTagRENull =
NULL;
int pos;
if ((*name !=
'!') && (file = strchr(name,
'\t')) !=
NULL)
{
*file++ =
'\0';
if ((searchString = strchr(file,
'\t')) !=
NULL)
*searchString++ =
'\0';
else return 0;
}
else return 0;
if(searchString[
0] ==
'/' || searchString[
0] ==
'?') {
pos=-
1;
posTagREEnd = strrchr(searchString,
';');
posTagRENull = strchr(searchString,
0);
if(!posTagREEnd || (posTagREEnd[
1] !=
'""') ||
(posTagRENull[-
1] == searchString[
0])) {
posTagREEnd = posTagRENull;
}
else {
*posTagREEnd =
0;
}
if(posTagREEnd > (searchString+
2)) {
posTagREEnd--;
if(searchString[
0] == *posTagREEnd)
*posTagREEnd=
0;
}
}
else {
pos=atoi(searchString);
*searchString=
0;
}
return addTag(name, file,
PLAIN_LANGUAGE_MODE, searchString, pos, tagPath,
index);
}
static int scanETagsLine(
const char *line,
const char * tagPath,
int index,
char * file,
int recLevel)
{
char name[
MAXLINE], searchString[
MAXLINE];
char incPath[
MAXPATHLEN];
int pos, len;
char *posDEL, *posSOH, *posCOM;
if(line[
0]==
12) {
*file=
0;
return 0;
}
posDEL=strchr(line,
'\177');
posSOH=strchr(line,
'\001');
posCOM=strrchr(line,
',');
if(*file && posDEL && (posSOH > posDEL) && (posCOM > posSOH)) {
len=Min(
MAXLINE-
1, posDEL - line);
strncpy(searchString, line, len);
searchString[len]=
0;
len=Min(
MAXLINE-
1, (posSOH - posDEL) -
1);
strncpy(name, posDEL +
1, len);
name[len]=
0;
pos=atoi(posCOM+
1);
return addTag(name, file,
PLAIN_LANGUAGE_MODE, searchString, pos,
tagPath, index);
}
if (*file && posDEL && (posCOM > posDEL)) {
len=Min(
MAXLINE-
1, posDEL - line);
strncpy(searchString, line, len);
searchString[len]=
0;
while(--len >=
0) {
if( isalnum((
unsigned char)searchString[len]) ||
(searchString[len] ==
'_'))
break;
}
if(len<
0)
return 0;
pos=len;
while (pos >=
0 && (isalnum((
unsigned char)searchString[pos]) ||
(searchString[pos] ==
'_')))
pos--;
strncpy(name, searchString + pos +
1, len - pos);
name[len - pos] =
0;
pos=atoi(posCOM+
1);
return addTag(name, file,
PLAIN_LANGUAGE_MODE, searchString, pos,
tagPath, index);
}
if(*line && posCOM) {
len=Min(
MAXPATHLEN-
1, posCOM - line);
strncpy(file, line, len);
file[len]=
0;
if(!(strncmp(posCOM+
1,
"include",
7))) {
if(*file !=
'/') {
if((strlen(tagPath) + strlen(file)) >=
MAXPATHLEN) {
fprintf(stderr,
"tags.c: MAXPATHLEN overflow\n");
*file=
0;
return 0;
}
strcpy(incPath, tagPath);
strcat(incPath, file);
CompressPathname(incPath);
return(loadTagsFile(incPath, index, recLevel+
1));
}
else {
return(loadTagsFile(file, index, recLevel+
1));
}
}
}
return 0;
}
typedef enum {
TFT_CHECK,
TFT_ETAGS,
TFT_CTAGS
}
TFT;
static int loadTagsFile(
const char *tagsFile,
int index,
int recLevel)
{
FILE *fp =
NULL;
char line[
MAXLINE];
char file[
MAXPATHLEN], tagPath[
MAXPATHLEN];
char resolvedTagsFile[
MAXPATHLEN+
1];
int nTagsAdded=
0;
int tagFileType =
TFT_CHECK;
if(recLevel >
MAX_TAG_INCLUDE_RECURSION_LEVEL) {
return 0;
}
if(!ResolvePath(tagsFile, resolvedTagsFile)) {
return 0;
}
if ((fp = fopen(resolvedTagsFile,
"r")) ==
NULL) {
return 0;
}
ParseFilename(resolvedTagsFile,
NULL, tagPath);
while (fgets(line,
MAXLINE, fp)) {
AllWindowsBusy(
"Loading tags file...");
if(tagFileType==
TFT_CHECK) {
if(line[
0]==
12)
tagFileType=
TFT_ETAGS;
else
tagFileType=
TFT_CTAGS;
}
if(tagFileType==
TFT_CTAGS) {
nTagsAdded += scanCTagsLine(line, tagPath, index);
}
else {
nTagsAdded += scanETagsLine(line, tagPath, index, file, recLevel);
}
}
fclose(fp);
AllWindowsUnbusy();
return nTagsAdded;
}
#define TAG_STS_ERR_FMT "NEdit: Error getting status for tag file %s\n"
int LookupTag(
const char *name,
const char **file,
int *language,
const char **searchString,
int * pos,
const char **path,
int search_type)
{
tag *t;
tagFile *tf;
struct stat statbuf;
tagFile *FileList;
int load_status;
searchMode = search_type;
if (searchMode ==
TIP)
FileList = TipsFileList;
else
FileList = TagsFileList;
for (tf = FileList; tf && name; tf = tf->next) {
if (tf->loaded) {
if (stat(tf->filename,&statbuf) !=
0) {
fprintf(stderr,
TAG_STS_ERR_FMT, tf->filename);
}
else {
if (tf->date == statbuf.st_mtime) {
continue;
}
}
delTag(
NULL,
NULL,-
2,
NULL,-
2,tf->index);
}
if (FileList == TipsFileList)
load_status = loadTipsFile(tf->filename, tf->index,
0);
else
load_status = loadTagsFile(tf->filename, tf->index,
0);
if(load_status) {
if (stat(tf->filename,&statbuf) !=
0) {
if(!tf->loaded) {
fprintf(stderr,
TAG_STS_ERR_FMT, tf->filename);
}
}
else {
tf->date = statbuf.st_mtime;
}
tf->loaded =
1;
}
else {
tf->loaded =
0;
}
}
t = getTag(name, search_type);
if (!t) {
return FALSE;
}
else {
*file = t->file;
*language = t->language;
*searchString = t->searchString;
*pos = t->posInf;
*path = t->path;
return TRUE;
}
}
static int findDef(WindowInfo *window,
const char *value,
int search_type) {
static char tagText[
MAX_TAG_LEN +
1];
const char *p;
char message[
MAX_TAG_LEN+
40];
int l, ml, status =
0;
searchMode = search_type;
l = strlen(value);
if (l <=
MAX_TAG_LEN) {
for (p = value; *p && isascii(*p); p++) {
}
if (!(*p)) {
ml = ((l <
MAX_TAG_LEN) ? (l) : (
MAX_TAG_LEN));
strncpy(tagText, value, ml);
tagText[ml] =
'\0';
status = findAllMatches(window, tagText);
if (status ==
0 && search_type ==
TIP && TagsFileList !=
NULL) {
searchMode =
TIP_FROM_TAG;
status = findAllMatches(window, tagText);
}
if (status ==
0) {
if (searchMode ==
TIP_FROM_TAG || searchMode ==
TIP) {
sprintf(message,
"No match for \"%s\" in calltips or tags.",
tagName);
tagsShowCalltip( window, message );
}
else
{
DialogF(
DF_WARN, window->textArea,
1,
"Tags",
"\"%s\" not found in tags file%s",
"OK", tagName,
(TagsFileList && TagsFileList->next) ?
"s" :
"");
}
}
}
else {
fprintf(stderr,
"NEdit: Can''t handle non 8-bit text\n");
XBell(TheDisplay,
0);
}
}
else {
fprintf(stderr,
"NEdit: Tag Length too long.\n");
XBell(TheDisplay,
0);
}
return status;
}
static void findDefinitionHelper(WindowInfo *window, Time time,
const char *arg,
int search_type)
{
if(arg)
{
findDef(window, arg, search_type);
}
else
{
searchMode = search_type;
XtGetSelectionValue(window->textArea,
XA_PRIMARY,
XA_STRING,
findDefCB, window, time);
}
}
void FindDefinition(WindowInfo *window, Time time,
const char *arg)
{
findDefinitionHelper(window, time, arg,
TAG);
}
void FindDefCalltip(WindowInfo *window, Time time,
const char *arg)
{
globAnchored = False;
globPos = -
1;
globHAlign =
TIP_LEFT;
globVAlign =
TIP_BELOW;
globAlignMode =
TIP_SLOPPY;
findDefinitionHelper(window, time, arg,
TIP);
}
static void findDefCB(Widget widget, XtPointer closure, Atom *sel,
Atom *type, XtPointer v,
unsigned long *length,
int *format)
{
WindowInfo *window = closure;
char *value = v;
if (*type ==
XT_CONVERT_FAIL || value ==
NULL) {
XBell(TheDisplay,
0);
}
else {
findDef(window, value, searchMode);
}
NEditFree(value);
}
int ShowTipString(WindowInfo *window,
char *text, Boolean anchored,
int pos, Boolean lookup,
int search_type,
int hAlign,
int vAlign,
int alignMode) {
if (search_type ==
TAG)
return 0;
globAnchored = anchored;
globPos = pos;
globHAlign = hAlign;
globVAlign = vAlign;
globAlignMode = alignMode;
if (!lookup)
return tagsShowCalltip(window, text);
else
return findDef(window, text, search_type);
}
static void setTag(tag *t,
const char *name,
const char *file,
int language,
const char *searchString,
int posInf,
const char *path)
{
t->name = RefStringDup(name);
t->file = RefStringDup(file);
t->searchString = RefStringDup(searchString);
t->path = RefStringDup(path);
t->language = language;
t->posInf = posInf;
}
static int fakeRegExSearch(WindowInfo *window,
char *in_buffer,
const char *searchString,
int *startPos,
int *endPos)
{
int found, searchStartPos, dir, ctagsMode;
char searchSubs[
3*
MAXLINE+
3], *outPtr;
const char *fileString, *inPtr;
if (in_buffer ==
NULL) {
fileString = BufAsString(window->buffer);
}
else {
fileString = in_buffer;
}
if (*startPos != -
1) {
dir =
SEARCH_FORWARD;
searchStartPos = *startPos;
ctagsMode=
0;
}
else if (searchString[
0] ==
'/') {
dir =
SEARCH_FORWARD;
searchStartPos =
0;
ctagsMode=
1;
}
else if (searchString[
0] ==
'?') {
dir =
SEARCH_BACKWARD;
searchStartPos = strlen(fileString);
ctagsMode=
1;
}
else {
fprintf(stderr,
"NEdit: Error parsing tag file search string");
return FALSE;
}
outPtr=searchSubs;
if(ctagsMode) {
inPtr=searchString+
1;
if(*inPtr ==
'^') {
*outPtr++ = *inPtr++;
}
}
else {
inPtr=searchString;
}
while(*inPtr) {
if( (*inPtr==
'\\' && inPtr[
1]==
'/') ||
(*inPtr==
'\r' && inPtr[
1]==
'$' && !inPtr[
2])
) {
inPtr++;
}
else if(strchr(
"()-[]<>{}.|^*+?&\\", *inPtr)
|| (*inPtr ==
'$' && (inPtr[
1]||(!ctagsMode)))){
*outPtr++ =
'\\';
*outPtr++ = *inPtr++;
}
else if (isspace((
unsigned char)*inPtr)) {
*outPtr++ =
'\\';
*outPtr++ =
's';
*outPtr++ =
'+';
do { inPtr++ ; }
while(isspace((
unsigned char)*inPtr));
}
else {
*outPtr++ = *inPtr++;
}
}
*outPtr=
0;
found = SearchString(fileString, searchSubs, dir,
SEARCH_REGEX,
False, searchStartPos, startPos, endPos,
NULL,
NULL,
NULL);
if(!found && !ctagsMode) {
found = SearchString(fileString, searchSubs,
SEARCH_BACKWARD,
SEARCH_REGEX, False, searchStartPos, startPos, endPos,
NULL,
NULL,
NULL);
}
if (found) {
return TRUE;
}
else {
XBell(TheDisplay,
0);
return FALSE;
}
}
static int findAllMatches(WindowInfo *window,
const char *string)
{
Widget dialogParent = window->textArea;
char filename[
MAXPATHLEN], pathname[
MAXPATHLEN];
char temp[
32+
2*
MAXPATHLEN+
MAXLINE];
const char *fileToSearch, *searchString, *tagPath;
char **dupTagsList;
int startPos, i, pathMatch=
0, samePath=
0, langMode, nMatches=
0;
if (*string ==
'\0' || strlen(string) >
MAX_TAG_LEN) {
XBell(TheDisplay,
0);
return -
1;
}
tagName=string;
while (LookupTag(string, &fileToSearch, &langMode, &searchString, &startPos,
&tagPath, searchMode)) {
if (window->languageMode !=
PLAIN_LANGUAGE_MODE &&
GetPrefSmartTags() && langMode !=
PLAIN_LANGUAGE_MODE &&
langMode != window->languageMode) {
string=
NULL;
continue;
}
if (*fileToSearch ==
'/')
strcpy(tagFiles[nMatches], fileToSearch);
else
sprintf(tagFiles[nMatches],
"%s%s",tagPath,fileToSearch);
strcpy(tagSearch[nMatches],searchString);
tagPosInf[nMatches]=startPos;
ParseFilename(tagFiles[nMatches], filename, pathname);
if (GetPrefSmartTags() && !strcmp(window->filename,filename)
&& !strcmp(window->path,pathname) ) {
if (nMatches) {
strcpy(tagFiles[
0],tagFiles[nMatches]);
strcpy(tagSearch[
0],tagSearch[nMatches]);
tagPosInf[
0]=tagPosInf[nMatches];
}
nMatches =
1;
break;
}
if (!strcmp(window->path,pathname)) {
samePath++;
pathMatch=nMatches;
}
if (++nMatches >=
MAXDUPTAGS) {
DialogF(
DF_WARN, dialogParent,
1,
"Tags",
"Too many duplicate tags, first %d shown",
"OK",
MAXDUPTAGS);
break;
}
string =
NULL;
}
if (!nMatches) {
return 0;
}
if (GetPrefSmartTags() && samePath ==
1 && nMatches >
1) {
strcpy(tagFiles[
0],tagFiles[pathMatch]);
strcpy(tagSearch[
0],tagSearch[pathMatch]);
tagPosInf[
0]=tagPosInf[pathMatch];
nMatches =
1;
}
if (GetPrefSmartTags()) {
for (i=
1; i<nMatches; i++)
if (strcmp(tagFiles[i],tagFiles[i-
1]))
break;
if (i==nMatches)
nMatches =
1;
}
if (nMatches>
1) {
if (!(dupTagsList = (
char **) NEditMalloc(
sizeof(
char *) * nMatches))) {
fprintf(stderr,
"xnedit: findAllMatches(): out of heap space!\n");
XBell(TheDisplay,
0);
return -
1;
}
for (i=
0; i<nMatches; i++) {
ParseFilename(tagFiles[i], filename, pathname);
if ((i<nMatches-
1 && !strcmp(tagFiles[i],tagFiles[i+
1])) ||
(i>
0 && !strcmp(tagFiles[i],tagFiles[i-
1]))) {
if(*(tagSearch[i]) && (tagPosInf[i] != -
1)) {
sprintf(temp,
"%2d. %s%s %8i %s", i+
1, pathname,
filename, tagPosInf[i], tagSearch[i]);
}
else if (*(tagSearch[i])) {
sprintf(temp,
"%2d. %s%s %s", i+
1, pathname,
filename, tagSearch[i]);
}
else {
sprintf(temp,
"%2d. %s%s %8i", i+
1, pathname, filename,
tagPosInf[i]);
}
}
else {
sprintf(temp,
"%2d. %s%s",i+
1,pathname,filename);
}
if (
NULL == (dupTagsList[i] = (
char*) NEditMalloc(strlen(temp) +
1))) {
int j;
fprintf(stderr,
"xnedit: findAllMatches(): out of heap space!\n");
for (j = i -
1; j > -
1; j--) {
NEditFree(dupTagsList[j]);
}
NEditFree(dupTagsList);
XBell(TheDisplay,
0);
return -
1;
}
strcpy(dupTagsList[i],temp);
}
createSelectMenu(dialogParent,
"Duplicate Tags", nMatches, dupTagsList);
for (i=
0; i<nMatches; i++)
NEditFree(dupTagsList[i]);
NEditFree(dupTagsList);
return 1;
}
if (searchMode ==
TAG)
editTaggedLocation( dialogParent,
0 );
else
showMatchingCalltip( dialogParent,
0 );
return 1;
}
static void findAllCB(Widget parent, XtPointer client_data, XtPointer call_data)
{
int i;
char *eptr;
XmSelectionBoxCallbackStruct *cbs =
(XmSelectionBoxCallbackStruct *) call_data;
if (cbs->reason == XmCR_NO_MATCH)
return;
if (cbs->reason == XmCR_CANCEL) {
XtDestroyWidget(XtParent(parent));
return;
}
XmStringGetLtoR(cbs->value,XmFONTLIST_DEFAULT_TAG,&eptr);
if ((i = atoi(eptr)-
1) <
0) {
XBell(TheDisplay,
0);
return;
}
if (searchMode ==
TAG)
editTaggedLocation( parent, i );
else
showMatchingCalltip( parent, i );
if (cbs->reason == XmCR_OK)
XtDestroyWidget(XtParent(parent));
}
static void findAllCloseCB(Widget parent, XtPointer client_data,
XtPointer call_data)
{
XtDestroyWidget(parent);
}
static int moveAheadNLines(
char *str,
int *pos,
int n ) {
int i=n;
while (str[*pos] !=
'\0' && n>
0) {
if (str[*pos] ==
'\n')
--n;
++(*pos);
}
if (n==
0)
return -
1;
else
return i-n;
}
static void showMatchingCalltip( Widget parent,
int i )
{
int startPos=
0, fileLen, readLen, tipLen;
int endPos=
0;
char *fileString;
FILE *fp;
struct stat statbuf;
char *message;
NormalizePathname(tagFiles[i]);
fp = fopen(tagFiles[i],
"r");
if (fp ==
NULL) {
DialogF(
DF_ERR, parent,
1,
"Error opening File",
"Error opening %s",
"OK", tagFiles[i]);
return;
}
if (fstat(fileno(fp), &statbuf) !=
0) {
fclose(fp);
DialogF(
DF_ERR, parent,
1,
"Error opening File",
"Error opening %s",
"OK", tagFiles[i]);
return;
}
fileLen = statbuf.st_size;
fileString = (
char*)NEditMalloc(fileLen+
1);
if (fileString ==
NULL) {
fclose(fp);
DialogF(
DF_ERR, parent,
1,
"File too large",
"File is too large to load",
"OK");
return;
}
readLen = fread(fileString,
sizeof(
char), fileLen, fp);
if (ferror(fp)) {
fclose(fp);
DialogF(
DF_ERR, parent,
1,
"Error reading File",
"Error reading %s",
"OK", tagFiles[i]);
NEditFree(fileString);
return;
}
fileString[readLen] =
0;
if (fclose(fp) !=
0) {
DialogF(
DF_WARN, parent,
1,
"Error closing File",
"Unable to close file",
"OK");
}
if (!*(tagSearch[i])) {
if ((moveAheadNLines( fileString, &startPos, tagPosInf[i]-
1 )) >=
0) {
DialogF(
DF_ERR, parent,
1,
"Tags Error",
"%s\n not long enough for definition to be on line %d",
"OK", tagFiles[i], tagPosInf[i]);
NEditFree(fileString);
return;
}
}
else {
startPos = tagPosInf[i];
if(!fakeRegExSearch(WidgetToWindow(parent), fileString, tagSearch[i],
&startPos, &endPos)){
DialogF(
DF_WARN, parent,
1,
"Tag not found",
"Definition for %s\nnot found in %s",
"OK", tagName,
tagFiles[i]);
NEditFree(fileString);
return;
}
}
if (searchMode ==
TIP) {
int dummy, found;
endPos = startPos;
found = SearchString(fileString,
"\\n\\s*\\n",
SEARCH_FORWARD,
SEARCH_REGEX, False, startPos, &endPos, &dummy,
NULL,
NULL,
NULL);
if (!found) {
moveAheadNLines( fileString, &endPos,
TIP_DEFAULT_LINES );
--endPos;
}
}
else {
endPos = startPos;
moveAheadNLines( fileString, &endPos,
TIP_DEFAULT_LINES );
if (((
size_t) endPos) <= (strlen(fileString)-
5)) {
sprintf( &fileString[endPos],
". . ." );
endPos +=
5;
}
}
tipLen = endPos - startPos;
message = (
char*)malloc(tipLen+
1);
if (message ==
NULL)
{
DialogF(
DF_ERR, parent,
1,
"Out of Memory",
"Can''t allocate memory for calltip message",
"OK");
NEditFree(fileString);
return;
}
strncpy( message, &fileString[startPos], tipLen );
message[tipLen] =
0;
tagsShowCalltip( WidgetToWindow(parent), message );
NEditFree(message);
NEditFree(fileString);
}
static void editTaggedLocation( Widget parent,
int i )
{
int startPos, endPos, lineNum, rows;
char filename[
MAXPATHLEN], pathname[
MAXPATHLEN];
WindowInfo *windowToSearch;
WindowInfo *parentWindow = WidgetToWindow(parent);
ParseFilename(tagFiles[i],filename,pathname);
EditExistingFile(parentWindow, filename, pathname,
NULL,
NULL,
0,
NULL,
False,
NULL, GetPrefOpenInTab(), False);
windowToSearch = FindWindowWithFile(filename, pathname);
if (windowToSearch ==
NULL) {
DialogF(
DF_WARN, parent,
1,
"File not found",
"File %s not found",
"OK",
tagFiles[i]);
return;
}
startPos=tagPosInf[i];
if(!*(tagSearch[i])) {
SelectNumberedLine(windowToSearch, startPos);
return;
}
if(!fakeRegExSearch(windowToSearch,
NULL, tagSearch[i], &startPos,
&endPos)){
DialogF(
DF_WARN, windowToSearch->shell,
1,
"Tag Error",
"Definition for %s\nnot found in %s",
"OK", tagName,
tagFiles[i]);
return;
}
BufSelect(windowToSearch->buffer, startPos, endPos);
RaiseFocusDocumentWindow(windowToSearch, True);
lineNum = BufCountLines(windowToSearch->buffer,
0, startPos);
XtVaGetValues(windowToSearch->lastFocus, textNrows, &rows,
NULL);
TextSetScroll(windowToSearch->lastFocus, lineNum - rows/
4,
0);
TextSetCursorPos(windowToSearch->lastFocus, endPos);
}
static Widget createSelectMenu(Widget parent,
char *label,
int nArgs,
char *args[])
{
int i;
char tmpStr[
100];
Widget menu;
XmStringTable list;
XmString popupTitle;
int ac;
Arg csdargs[
20];
list = (XmStringTable) NEditMalloc(nArgs *
sizeof(XmString *));
for (i=
0; i<nArgs; i++)
list[i] = XmStringCreateSimple(args[i]);
sprintf(tmpStr,
"Select File With TAG: %s",tagName);
popupTitle = XmStringCreateSimple(tmpStr);
ac =
0;
XtSetArg(csdargs[ac], XmNlistLabelString, popupTitle); ac++;
XtSetArg(csdargs[ac], XmNlistItems, list); ac++;
XtSetArg(csdargs[ac], XmNlistItemCount, nArgs); ac++;
XtSetArg(csdargs[ac], XmNvisibleItemCount,
12); ac++;
XtSetArg(csdargs[ac], XmNautoUnmanage, False); ac++;
menu = CreateSelectionDialog(parent,label,csdargs,ac);
XtUnmanageChild(XmSelectionBoxGetChild(menu, XmDIALOG_TEXT));
XtUnmanageChild(XmSelectionBoxGetChild(menu, XmDIALOG_HELP_BUTTON));
XtUnmanageChild(XmSelectionBoxGetChild(menu, XmDIALOG_SELECTION_LABEL));
XtAddCallback(menu, XmNokCallback, (XtCallbackProc)findAllCB, menu);
XtAddCallback(menu, XmNapplyCallback, (XtCallbackProc)findAllCB, menu);
XtAddCallback(menu, XmNcancelCallback, (XtCallbackProc)findAllCB, menu);
AddMotifCloseCallback(XtParent(menu), findAllCloseCB,
NULL);
for (i=
0; i<nArgs; i++)
XmStringFree(list[i]);
NEditFree(list);
XmStringFree(popupTitle);
ManageDialogCenteredOnPointer(menu);
return menu;
}
enum tftoken_types {
TF_EOF,
TF_BLOCK,
TF_VERSION,
TF_INCLUDE,
TF_LANGUAGE,
TF_ALIAS,
TF_ERROR,
TF_ERROR_EOF };
static int searchLine(
char *line,
const char *regex) {
int dummy1, dummy2;
return SearchString(line, regex,
SEARCH_FORWARD,
SEARCH_REGEX,
False,
0, &dummy1, &dummy2,
NULL,
NULL,
NULL);
}
static Boolean lineEmpty(
const char *line) {
while (*line && *line !=
'\n') {
if (*line !=
' ' && *line !=
'\t')
return False;
++line;
}
return True;
}
static void rstrip(
char *dst,
const char *src ) {
int wsStart, dummy2;
if(SearchString(src,
"\\s*\\n",
SEARCH_FORWARD,
SEARCH_REGEX,
False,
0, &wsStart, &dummy2,
NULL,
NULL,
NULL)) {
if(dst != src)
memcpy(dst, src, wsStart);
dst[wsStart] =
0;
}
else
if(dst != src)
strcpy(dst, src);
}
static int nextTFBlock(
FILE *fp,
char *header,
char **body,
int *blkLine,
int *currLine)
{
const char *commenTF_regex =
"^\\s*\\* comment \\*\\s*$";
const char *version_regex =
"^\\s*\\* version \\*\\s*$";
const char *include_regex =
"^\\s*\\* include \\*\\s*$";
const char *language_regex =
"^\\s*\\* language \\*\\s*$";
const char *alias_regex =
"^\\s*\\* alias \\*\\s*$";
char line[
MAXLINE], *status;
int dummy1;
int code;
while(
1) {
while((status=fgets(line,
MAXLINE, fp))) {
++(*currLine);
if(!lineEmpty( line ))
break;
}
if(!status)
return TF_EOF;
if( !searchLine(line, commenTF_regex) )
break;
while((status=fgets(line,
MAXLINE, fp))) {
++(*currLine);
if(lineEmpty( line ))
break;
}
if(!status)
return TF_EOF;
}
dummy1 = searchLine(line, include_regex);
if( dummy1 || searchLine(line, alias_regex) ) {
int incLen, incPos, i, incLines;
if(dummy1)
code =
TF_INCLUDE;
else {
code =
TF_ALIAS;
status=fgets(line,
MAXLINE, fp);
++(*currLine);
if (!status)
return TF_ERROR_EOF;
if (lineEmpty( line )) {
fprintf( stderr,
"xnedit: Warning: empty ''* alias *'' "
"block in calltips file.\n" );
return TF_ERROR;
}
rstrip(header, line);
}
incPos = ftell(fp);
*blkLine = *currLine +
1;
if (incPos <
0)
return TF_ERROR;
while((status=fgets(line,
MAXLINE, fp)) || feof(fp)) {
++(*currLine);
if(feof(fp) || lineEmpty( line ))
break;
}
incLen = ftell(fp) - incPos;
incLines = *currLine - *blkLine;
--(*currLine);
if (incLines ==
0) {
fprintf( stderr,
"xnedit: Warning: empty ''* include *'' or"
" ''* alias *'' block in calltips file.\n" );
return TF_ERROR;
}
*body = (
char *)NEditMalloc(incLen+
1);
if (!*body)
return TF_ERROR;
*body[
0]=
0;
if (fseek(fp, incPos,
SEEK_SET) !=
0) {
NEditFree(*body);
return TF_ERROR;
}
for (i=
0; i<incLines; i++) {
status = fgets(line,
MAXLINE, fp);
if (!status) {
NEditFree(*body);
return TF_ERROR_EOF;
}
rstrip(line,line);
if(i)
strcat(*body,
":");
strcat(*body, line);
}
}
else if( searchLine(line, language_regex) ) {
status=fgets(line,
MAXLINE, fp);
++(*currLine);
if (!status)
return TF_ERROR_EOF;
if (lineEmpty( line )) {
fprintf( stderr,
"xnedit: Warning: empty ''* language *'' block in calltips file.\n" );
return TF_ERROR;
}
*blkLine = *currLine;
rstrip(header, line);
code =
TF_LANGUAGE;
}
else if( searchLine(line, version_regex) ) {
status=fgets(line,
MAXLINE, fp);
++(*currLine);
if (!status)
return TF_ERROR_EOF;
if (lineEmpty( line )) {
fprintf( stderr,
"xnedit: Warning: empty ''* version *'' block in calltips file.\n" );
return TF_ERROR;
}
*blkLine = *currLine;
rstrip(header, line);
code =
TF_VERSION;
}
else {
rstrip(header, line);
status=fgets(line,
MAXLINE, fp);
++(*currLine);
if (!status)
return TF_ERROR_EOF;
if (lineEmpty( line )) {
fprintf( stderr,
"xnedit: Warning: empty calltip block:\n"
" \"%s\"\n", header);
return TF_ERROR;
}
*blkLine = *currLine;
*body = strdup(line);
code =
TF_BLOCK;
}
dummy1 = *currLine;
while(fgets(line,
MAXLINE, fp)) {
++(*currLine);
if (lineEmpty( line ))
break;
}
if (dummy1+
1 < *currLine && code !=
TF_BLOCK) {
fprintf( stderr,
"xnedit: Warning: extra lines in language or version block ignored.\n" );
}
return code;
}
typedef struct _alias {
char *dest;
char *sources;
struct _alias *next;
} tf_alias;
static tf_alias *new_alias(
const char *dest,
char *sources) {
tf_alias *alias;
alias = (tf_alias *)NEditMalloc(
sizeof(tf_alias) );
if(!alias)
return NULL;
alias->dest = (
char*)NEditMalloc( strlen(dest)+
1 );
if(!(alias->dest))
return NULL;
strcpy( alias->dest, dest );
alias->sources = sources;
return alias;
}
static void free_alias_list(tf_alias *alias) {
tf_alias *tmp_alias;
while(alias) {
tmp_alias = alias->next;
NEditFree(alias->dest);
NEditFree(alias->sources);
NEditFree(alias);
alias = tmp_alias;
}
}
static int loadTipsFile(
const char *tipsFile,
int index,
int recLevel)
{
FILE *fp =
NULL;
char header[
MAXLINE];
char *body, *tipIncFile;
char tipPath[
MAXPATHLEN];
char resolvedTipsFile[
MAXPATHLEN+
1];
int nTipsAdded=
0, langMode =
PLAIN_LANGUAGE_MODE, oldLangMode;
int currLine=
0, code, blkLine;
tf_alias *aliases=
NULL, *tmp_alias;
if(recLevel >
MAX_TAG_INCLUDE_RECURSION_LEVEL) {
fprintf(stderr,
"xnedit: Warning: Reached recursion limit before loading calltips file:\n\t%s\n", tipsFile);
return 0;
}
#ifndef VMS
strncpy(tipPath, tipsFile,
MAXPATHLEN);
tipPath[
MAXPATHLEN -
1] =
'\0';
ExpandTilde(tipPath);
if(!ResolvePath(tipPath, resolvedTipsFile))
return 0;
#else
if(!ResolvePath(tipsFile, resolvedTipsFile))
return 0;
#endif
ParseFilename(resolvedTipsFile,
NULL, tipPath);
if ((fp = fopen(resolvedTipsFile,
"r")) ==
NULL)
return 0;
while(
1 ) {
code = nextTFBlock(fp, header, &body, &blkLine, &currLine);
if( code ==
TF_ERROR_EOF ) {
fprintf(stderr,
"xnedit: Warning: unexpected EOF in calltips file.\n");
break;
}
if( code ==
TF_EOF )
break;
switch (code) {
case TF_BLOCK:
nTipsAdded += addTag(header, resolvedTipsFile, langMode,
"",
blkLine, tipPath, index);
NEditFree( body );
break;
case TF_INCLUDE:
for(tipIncFile=strtok(body,
":"); tipIncFile;
tipIncFile=strtok(
NULL,
":")) {
nTipsAdded += loadTipsFile( tipIncFile, index, recLevel+
1);
}
NEditFree( body );
break;
case TF_LANGUAGE:
oldLangMode = langMode;
langMode = FindLanguageMode( header );
if (langMode ==
PLAIN_LANGUAGE_MODE &&
strcmp(header,
"Plain")) {
fprintf(stderr,
"xnedit: Error reading calltips file:\n\t%s\n"
"Unknown language mode: \"%s\"\n",
tipsFile, header);
langMode = oldLangMode;
}
break;
case TF_ERROR:
fprintf(stderr,
"xnedit: Warning: Recoverable error while "
"reading calltips file:\n \"%s\"\n",
resolvedTipsFile);
break;
case TF_ALIAS:
tmp_alias = aliases;
aliases = new_alias(header, body);
if( !aliases ) {
fprintf(stderr,
"xnedit: Can''t allocate memory for tipfile "
"alias in calltips file:\n \"%s\"\n",
resolvedTipsFile);
free_alias_list(tmp_alias);
fclose(fp);
return 0;
}
aliases->next = tmp_alias;
break;
default:
;
}
}
tmp_alias = aliases;
while (tmp_alias) {
tag *t;
char *src;
t = getTag(tmp_alias->dest,
TIP);
if (!t) {
fprintf(stderr,
"xnedit: Can''t find destination of alias \"%s\"\n"
" in calltips file:\n \"%s\"\n",
tmp_alias->dest, resolvedTipsFile);
}
else {
for(src=strtok(tmp_alias->sources,
":"); src; src=strtok(
NULL,
":"))
addTag(src, resolvedTipsFile, t->language,
"", t->posInf,
tipPath, index);
}
tmp_alias = tmp_alias->next;
}
free_alias_list(aliases);
fclose(fp);
return nTipsAdded;
}