File: tags.c

package info (click to toggle)
nedit 5.02-2
  • links: PTS
  • area: non-free
  • in suites: hamm
  • size: 2,460 kB
  • ctags: 2,911
  • sloc: ansic: 39,134; yacc: 335; makefile: 65; sh: 8
file content (357 lines) | stat: -rw-r--r-- 10,233 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
#include <stdio.h>
#include <stdlib.h>
#ifdef VMS
#include "../util/VMSparam.h"
#else
#include <sys/param.h>
#endif /*VMS*/
#include <Xm/Xm.h>
#include <X11/Xatom.h>
#include "../util/DialogF.h"
#include "../util/fileUtils.h"
#include "textBuf.h"
#include "text.h"
#include "nedit.h"
#include "window.h"
#include "file.h"
#include "search.h"
#include "selection.h"
#include "tags.h"

#define MAXLINE 512
#define MAX_TAG_LEN 80

enum searchDirection {FORWARD, BACKWARD};

typedef struct {
    char *name;
    char *file;
    char *searchString;
} tag;

static void findDefCB(Widget widget, WindowInfo *window, Atom *sel,
    	Atom *type, char *value, int *length, int *format);
static void findDef(Widget dialogParent, char *string);
static void setTag(tag *t, char *name, char *file, char *searchString);
static void freeTagList(tag *tags);
static int fakeRegExSearch(WindowInfo *window, char *searchString, 
	int *start, int *end);

/* Parsed list of tags read by LoadTagsFile.  List is terminated by a tag
   structure with the name field == NULL */
static tag *Tags = NULL;
static char TagPath[MAXPATHLEN];

int LoadTagsFile(char *filename)
{
    FILE *fp = NULL;
    char line[MAXLINE], name[MAXLINE], file[MAXLINE], searchString[MAXLINE];
    char unused[MAXPATHLEN];
    char *charErr;
    tag *tags;
    int i, nTags, nRead;
    WindowInfo *w;

    /* Open the file */
    if ((fp = fopen(filename, "r")) == NULL)
    	return FALSE;
	
    /* Read it once to see how many lines there are */
    for (nTags=0; TRUE; nTags++) {
    	charErr = fgets(line, MAXLINE, fp);
    	if (charErr == NULL) {
    	    if (feof(fp))
    	    	break;
    	    else
    	    	return FALSE;
    	}
    }
    
    /* Allocate zeroed memory for list so that it is automatically terminated
       and can be freed by freeTagList at any stage in its construction*/
    tags = (tag *)calloc(nTags + 1, sizeof(tag));
    
    /* Read the file and store its contents */
    rewind(fp);
    for (i=0; i<nTags; i++) {
    	charErr = fgets(line, MAXLINE, fp);
    	if (charErr == NULL) {
    	    if (feof(fp))
    	    	break;
    	    else {
    	    	freeTagList(tags);
    	    	return FALSE;
    	    }
    	}
    	nRead = sscanf(line, "%s\t%s\t%[^\n]", name, file, searchString);
    	if (nRead != 3) {
    	    freeTagList(tags);
    	    return FALSE;
    	}
	setTag(&tags[i], name, file, searchString);
    }
    
    /* Make sure everything was read */
    if (i != nTags) {
    	freeTagList(tags);
    	return FALSE;
    }
    
    /* Replace current tags data and path for retrieving files */
    if (Tags != NULL)
    	freeTagList(Tags);
    Tags = tags;
    ParseFilename(filename, unused, TagPath);
    
    /* Undim the "Find Definition" menu item in the existing windows */
    for (w=WindowList; w!=NULL; w=w->next)
    	XtSetSensitive(w->findDefItem, TRUE);
    return TRUE;
}

int TagsFileLoaded(void)
{
    return Tags != NULL;
}

/*
** Given a name, lookup a file, search string.  Returned strings are pointers
** to internal storage which are valid until the next LoadTagsFile call.
*/
int LookupTag(char *name, char **file, char **searchString)
{
    int i;
    tag *t;
    
    if (!TagsFileLoaded())
    	return FALSE;
    	
    for (i=0, t=Tags; t->name!=NULL; i++, t++) {
 	if (!strcmp(t->name, name)) {
 	    *file = t->file;
 	    *searchString = t->searchString;
 	    return TRUE;
	}
    }
    return FALSE;
}

/*
** Lookup the definition for the current primary selection the currently
** loaded tags file and bring up the file and line that the tags file
** indicates.
*/
void FindDefinition(WindowInfo *window, Time time)
{
    XtGetSelectionValue(window->textArea, XA_PRIMARY, XA_STRING,
    	    (XtSelectionCallbackProc)findDefCB, window, time);
}

static void findDefCB(Widget widget, WindowInfo *window, Atom *sel,
    	Atom *type, char *value, int *length, int *format)
{
    char tagText[MAX_TAG_LEN+1];
    
    /* skip if we can't get the selection data, or it's obviously too long */
    if (*type == XT_CONVERT_FAIL || value == NULL) {
    	XBell(TheDisplay, 0);
	return;
    }
    if (*length > MAX_TAG_LEN) {
    	XBell(TheDisplay, 0);
	XtFree(value);
	return;
    }
    /* should be of type text??? */
    if (*format != 8) {
    	fprintf(stderr, "NEdit: Can't handle non 8-bit text\n");
    	XBell(TheDisplay, 0);
	XtFree(value);
	return;
    }
    
    /* Copy the string just to make space for the null character (this may
       not be necessary, XLib documentation claims a NULL is already added,
       but the Xt documentation for this routine makes no such claim) */
    strncpy(tagText, value, *length);
    tagText[*length] = '\0';
    
    /* Lookup the tag and put up the window */
    findDef(window->textArea, tagText);
    
    /* The selection requstor is required to free the memory passed
       to it via value */
    XtFree((char *)value);
}

/*
** Lookup the definition for "string" in the currently loaded tags file
** and bring up the file and line that the tags file indicates
*/
static void findDef(Widget dialogParent, char *string)
{
    int startPos, endPos, found, lineNum, rows;
    char *fileToSearch, *searchString, *eptr;
    WindowInfo *windowToSearch;
    char filename[MAXPATHLEN], pathname[MAXPATHLEN], temp[MAXPATHLEN];

    /* verify that the string is reasonable as a tag */
    if (*string == '\0') {
	XBell(TheDisplay, 0);
	return;
    }
    if (strlen(string) > MAX_TAG_LEN) {
    	XBell(TheDisplay, 0);
	return;
    }
    
    /* lookup the name in the tags file */
    found = LookupTag(string, &fileToSearch, &searchString);
    if (!found) {
    	DialogF(DF_WARN, dialogParent, 1, "%s not found in tags file", "OK",
    		string);
    	return;
    }
    
    /* if the path is not absolute, qualify file path with directory
       from which tags file was loaded */
    if (fileToSearch[0] == '/')
    	strcpy(temp, fileToSearch);
    else {
    	strcpy(temp, TagPath);
    	strcat(temp, fileToSearch);
    }
    ParseFilename(temp, filename, pathname);
    
    /* open the file containing the definition */
    EditExistingFile(WindowList, filename, pathname, FALSE);
    windowToSearch = FindWindowWithFile(filename, pathname);
    if (windowToSearch == NULL) {
    	DialogF(DF_WARN, dialogParent, 1, "File %s not found", 
    		"OK", fileToSearch);
    	return;
    }
    
    /* if the search string is a number, select the numbered line */
    lineNum = strtol(searchString, &eptr, 10);
    if (eptr != searchString) {
    	SelectNumberedLine(windowToSearch, lineNum);
    	return;
    }
    
    /* search for the tags file search string in the newly opened file */
    found = fakeRegExSearch(windowToSearch, searchString ,&startPos, &endPos);
    if (!found) {
    	DialogF(DF_WARN, windowToSearch->shell, 1,"Definition for %s\nnot found in %s", 
    		"OK", string, fileToSearch);
    	return;
    }

    /* select the matched string */
    BufSelect(windowToSearch->buffer, startPos, endPos);
    
    /* Position it nicely in the window, about 1/4 of the way down from the
       top */
    lineNum = BufCountLines(windowToSearch->buffer, 0, startPos);
    XtVaGetValues(windowToSearch->lastFocus, textNrows, &rows, 0);
    TextSetScroll(windowToSearch->lastFocus, lineNum - rows/4, 0);
    TextSetCursorPos(windowToSearch->lastFocus, endPos);
}

static void setTag(tag *t, char *name, char *file, char *searchString)
{
    t->name = (char *)malloc(sizeof(char) * strlen(name) + 1);
    strcpy(t->name, name);
    t->file = (char *)malloc(sizeof(char) * strlen(file) + 1);
    strcpy(t->file, file);
    t->searchString = (char *)malloc(sizeof(char) * strlen(searchString) + 1);
    strcpy(t->searchString, searchString);
}

static void freeTagList(tag *tags)
{
    int i;
    tag *t;
    
    for (i=0, t=tags; t->name!=NULL; i++, t++) {
    	free(t->name);
    	free(t->file);
    	free(t->searchString);
    }
    free(tags);
}

/*
** regex searching is not available on all platforms.  To use built in
** case sensitive searching, this routine fakes enough to handle the
** search characters presented in ctags files
*/
static int fakeRegExSearch(WindowInfo *window, char *searchString, 
	int *start, int *end)
{
    int startPos, endPos, found=FALSE, hasBOL, hasEOL, fileLen, searchLen, dir;
    char *fileString, searchSubs[MAXLINE];
    
    /* get the entire (sigh) text buffer from the text area widget */
    fileString = BufGetAll(window->buffer);
    fileLen = window->buffer->length;

    /* remove / .. / or ? .. ? and substitute ^ and $ with \n */
    searchLen = strlen(searchString);
    if (searchString[0] == '/')
    	dir = FORWARD;
    else if (searchString[0] == '?')
    	dir = BACKWARD;
    else {
    	fprintf(stderr, "NEdit: Error parsing tag file search string");
    	return FALSE;
    }
    searchLen -= 2;
    strncpy(searchSubs, &searchString[1], searchLen);
    searchSubs[searchLen] = '\0';
    hasBOL = searchSubs[0] == '^';
    hasEOL = searchSubs[searchLen-1] == '$';
    if (hasBOL) searchSubs[0] = '\n';
    if (hasEOL) searchSubs[searchLen-1] = '\n';

    /* search for newline-substituted string in the file */
    if (dir==FORWARD)
    	found = SearchString(fileString, searchSubs, SEARCH_FORWARD,
    		SEARCH_CASE_SENSE, False, 0, &startPos, &endPos, NULL);
    else
    	found = SearchString(fileString, searchSubs, SEARCH_BACKWARD,
    		SEARCH_CASE_SENSE, False, fileLen, &startPos, &endPos, NULL);
    if (found) {
    	if (hasBOL) startPos++;
    	if (hasEOL) endPos--;
    }
    
    /* if not found: ^ may match beginning of file, $ may match end */
    if (!found && hasBOL) {
    	found = strncmp(&searchSubs[1], fileString, searchLen-1);
    	if (found) {
    	    startPos = 0;
    	    endPos = searchLen - 2;
    	}
    }
    if (!found && hasEOL) {	    
    	found = strncmp(searchSubs, fileString+fileLen-searchLen+1,
    		 searchLen-1);
    	if (found) {
    	    startPos = fileLen-searchLen+2;
    	    endPos = fileLen;
    	}
    }

    /* free the text buffer copy returned from XmTextGetString */
    XtFree(fileString);
    
    /* return the result */
    if (!found) {
    	XBell(TheDisplay, 0);
	return FALSE;
    }
    *start = startPos;
    *end = endPos;
    return TRUE;
}