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