xref: /Universal-ctags/main/unwindi.c (revision 347a83969630a5d15e3fc0f122b75c18b5bfec26)
1 /*
2  *
3  *  Copyright (c) 2019, Red Hat, Inc.
4  *  Copyright (c) 2019, Masatake YAMATO
5  *
6  *  Author: Masatake YAMATO <yamato@redhat.com>
7  *
8  *   This source code is released for free distribution under the terms of the
9  *   GNU General Public License version 2 or (at your option) any later version.
10  *
11  *   Unwindable input stream / Unlimited ungetc
12  *
13  */
14 
15 /*
16 *   INCLUDE FILES
17 */
18 
19 #include "general.h"
20 
21 #include "debug.h"
22 #include "gcc-attr.h"
23 #include "inline.h"
24 #include "mio.h"
25 #include "objpool.h"
26 #include "ptrarray.h"
27 #include "read.h"
28 #include "routines.h"
29 #include "trashbox.h"
30 #include "unwindi.h"
31 
32 #include <string.h>
33 
34 typedef struct sUugcChar {
35 	int c;
36 	/* lineNumber before reading the char (frontLineNumber).
37 	 * The lineNumber after reading the char (rearLineNumber) can be calculated:
38 	 * If the char is \n, rearLineNumber is frontLineNumber + 1.
39 	 * If the char is not, rearLineNumber is the same as frontLineNumber. */
40 	unsigned long lineNumber;
41 } uugcChar;
42 
43 
44 static ptrArray *uugcInputFile;
45 static uugcChar *uugcCurrentChar;
46 static objPool  *uugcCharPool;
47 
48 static struct sUwiStats uwiStats;
49 
deleteChar(void * c)50 static void deleteChar (void *c)
51 {
52 	eFree (c);
53 }
54 
newChar(void * data CTAGS_ATTR_UNUSED)55 static void* newChar (void *data CTAGS_ATTR_UNUSED)
56 {
57 	return xMalloc (1, uugcChar);
58 }
59 
uugcDeleteC(uugcChar * c)60 CTAGS_INLINE void uugcDeleteC (uugcChar *c)
61 {
62 	if (c == uugcCurrentChar)
63 		uugcCurrentChar = NULL;
64 
65 	objPoolPut (uugcCharPool, c);
66 }
67 
uugcActivate(void)68 static void uugcActivate (void)
69 {
70 	Assert (!uugcInputFile);
71 	Assert (!uugcCurrentChar);
72 
73 	if (uugcCharPool == NULL)
74 	{
75 		uugcCharPool = objPoolNew(256,
76 								  newChar,
77 								  deleteChar,
78 								  NULL,
79 								  NULL);
80 		DEFAULT_TRASH_BOX(uugcCharPool, objPoolDelete);
81 	}
82 
83 	uugcInputFile = ptrArrayNew ((ptrArrayDeleteFunc)uugcDeleteC);
84 }
85 
uugcDeactive(void)86 static void uugcDeactive(void)
87 {
88 	Assert (uugcInputFile);
89 	ptrArrayDelete (uugcInputFile);
90 	uugcInputFile = NULL;
91 	uugcCurrentChar = NULL;
92 }
93 
uugcNewC(int chr,unsigned long ln)94 CTAGS_INLINE uugcChar *uugcNewC (int chr, unsigned long ln)
95 {
96 	Assert (uugcCharPool);
97 
98 	uugcChar *c = objPoolGet (uugcCharPool);
99 	c->c = chr;
100 	c->lineNumber = ln;
101 	return c;
102 }
103 
uugciGetC(void)104 CTAGS_INLINE uugcChar *uugciGetC (void)
105 {
106 	uugcChar *c;
107 
108 	Assert (uugcInputFile);
109 
110 	if (ptrArrayCount (uugcInputFile) > 0)
111 	{
112 		c = ptrArrayLast (uugcInputFile);
113 		ptrArrayRemoveLast (uugcInputFile);
114 	}
115 	else
116 	{
117 		unsigned long lineNumber = getInputLineNumber ();
118 		int chr = getcFromInputFile();
119 		c = uugcNewC (chr, lineNumber);
120 	}
121 
122 	uugcCurrentChar = c;
123 	return uugcCurrentChar;
124 }
125 
uugcUngetC(uugcChar * c)126 CTAGS_INLINE void uugcUngetC (uugcChar *c)
127 {
128 	uugcCurrentChar = NULL;
129 
130 	if (c->c == EOF)
131 	{
132 		ptrArrayClear (uugcInputFile);
133 		uugcDeleteC (c);
134 		return;
135 	}
136 
137 	ptrArrayAdd (uugcInputFile, c);
138 }
139 
uugcInjectC(int chr)140 CTAGS_INLINE void uugcInjectC (int chr)
141 {
142 	if (chr == EOF)
143 		return;
144 
145 	uugcChar *lastc = NULL;
146 	if (ptrArrayCount (uugcInputFile) > 0)
147 		lastc = ptrArrayLast (uugcInputFile);
148 
149 	unsigned long lineNumber;
150 	if (lastc)
151 	{
152 		if (chr == '\n' && lastc->lineNumber > 0)
153 			lineNumber = lastc->lineNumber - 1;
154 		else
155 			lineNumber = lastc->lineNumber;
156 	}
157 	else
158 	{
159 		lineNumber = getInputLineNumber ();
160 		if (chr == '\n')
161 			lineNumber--;
162 	}
163 
164 	uugcChar *c = uugcNewC(chr, lineNumber);
165 	uugcUngetC (c);
166 }
167 
uugcGetLineNumber()168 CTAGS_INLINE long uugcGetLineNumber ()
169 {
170 	Assert (uugcInputFile);
171 
172 	if (uugcCurrentChar)
173 	{
174 		unsigned long ln;
175 		if (uugcCurrentChar->c == '\n')
176 			ln = uugcCurrentChar->lineNumber + 1;
177 		else
178 			ln = uugcCurrentChar->lineNumber;
179 		return ln;
180 	}
181 	else if (ptrArrayCount (uugcInputFile) > 0)
182 	{
183 		uugcChar *c = ptrArrayLast (uugcInputFile);
184 		return c->lineNumber;
185 	}
186 	else
187 		return getInputLineNumber ();
188 }
189 
uugcGetFilePosition(void)190 CTAGS_INLINE MIOPos uugcGetFilePosition (void)
191 {
192 	if (uugcCurrentChar)
193 	{
194 		unsigned long ln;
195 		if (uugcCurrentChar->c == '\n')
196 			ln = uugcCurrentChar->lineNumber + 1;
197 		else
198 			ln = uugcCurrentChar->lineNumber;
199 		return getInputFilePositionForLine (ln);
200 	}
201 	else if (ptrArrayCount (uugcInputFile) > 0)
202 	{
203 		uugcChar *c = ptrArrayLast (uugcInputFile);
204 		return getInputFilePositionForLine (c->lineNumber);
205 	}
206 	else
207 		return getInputFilePosition ();
208 }
209 
210 static ptrArray *uwiBuffer;
211 static unsigned int *uwiMarkerStack;
212 static unsigned int uwiMarkerStackLength;
213 static unsigned int *uwiCurrentMarker;
214 
uwiActivate(unsigned int stackLength)215 extern void uwiActivate (unsigned int stackLength)
216 {
217 	Assert (stackLength > 0);
218 
219 	uugcActivate ();
220 	uwiBuffer = ptrArrayNew ((ptrArrayDeleteFunc)uugcDeleteC);
221 	uwiMarkerStackLength = stackLength;
222 	uwiMarkerStack = xMalloc (stackLength, unsigned int);
223 	uwiCurrentMarker = NULL;
224 
225 	uwiStatsInit (&uwiStats);
226 }
227 
uwiDeactivate(struct sUwiStats * statsToBeUpdated)228 extern void uwiDeactivate (struct sUwiStats *statsToBeUpdated)
229 {
230 	Assert (uwiBuffer);
231 	Assert (uwiMarkerStack);
232 
233 	if (statsToBeUpdated)
234 	{
235 		if (statsToBeUpdated->maxLength < uwiStats.maxLength)
236 			statsToBeUpdated->maxLength = uwiStats.maxLength;
237 		if (!statsToBeUpdated->overflow)
238 			statsToBeUpdated->overflow = uwiStats.overflow;
239 		if (!statsToBeUpdated->underflow)
240 			statsToBeUpdated->underflow = uwiStats.underflow;
241 	}
242 
243 	ptrArrayDelete (uwiBuffer);
244 	eFree (uwiMarkerStack);
245 	uwiBuffer = NULL;
246 	uwiMarkerStack = NULL;
247 	uwiMarkerStackLength = 0;
248 	uugcDeactive();
249 }
250 
uwiGetC()251 extern int uwiGetC ()
252 {
253 	int c;
254 	uugcChar *chr = uugciGetC ();
255 
256 	c = chr->c;
257 
258 	if (uwiCurrentMarker)
259 	{
260 		*uwiCurrentMarker += 1;
261 		ptrArrayAdd (uwiBuffer, chr);
262 	}
263 	else
264 	{
265 		uugcCurrentChar = NULL;
266 		uugcDeleteC (chr);
267 	}
268 
269 	return c;
270 }
271 
uwiUngetC(int c)272 extern void uwiUngetC (int c)
273 {
274 	Assert (!uwiCurrentMarker);
275 	uugcInjectC (c);
276 }
277 
uwiGetLineNumber(void)278 extern unsigned long uwiGetLineNumber (void)
279 {
280 	return uugcGetLineNumber ();
281 }
282 
uwiGetFilePosition(void)283 extern MIOPos uwiGetFilePosition (void)
284 {
285 	return uugcGetFilePosition ();
286 }
287 
uwiPushMarker(void)288 extern void uwiPushMarker (void)
289 {
290 
291 	if (uwiStats.maxLength < (uwiCurrentMarker - uwiMarkerStack) + 1)
292 		uwiStats.maxLength = (uwiCurrentMarker - uwiMarkerStack) + 1;
293 
294 	if (uwiCurrentMarker - uwiMarkerStack >= ( uwiMarkerStackLength - 1 )) {
295 		error (WARNING,
296 			"trying to add too many markers during parsing: %s "
297 			"(this is a bug, please consider filing an issue)", getInputFileName());
298 		uwiCurrentMarker = NULL;
299 		uwiStats.overflow = true;
300 	}
301 
302 	if (uwiCurrentMarker) uwiCurrentMarker++;
303 	else uwiCurrentMarker = uwiMarkerStack;
304 
305 	*uwiCurrentMarker = 0;
306 }
307 
uwiPopMarker(const int upto,const bool revertChars)308 extern void uwiPopMarker (const int upto, const bool revertChars)
309 {
310 	if (uwiCurrentMarker - uwiMarkerStack < 0) {
311 		error (WARNING,
312 				"trying to drop too many markers during parsing: %s "
313 				"(this is a bug, please consider filing an issue)", getInputFileName());
314 
315 		uwiCurrentMarker = NULL;
316 		uwiStats.underflow = true;
317 		return;
318 	}
319 
320 	uwiClearMarker (upto, revertChars);
321 
322 	if (uwiCurrentMarker == uwiMarkerStack) uwiCurrentMarker = NULL;
323 	else uwiCurrentMarker--;
324 }
325 
uwiClearMarker(const int upto,const bool revertChars)326 extern void uwiClearMarker (const int upto, const bool revertChars)
327 {
328 	Assert (uwiCurrentMarker);
329 	int count = (upto <= 0)? *uwiCurrentMarker : upto;
330 	void (*charHandler)(uugcChar *) = revertChars ? uugcUngetC : uugcDeleteC;
331 
332 	while (count-- > 0)
333 	{
334 		charHandler (ptrArrayLast (uwiBuffer));
335 		ptrArrayRemoveLast (uwiBuffer);
336 		*uwiCurrentMarker -= 1;
337 	}
338 }
339 
uwiDropMaker()340 extern void uwiDropMaker ()
341 {
342 	uwiPopMarker (0, false);
343 }
344 
uwiStatsInit(struct sUwiStats * stats)345 extern void uwiStatsInit (struct sUwiStats *stats)
346 {
347 	memset (stats, 0, sizeof (*stats));
348 }
349 
uwiStatsPrint(struct sUwiStats * stats)350 extern void uwiStatsPrint (struct sUwiStats *stats)
351 {
352 	fprintf(stderr, "Unwinding the longest input stream stack usage: %d\n",
353 			stats->maxLength);
354 	fprintf(stderr, "Unwinding input stream stack overflow incidence: %s\n",
355 			stats->overflow? "yes": "no");
356 	fprintf(stderr, "Unwinding input stream stack underflow incidence: %s\n",
357 			stats->underflow? "yes": "no");
358 }
359