1 /* e_tags.cpp *
2 * Copyright (c) 1994-1996, Marko Macek
3 *
4 * You may distribute under the terms of either the GNU General Public
5 * License or the Artistic License, as specified in the README file.
6 *
7 */
8
9 #include "e_tags.h"
10
11 #ifdef CONFIG_TAGS
12
13 #include "o_buflist.h"
14 #include "s_files.h"
15 #include "s_util.h"
16 #include "sysdep.h"
17
18 #include <fcntl.h>
19
20 struct TagData {
21 int Tag; // tag name pos
22 int FileName;
23 int TagBase; // name of tag file
24 int Line;
25 int StrFind; // string to find
26 };
27
28 struct TagStack {
29 static TagStack* TStack;
30
31 StlString CurrentTag;
32 StlString FileName;
33 int Line, Col;
34 int TagPos;
35
TagStackTagStack36 TagStack(const char* tag, const char* file, int line, int col, int pos) :
37 CurrentTag(tag), FileName(file), Line(line), Col(col), TagPos(pos), Next(TStack)
38 {
39 TStack = this;
40 }
~TagStackTagStack41 ~TagStack()
42 {
43 TStack = Next;
44 }
45
46 private:
47 TagStack *Next;
48 };
49
50 TagStack* TagStack::TStack = 0;
51
52 static char *TagMem = 0;
53 static int TagLen = 0;
54 static int ATagMem = 0;
55
56 static int CTags = 0; // number of tags
57 static int ATags = 0;
58 static TagData *TagD = 0;
59 static int *TagI = 0;
60 static int TagFileCount = 0;
61 static int *TagFiles = 0;
62
63 static int TagFilesLoaded = 0; // tag files are loaded at first lookup
64 static char *CurrentTag;
65 static int TagPosition = -1;
66
AllocMem(const char * Mem,size_t Len,int * TagPos)67 static int AllocMem(const char *Mem, size_t Len, int *TagPos) {
68 int N = 1024;
69 char *NM;
70 *TagPos = TagLen;
71
72 while (N < (TagLen + Len))
73 N <<= 1;
74 if (ATagMem < N || !TagMem) {
75 if (!(NM = (char *)realloc((void *)TagMem, N)))
76 return 0;
77 TagMem = NM;
78 ATagMem = N;
79 }
80 memcpy(TagMem + TagLen, Mem, Len);
81 TagLen += Len;
82 return 1;
83 }
84
AddTag(int Tag,int FileName,int TagBase,int Line,int StrFind)85 static int AddTag(int Tag, int FileName, int TagBase, int Line, int StrFind) {
86 int N;
87
88 N = 1024;
89 while (N < CTags + 1) N <<= 1;
90 if (ATags < N || TagD == 0) {
91 TagData *ND;
92
93 if (!(ND = (TagData *)realloc((void *)TagD, N * sizeof(TagData))))
94 return 0;
95 TagD = ND;
96 ATags = N;
97 }
98 TagD[CTags].Tag = Tag;
99 TagD[CTags].Line = Line;
100 TagD[CTags].FileName = FileName;
101 TagD[CTags].TagBase = TagBase;
102 TagD[CTags].StrFind = StrFind;
103 CTags++;
104 return 1;
105 }
106
107 #if defined(__IBMCPP__)
cmptags(const void * p1,const void * p2)108 static int _LNK_CONV cmptags(const void *p1, const void *p2) {
109 #else
110 static int cmptags(const void *p1, const void *p2) {
111 #endif
112 //return strcmp(TagMem + TagD[*(int *)p1].Tag,
113 // TagMem + TagD[*(int *)p2].Tag);
114 int r = strcmp(TagMem + TagD[*(int *)p1].Tag,
115 TagMem + TagD[*(int *)p2].Tag);
116 if (r != 0)
117 return r;
118
119 r = strcmp(TagMem + TagD[*(int *)p1].FileName,
120 TagMem + TagD[*(int *)p2].FileName);
121 if (r != 0)
122 return r;
123
124 if (TagD[*(int *)p1].Line != TagD[*(int *)p2].Line)
125 r = TagD[*(int *)p1].Line < TagD[*(int *)p2].Line ? -1 : 1;
126
127 return r;
128 }
129
130 static int SortTags() {
131 int *NI;
132 int i;
133
134 if (!CTags)
135 return 0;
136
137 if (!(NI = (int *)realloc(TagI, CTags * sizeof(int))))
138 return 0;
139
140 TagI = NI;
141 for (i = 0; i < CTags; i++)
142 TagI[i] = i;
143
144 qsort(TagI, CTags, sizeof(TagI[0]), cmptags);
145
146 return 1;
147 }
148
149 static int TagsLoad(int id) {
150 //char line[2048];
151 char *tags;
152 int fd;
153 struct stat sb;
154 long size;
155 int r = 0;
156
157 if ((fd = open(TagMem + TagFiles[id], O_BINARY | O_RDONLY)) == -1)
158 return 0;
159
160 if (fstat(fd, &sb) == -1) {
161 close(fd);
162 return 0;
163 }
164
165 if (!(tags = (char *)malloc((size_t)sb.st_size))) {
166 close(fd);
167 return 0;
168 }
169
170 char *p = tags;
171 char *e = tags + sb.st_size;
172
173 size = read(fd, tags, (size_t)sb.st_size);
174 close(fd);
175 if (size != sb.st_size)
176 goto err;
177
178 if (!TagMem) { // preallocate (useful when big file)
179 if (!(TagMem = (char *)realloc((void *)TagMem, TagLen + (size_t)sb.st_size)))
180 goto err;
181
182 ATagMem = TagLen + (int)sb.st_size;
183 }
184
185 char *LTag, *LFile, *LLine;
186 int TagL, FileL/*, LineL*/;
187 int MTag, MFile;
188
189 while (p < e) {
190 LTag = p;
191 while (p < e && *p != '\t') p++;
192 if (p < e && *p == '\t') *p++ = 0;
193 else break;
194 TagL = p - LTag;
195 LFile = p;
196 while (p < e && *p != '\t') p++;
197 if (p < e && *p == '\t') *p++ = 0;
198 else break;
199 FileL = p - LFile;
200 LLine = p;
201 while (p < e && *p != '\r' && *p != '\n') p++;
202 if (p < e && *p == '\r') *p++ = 0; // optional
203 if (p < e && *p == '\n') *p++ = 0;
204 else break;
205 //LineL = p - LLine;
206
207 if (!AllocMem(LTag, TagL + FileL, &MTag))
208 goto err;
209
210 MFile = MTag + TagL;
211
212 //printf("TSTR %s\n", LTag);
213 //printf("FSTR %s\n", LTag + TagL);
214 if (LLine[0] == '/') {
215 char *AStr = LLine;
216 char *p = AStr + 1;
217 char *d = AStr;
218 int MStr;
219
220 while (*p) {
221 if (*p == '\\') {
222 p++;
223 if (*p)
224 *d++ = *p++;
225 } else if (*p == '^' || *p == '$') p++;
226 else if (*p == '/')
227 break;
228 else
229 *d++ = *p++;
230 }
231 *d = 0;
232 if (stricmp(d - 10, "/*FOLD00*/") == 0)
233 d[-11] = 0; /* remove our internal folds */
234 //printf("MSTR %s\n", AStr);
235 if (!AllocMem(AStr, strlen(AStr) + 1, &MStr))
236 goto err;
237
238 if (!AddTag(MTag, MFile, TagFiles[id], -1, MStr))
239 goto err;
240 } else {
241 if (!AddTag(MTag, MFile, TagFiles[id], atoi(LLine), -1))
242 goto err;
243 }
244 }
245 r = 1;
246 err:
247 free(tags);
248 return r;
249 }
250
251 int TagsAdd(const char *FileName) {
252 int *NewT;
253 int NewF;
254
255 if (!AllocMem(FileName, strlen(FileName) + 1, &NewF))
256 return 0;
257
258 if (!(NewT = (int *)realloc((void *)TagFiles, (TagFileCount + 1) * sizeof(int))))
259 return 0;
260
261 TagFiles = NewT;
262 TagFiles[TagFileCount++] = NewF;
263
264 return 1;
265 }
266
267 int TagsSave(FILE *fp) {
268 for (int i = 0; i < TagFileCount; i++)
269 if (fprintf(fp, "T|%s\n", TagMem + TagFiles[i]) < 0)
270 return 0;
271
272 return 1;
273 }
274
275 static void ClearTagStack() {
276 free(CurrentTag);
277 CurrentTag = 0;
278 TagPosition = -1;
279 while (TagStack::TStack)
280 delete TagStack::TStack;
281 }
282
283 int TagLoad(const char *FileName) {
284 if (!TagsAdd(FileName))
285 return 0;
286
287 ClearTagStack();
288 if (TagFilesLoaded) {
289 if (!TagsLoad(TagFileCount - 1) || !SortTags()) {
290 TagClear();
291 return 0;
292 }
293 }
294
295 return 1;
296 }
297
298 static int LoadTagFiles() {
299 if (!TagFileCount)
300 return 0;
301
302 if (TagFilesLoaded)
303 return 1;
304
305 for (int i = 0; i < TagFileCount; i++)
306 if (!TagsLoad(i)) {
307 TagClear();
308 return 0;
309 }
310 if (!SortTags()) {
311 TagClear();
312 return 0;
313 }
314 TagFilesLoaded = 1;
315 return 1;
316 }
317
318 void TagClear() {
319 free(TagD);
320 free(TagI);
321 TagD = 0;
322 TagI = 0;
323 CTags = 0;
324 ATags = 0;
325
326 free(TagFiles);
327 TagFiles = 0;
328 TagFileCount = 0;
329 TagFilesLoaded = 0;
330
331 free(TagMem);
332 TagMem = 0;
333 TagLen = 0;
334 ATagMem = 0;
335
336 ClearTagStack();
337 }
338
339 static int GotoFilePos(EView *View, const char *FileName, int Line, int Col) {
340 if (!FileLoad(0, FileName, 0, View))
341 return 0;
342
343 if (((EBuffer *)ActiveModel)->Loaded == 0)
344 ((EBuffer *)ActiveModel)->Load();
345 ((EBuffer *)ActiveModel)->CenterNearPosR(Col, Line);
346
347 return 1;
348 }
349
350 static int GotoTag(int M, EView *View) {
351 char path[MAXPATH];
352 char Dir[MAXPATH];
353 TagData *TT = &TagD[TagI[M]];
354
355 JustDirectory(TagMem + TT->TagBase, Dir, sizeof(Dir));
356
357 if (IsFullPath(TagMem + TT->FileName)) {
358 strcpy(path, TagMem + TT->FileName);
359 } else {
360 strcpy(path, Dir);
361 Slash(path, 1);
362 strcat(path, TagMem + TT->FileName);
363 }
364 if (TT->Line != -1) {
365 if (!GotoFilePos(View, path, TT->Line - 1, 0))
366 return 0;
367 } else {
368 if (!GotoFilePos(View, path, 0, 0))
369 return 0;
370 if (!((EBuffer *)ActiveModel)->FindStr(TagMem + TT->StrFind, strlen(TagMem + TT->StrFind), 0))
371 return 0;
372 }
373 ((EBuffer *)ActiveModel)->FindStr(TagMem + TT->Tag, strlen(TagMem + TT->Tag), 0);
374 return 1;
375 }
376
377 static int PushPos(EBuffer *B)
378 {
379 (void) new TagStack(CurrentTag, B->FileName, B->VToR(B->CP.Row), B->CP.Col,
380 TagPosition);
381 TagStack* T = TagStack::TStack;
382 //printf("PUSHED POS %p %s\n", T, T->CurrentTag.c_str());
383 return 1;
384 }
385
386 int TagGoto(EView *View, const char *Tag) {
387 assert(Tag);
388
389 if (!LoadTagFiles() || !CTags)
390 return 0;
391
392 int L = 0, R = CTags, M, cmp;
393
394 while (L < R) {
395 M = (L + R) / 2;
396 cmp = strcmp(Tag, TagMem + TagD[TagI[M]].Tag);
397 if (cmp == 0) {
398 while (M > 0 && strcmp(Tag, TagMem + TagD[TagI[M - 1]].Tag) == 0)
399 M--;
400
401 if (!GotoTag(M, View))
402 return 0;
403
404 free(CurrentTag);
405 CurrentTag = strdup(Tag);
406 TagPosition = M;
407 return 1;
408 } else if (cmp < 0) {
409 R = M;
410 } else {
411 L = M + 1;
412 }
413 }
414
415 return 0; // tag not found
416 }
417
418 int TagFind(EBuffer *B, EView *View, const char *Tag) {
419 assert(View && Tag && B);
420
421 if (!LoadTagFiles())
422 return 0;
423
424 int L = 0, R = CTags, M, cmp;
425 //printf("TAGFIND %s %s tp:%d\n", Tag, CurrentTag, TagPosition);
426 if (CurrentTag) {
427 if (strcmp(CurrentTag, Tag) == 0) {
428 if (!TagStack::TStack || TagPosition != TagStack::TStack->TagPos)
429 if (!PushPos(B))
430 return 0;
431
432 TagPosition = TagStack::TStack->TagPos;
433 return TagNext(View);
434 }
435 }
436
437 if (!CTags)
438 return 0;
439
440 while (L < R) {
441 M = (L + R) / 2;
442 cmp = strcmp(Tag, TagMem + TagD[TagI[M]].Tag);
443 if (cmp == 0) {
444 while (M > 0 && strcmp(Tag, TagMem + TagD[TagI[M - 1]].Tag) == 0)
445 M--;
446
447 if (PushPos(B) == 0)
448 return 0;
449
450 if (GotoTag(M, View) == 0)
451 return 0;
452
453 free(CurrentTag); // in case it already exists.
454 CurrentTag = strdup(Tag);
455 TagPosition = M;
456 return 1;
457 } else if (cmp < 0) {
458 R = M;
459 } else {
460 L = M + 1;
461 }
462 }
463
464 return 0; // tag not found
465 }
466
467 int TagDefined(const char *Tag) {
468 if (!LoadTagFiles() || !CTags)
469 return 0;
470
471 int L = 0, R = CTags, M, cmp;
472 while (L < R) {
473 M = (L + R) / 2;
474 cmp = strcmp(Tag, TagMem + TagD[TagI[M]].Tag);
475 if (cmp == 0)
476 return 1;
477 else if (cmp < 0)
478 R = M;
479 else
480 L = M + 1;
481 }
482
483 return 0; // tag not found
484 }
485
486 int TagComplete(char **Words, int *WordsPos, int WordsMax, const char *Tag) {
487 if (!Tag || !Words || (*WordsPos >= WordsMax))
488 return 0;
489
490 if (!LoadTagFiles() || !CTags)
491 return 0;
492
493 int L = 0, R = CTags, len = strlen(Tag);
494 while (L < R) {
495 int c, M;
496
497 M = (L + R) / 2;
498 c = strncmp(Tag, TagMem + TagD[TagI[M]].Tag, len);
499 if (c == 0) {
500 while (M > 0 &&
501 strncmp(Tag, TagMem + TagD[TagI[M - 1]].Tag, len) == 0)
502 M--; // find begining
503 int N = M, w = 0;
504 while (strncmp(Tag, TagMem + TagD[TagI[N]].Tag, len) == 0) {
505 // the first word is not tested for previous match
506 if (!w || strcmp(TagMem + TagD[TagI[N]].Tag,
507 TagMem + TagD[TagI[N-1]].Tag)) {
508 int l = strlen(TagMem + TagD[TagI[N]].Tag) - len;
509 if (l > 0) {
510 char *s = new char[l + 1];
511 if (!s)
512 break;
513 strcpy(s, TagMem + TagD[TagI[N]].Tag + len);
514 Words[(*WordsPos)++] = s;
515 w++; // also mark the first usage
516 if (*WordsPos >= WordsMax)
517 break;
518 }
519 }
520 N++;
521 }
522 return w;
523 } else if (c < 0)
524 R = M;
525 else
526 L = M + 1;
527 }
528 return 0; // tag not found
529 }
530
531 int TagNext(EView *View) {
532 assert(View);
533
534 if (!CurrentTag || TagPosition == -1) {
535 View->Msg(S_INFO, "No current tag.");
536 return 0;
537 }
538
539 if (TagPosition < CTags - 1 && strcmp(CurrentTag, TagMem + TagD[TagI[TagPosition + 1]].Tag) == 0) {
540 TagPosition++;
541 return GotoTag(TagPosition, View);
542 }
543 View->Msg(S_INFO, "No next match for tag.");
544 return 0;
545 }
546
547 int TagPrev(EView *View) {
548 assert(View);
549
550 if (!CurrentTag || TagPosition == -1) {
551 View->Msg(S_INFO, "No current tag.");
552 return 0;
553 }
554
555 if (TagPosition > 0 && strcmp(CurrentTag, TagMem + TagD[TagI[TagPosition - 1]].Tag) == 0) {
556 TagPosition--;
557 return GotoTag(TagPosition, View);
558 }
559 View->Msg(S_INFO, "No previous match for tag.");
560 return 0;
561 }
562
563 int TagPop(EView *View) {
564
565 assert(View != 0);
566
567 delete TagStack::TStack;
568 TagStack* T = TagStack::TStack;
569 if (T) {
570 //printf("DELETE POP %p %s\n", T, T->CurrentTag.c_str());
571 free(CurrentTag);
572 CurrentTag = strdup(T->CurrentTag.c_str());
573 TagPosition = T->TagPos;
574 return GotoFilePos(View, T->FileName.c_str(), T->Line, T->Col);
575 }
576 View->Msg(S_INFO, "Tag stack empty.");
577 return 0;
578 }
579
580 #endif
581