xref: /Universal-ctags/main/args.c (revision 3f0fa2cf1fd77f227cc9f09d0fe512db7502f808)
1 /*
2 *   Copyright (c) 1999-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 *   This module contains functions for reading command line arguments.
8 */
9 
10 /*
11 *   INCLUDE FILES
12 */
13 #include "general.h"  /* must always come first */
14 
15 #include <stdio.h>
16 #include <string.h>
17 #include <ctype.h>
18 
19 #include "args_p.h"
20 #include "debug.h"
21 #include "routines.h"
22 #include "vstring.h"
23 
24 /*
25 *   FUNCTION DEFINITIONS
26 */
27 
nextStringArg(const char ** const next)28 static char *nextStringArg (const char** const next)
29 {
30 	char* result = NULL;
31 	const char* start;
32 
33 	Assert (*next != NULL);
34 	for (start = *next  ;  isspace ((int) *start)  ;  ++start)
35 		;
36 	if (*start == '\0')
37 		*next = start;
38 	else
39 	{
40 		size_t length;
41 		const char* end;
42 
43 		for (end = start ;  *end != '\0'  &&  ! isspace ((int) *end)  ;  ++end)
44 			;
45 		length = end - start;
46 		Assert (length > 0);
47 		result = xMalloc (length + 1, char);
48 		strncpy (result, start, length);
49 		result [length] = '\0';
50 		*next = end;
51 	}
52 	return result;
53 }
54 
nextStringLine(const char ** const next)55 static char* nextStringLine (const char** const next)
56 {
57 	char* result = NULL;
58 	size_t length;
59 	const char* end;
60 
61 	Assert (*next != NULL);
62 	for (end = *next ;  *end != '\n'  &&  *end != '\0' ;  ++end)
63 		;
64 	length = end - *next;
65 	if (length > 0)
66 	{
67 		result = xMalloc (length + 1, char);
68 		strncpy (result, *next, length);
69 		result [length] = '\0';
70 	}
71 	if (*end == '\n')
72 		++end;
73 	else if (*end == '\r')
74 	{
75 		++end;
76 		if (*end == '\n')
77 			++end;
78 	}
79 	*next = end;
80 	return result;
81 }
82 
nextString(const Arguments * const current,const char ** const next)83 static char* nextString (const Arguments* const current, const char** const next)
84 {
85 	char* result;
86 	if (current->lineMode)
87 		result = nextStringLine (next);
88 	else
89 		result = nextStringArg (next);
90 	return result;
91 }
92 
nextFileArg(FILE * const fp)93 static char* nextFileArg (FILE* const fp)
94 {
95 	char* result = NULL;
96 	Assert (fp != NULL);
97 	if (! feof (fp))
98 	{
99 		vString* vs = vStringNew ();
100 		int c;
101 		do
102 			c = fgetc (fp);
103 		while (isspace (c));
104 
105 		if (c != EOF)
106 		{
107 			do
108 			{
109 				vStringPut (vs, c);
110 				c = fgetc (fp);
111 			} while (c != EOF  &&  ! isspace (c));
112 			Assert (vStringLength (vs) > 0);
113 			result = xMalloc (vStringLength (vs) + 1, char);
114 			strcpy (result, vStringValue (vs));
115 		}
116 		vStringDelete (vs);
117 	}
118 	return result;
119 }
120 
nextFileLine(FILE * const fp)121 static char* nextFileLine (FILE* const fp)
122 {
123 	char* result = NULL;
124 	Assert (fp != NULL);
125 	if (! feof (fp))
126 	{
127 		vString* vs = vStringNew ();
128 		int c;
129 
130 		c = fgetc (fp);
131 		while (c != EOF)
132 		{
133 			if (c != '\n'  &&  c != '\r')
134 				vStringPut (vs, c);
135 			else if (vStringLength (vs) > 0)
136 				break;
137 			c = fgetc (fp);
138 		}
139 		if (c != EOF  ||  vStringLength (vs) > 0)
140 		{
141 			if (c == '\r')
142 			{
143 				c = fgetc (fp);
144 				if (c != '\n')
145 					c = ungetc (c, fp);
146 			}
147 			vStringStripTrailing (vs);
148 			vStringStripLeading (vs);
149 			result = xMalloc (vStringLength (vs) + 1, char);
150 			strcpy (result, vStringValue (vs));
151 		}
152 		vStringDelete (vs);
153 	}
154 	return result;
155 }
156 
isCommentLine(char * line)157 static bool isCommentLine (char* line)
158 {
159 	while (isspace(*line))
160 		++line;
161 	return (*line == '#');
162 }
163 
isOptscriptLine(char * line)164 static bool isOptscriptLine (char *line)
165 {
166 	size_t len = strlen (line);
167 	if (len < 2)
168 		return false;
169 	if (line [len - 1] == '{' && line [len - 2] == '{')
170 		return true;
171 	return false;
172 }
173 
nextOptscriptLines(FILE * const fp,char * line)174 static char* nextOptscriptLines (FILE* const fp, char *line)
175 {
176 	vString *vstr = vStringNewInit (line);
177 	vStringPut (vstr, '\n');
178 	eFree (line);
179 
180 	/* \n}}, \n=>1, }=>2, }=>3  */
181 	int endMarkers = 0;
182 	int c;
183 	while (true)
184 	{
185 		c = fgetc (fp);
186 		if (c == EOF)
187 			break;
188 
189 		if (c == '\r' || c == '\n')
190 		{
191 			if (c == '\r')
192 			{
193 				c = fgetc (fp);
194 				if (c != '\n')
195 				{
196 					ungetc(c, fp);
197 					c = '\n';
198 				}
199 			}
200 			if (c == '\n')
201 			{
202 				vStringPut (vstr, c);
203 				if (endMarkers != 1)
204 					endMarkers = 1;
205 			}
206 		}
207 		else if (c == '}')
208 		{
209 			vStringPut (vstr, c);
210 			if (endMarkers == 1 || endMarkers == 2)
211 				endMarkers++;
212 			if (endMarkers == 3)
213 				break;
214 		}
215 		else
216 		{
217 			endMarkers = 0;
218 			vStringPut (vstr, c);
219 		}
220 	}
221 
222 	if (c == EOF)
223 	{
224 		switch (endMarkers)
225 		{
226 		case 0:
227 			vStringPut (vstr, '\n');
228 			/* Fall through */
229 		case 1:
230 			vStringPut (vstr, '}');
231 			/* Fall through */
232 		case 2:
233 			vStringPut (vstr, '}');
234 		default:
235 			break;
236 		}
237 	}
238 
239 	c = fgetc (fp);
240 	while (c != EOF)
241 	{
242 		if (c == '\n')
243 			break;
244 		if (c == '\r')
245 		{
246 			c = fgetc (fp);
247 			if (c == '\n')
248 				break;
249 			ungetc (c, fp);
250 		}
251 		c = fgetc (fp);
252 	}
253 	return vStringDeleteUnwrap (vstr);
254 }
255 
nextFileLineSkippingComments(FILE * const fp)256 static char* nextFileLineSkippingComments (FILE* const fp)
257 {
258 	char* result;
259 	bool comment;
260 	bool optscript;
261 
262 	do
263 	{
264 		result = nextFileLine (fp);
265 		comment = false;
266 		optscript = false;
267 		if (result)
268 		{
269 			comment = isCommentLine (result);
270 			optscript = isOptscriptLine (result);
271 		}
272 		if (comment)
273 			eFree (result);
274 		else if (optscript)
275 			result = nextOptscriptLines (fp, result);
276 	} while (comment);
277 
278 	return result;
279 }
280 
nextFileString(const Arguments * const current,FILE * const fp)281 static char* nextFileString (const Arguments* const current, FILE* const fp)
282 {
283 	char* result;
284 	if (current->lineMode)
285 		result = nextFileLineSkippingComments (fp);
286 	else
287 		result = nextFileArg (fp);
288 	return result;
289 }
290 
argNewFromString(const char * const string)291 extern Arguments* argNewFromString (const char* const string)
292 {
293 	Arguments* result = xMalloc (1, Arguments);
294 	memset (result, 0, sizeof (Arguments));
295 	result->type = ARG_STRING;
296 	result->u.stringArgs.next = string;
297 	result->item = nextString (result, &result->u.stringArgs.next);
298 	return result;
299 }
300 
argNewFromArgv(char * const * const argv)301 extern Arguments* argNewFromArgv (char* const* const argv)
302 {
303 	Arguments* result = xMalloc (1, Arguments);
304 	memset (result, 0, sizeof (Arguments));
305 	result->type = ARG_ARGV;
306 	result->u.argvArgs.argv = argv;
307 	result->u.argvArgs.item = result->u.argvArgs.argv;
308 	result->item = *result->u.argvArgs.item;
309 	return result;
310 }
311 
argNewFromFile(FILE * const fp)312 extern Arguments* argNewFromFile (FILE* const fp)
313 {
314 	Arguments* result = xMalloc (1, Arguments);
315 	memset (result, 0, sizeof (Arguments));
316 	result->type = ARG_FILE;
317 	result->u.fileArgs.fp = fp;
318 	result->item = nextFileString (result, result->u.fileArgs.fp);
319 	return result;
320 }
321 
argNewFromLineFile(FILE * const fp)322 extern Arguments* argNewFromLineFile (FILE* const fp)
323 {
324 	Arguments* result = xMalloc (1, Arguments);
325 	memset (result, 0, sizeof (Arguments));
326 	result->type = ARG_FILE;
327 	result->lineMode = true;
328 	result->u.fileArgs.fp = fp;
329 	result->item = nextFileString (result, result->u.fileArgs.fp);
330 	return result;
331 }
332 
argItem(const Arguments * const current)333 extern char *argItem (const Arguments* const current)
334 {
335 	Assert (current != NULL);
336 	Assert (! argOff (current));
337 	return current->item;
338 }
339 
argOff(const Arguments * const current)340 extern bool argOff (const Arguments* const current)
341 {
342 	Assert (current != NULL);
343 	return (bool) (current->item == NULL);
344 }
345 
argSetWordMode(Arguments * const current)346 extern void argSetWordMode (Arguments* const current)
347 {
348 	Assert (current != NULL);
349 	current->lineMode = false;
350 }
351 
argSetLineMode(Arguments * const current)352 extern void argSetLineMode (Arguments* const current)
353 {
354 	Assert (current != NULL);
355 	current->lineMode = true;
356 }
357 
argForth(Arguments * const current)358 extern void argForth (Arguments* const current)
359 {
360 	Assert (current != NULL);
361 	Assert (! argOff (current));
362 	switch (current->type)
363 	{
364 		case ARG_STRING:
365 			if (current->item != NULL)
366 				eFree (current->item);
367 			current->item = nextString (current, &current->u.stringArgs.next);
368 			break;
369 		case ARG_ARGV:
370 			++current->u.argvArgs.item;
371 			current->item = *current->u.argvArgs.item;
372 			break;
373 		case ARG_FILE:
374 			if (current->item != NULL)
375 				eFree (current->item);
376 			current->item = nextFileString (current, current->u.fileArgs.fp);
377 			break;
378 		default:
379 			Assert ("Invalid argument type" == NULL);
380 			break;
381 	}
382 }
383 
argDelete(Arguments * const current)384 extern void argDelete (Arguments* const current)
385 {
386 	Assert (current != NULL);
387 	if ((current->type ==  ARG_STRING
388 		 || current->type ==  ARG_FILE) &&  current->item != NULL)
389 		eFree (current->item);
390 	memset (current, 0, sizeof (Arguments));
391 	eFree (current);
392 }
393