1 /*
2 * Copyright (c) 1998-2002, Darren Hiebert
3 *
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License version 2 or (at your option) any later version.
6 *
7 * External interface to entry.c
8 */
9
10 #include "general.h" /* must always come first */
11
12 #include <string.h>
13
14 #include "debug.h"
15 #include "entry_p.h"
16 #include "mio.h"
17 #include "options_p.h"
18 #include "parse.h"
19 #include "parse_p.h"
20 #include "read.h"
21 #include "routines.h"
22 #include "routines_p.h"
23 #include "vstring.h"
24 #include "writer_p.h"
25
26
27 #define ETAGS_FILE "TAGS"
28
29
30 static int writeEtagsEntry (tagWriter *writer, MIO * mio, const tagEntryInfo *const tag,
31 void *clientData CTAGS_ATTR_UNUSED);
32 static void *beginEtagsFile (tagWriter *writer, MIO * mio,
33 void *clientData CTAGS_ATTR_UNUSED);
34 static bool endEtagsFile (tagWriter *writer, MIO * mio, const char* filename,
35 void *clientData CTAGS_ATTR_UNUSED);
36
37 tagWriter etagsWriter = {
38 .writeEntry = writeEtagsEntry,
39 .writePtagEntry = NULL,
40 .preWriteEntry = beginEtagsFile,
41 .postWriteEntry = endEtagsFile,
42 .rescanFailedEntry = NULL,
43 .treatFieldAsFixed = NULL,
44 .defaultFileName = ETAGS_FILE,
45 };
46
47 struct sEtags {
48 char *name;
49 MIO *mio;
50 size_t byteCount;
51 vString *vLine;
52 };
53
54
55
beginEtagsFile(tagWriter * writer CTAGS_ATTR_UNUSED,MIO * mio CTAGS_ATTR_UNUSED,void * clientData CTAGS_ATTR_UNUSED)56 static void *beginEtagsFile (tagWriter *writer CTAGS_ATTR_UNUSED, MIO *mio CTAGS_ATTR_UNUSED,
57 void *clientData CTAGS_ATTR_UNUSED)
58 {
59 static struct sEtags etags = { NULL, NULL, 0, NULL };
60
61 etags.mio = tempFile ("w+b", &etags.name);
62 etags.byteCount = 0;
63 etags.vLine = vStringNew ();
64 return &etags;
65 }
66
endEtagsFile(tagWriter * writer,MIO * mainfp,const char * filename,void * clientData CTAGS_ATTR_UNUSED)67 static bool endEtagsFile (tagWriter *writer,
68 MIO *mainfp, const char *filename,
69 void *clientData CTAGS_ATTR_UNUSED)
70 {
71 const char *line;
72 struct sEtags *etags = writer->private;
73
74 mio_printf (mainfp, "\f\n%s,%ld\n", filename, (long) etags->byteCount);
75 setNumTagsAdded (numTagsAdded () + 1);
76 abort_if_ferror (mainfp);
77
78 if (etags->mio != NULL)
79 {
80 mio_rewind (etags->mio);
81
82 while ((line = readLineRaw (etags->vLine, etags->mio)) != NULL)
83 mio_puts (mainfp, line);
84
85 vStringDelete (etags->vLine);
86 mio_unref (etags->mio);
87 remove (etags->name);
88 eFree (etags->name);
89 etags->vLine = NULL;
90 etags->mio = NULL;
91 etags->name = NULL;
92 }
93 return false;
94 }
95
ada_suffix(const tagEntryInfo * const tag,const char * const line)96 static const char* ada_suffix (const tagEntryInfo *const tag, const char *const line)
97 {
98 kindDefinition *kdef = getLanguageKind(tag->langType, tag->kindIndex);
99
100 Assert (kdef);
101
102 /* Mapping from ctags' kind letter to etags's suffix string.
103 * See https://www.gnu.org/software/emacs/manual/html_node/emacs/Tag-Syntax.html */
104 switch (kdef->letter)
105 {
106 case 'p':
107 case 'k':
108 return "/b";
109 case 'K':
110 return "/k";
111 case 'P':
112 return "/s";
113 case 't':
114 return "/t";
115 case 'R':
116 case 'r':
117 {
118 /* Unlike etags, ctags uses the procedure kind for both
119 * procedures and functions. So in the level, emitting a tag,
120 * we cannot distinguish whether a tag is for a procedureor a
121 * function.
122 *
123 * If the typeref field of the tag is filled, we can say the tag
124 * is for a function. However, Ada parser doesn't implement the
125 * typeref field yet, and implementing it is not so easy.
126 *
127 * So we have to take an unclean way here: scanning the input
128 * line again.
129 * FIXME: remove the scanning code and implement the typeref field
130 * in Ada.
131 */
132 const char *r = strstr (line, "return");
133 const char *f = strstr (line, "function");
134 const char *p = strstr (line, "procedure");
135 if (r && f)
136 return "/f";
137 else if (p && !r)
138 return "/p";
139 return ""; /* Unknown */
140 }
141 default:
142 return "";
143 }
144 }
145
writeEtagsEntry(tagWriter * writer,MIO * mio,const tagEntryInfo * const tag,void * clientData CTAGS_ATTR_UNUSED)146 static int writeEtagsEntry (tagWriter *writer,
147 MIO * mio, const tagEntryInfo *const tag,
148 void *clientData CTAGS_ATTR_UNUSED)
149 {
150 langType adaLangType = getNamedLanguage ("Ada", 0);
151 Assert (adaLangType != LANG_IGNORE);
152
153 int length;
154 struct sEtags *etags = writer->private;
155
156 mio = etags->mio;
157
158 if (tag->isFileEntry)
159 length = mio_printf (mio, "\177%s\001%lu,0\n",
160 tag->name, tag->lineNumber);
161 else
162 {
163 size_t len;
164 long seekValue;
165 char *const line =
166 readLineFromBypassForTag (etags->vLine, tag, &seekValue);
167 if (line == NULL || line [0] == '\0')
168 return 0;
169
170 len = strlen (line);
171
172 if (tag->truncateLineAfterTag)
173 truncateTagLineAfterTag (line, tag->name, true);
174 else if (line [len - 1] == '\n')
175 line [--len] = '\0';
176
177 if (Option.patternLengthLimit > 0 && Option.patternLengthLimit < len)
178 {
179 unsigned int truncationLength = Option.patternLengthLimit;
180
181 /* don't cut in the middle of a UTF-8 character, but don't allow
182 * for more than one extra character in case it actually wasn't
183 * UTF-8. See also entry.c:appendInputLine() */
184 while (truncationLength < len &&
185 truncationLength < Option.patternLengthLimit + 3 &&
186 (((unsigned char) line[truncationLength]) & 0xc0) == 0x80)
187 truncationLength++;
188
189 line [truncationLength] = '\0';
190 }
191
192 length = mio_printf (mio, "%s\177%s%s\001%lu,%ld\n", line,
193 tag->name,
194 (tag->langType == adaLangType)
195 ? ada_suffix (tag, line)
196 : "",
197 tag->lineNumber, seekValue);
198 }
199 etags->byteCount += length;
200
201 return length;
202 }
203