Making "inline" behave like an attribute. Fixes #1
[arduino-ctags.git] / read.c
blob7940c8687558355067b8b8a9194999617fbafcac
1 /*
2 * $Id: read.c 708 2009-07-04 05:29:02Z dhiebert $
4 * Copyright (c) 1996-2002, Darren Hiebert
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License.
9 * This module contains low level source and tag file read functions (newline
10 * conversion for source files are performed at this level).
14 * INCLUDE FILES
16 #include "general.h" /* must always come first */
18 #include <string.h>
19 #include <ctype.h>
21 #define FILE_WRITE
22 #include "read.h"
23 #include "debug.h"
24 #include "entry.h"
25 #include "main.h"
26 #include "routines.h"
27 #include "options.h"
30 * DATA DEFINITIONS
32 inputFile File; /* globally read through macros */
33 static fpos_t StartOfLine; /* holds deferred position of start of line */
36 * FUNCTION DEFINITIONS
39 extern void freeSourceFileResources (void)
41 if (File.name != NULL)
42 vStringDelete (File.name);
43 if (File.path != NULL)
44 vStringDelete (File.path);
45 if (File.source.name != NULL)
46 vStringDelete (File.source.name);
47 if (File.source.tagPath != NULL)
48 eFree (File.source.tagPath);
49 if (File.line != NULL)
50 vStringDelete (File.line);
54 * Source file access functions
57 static void setInputFileName (const char *const fileName)
59 const char *const head = fileName;
60 const char *const tail = baseFilename (head);
62 if (File.name != NULL)
63 vStringDelete (File.name);
64 File.name = vStringNewInit (fileName);
66 if (File.path != NULL)
67 vStringDelete (File.path);
68 if (tail == head)
69 File.path = NULL;
70 else
72 const size_t length = tail - head - 1;
73 File.path = vStringNew ();
74 vStringNCopyS (File.path, fileName, length);
78 static void setSourceFileParameters (vString *const fileName)
80 if (File.source.name != NULL)
81 vStringDelete (File.source.name);
82 File.source.name = fileName;
84 if (File.source.tagPath != NULL)
85 eFree (File.source.tagPath);
86 if (! Option.tagRelative || isAbsolutePath (vStringValue (fileName)))
87 File.source.tagPath = eStrdup (vStringValue (fileName));
88 else
89 File.source.tagPath =
90 relativeFilename (vStringValue (fileName), TagFile.directory);
92 if (vStringLength (fileName) > TagFile.max.file)
93 TagFile.max.file = vStringLength (fileName);
95 File.source.isHeader = isIncludeFile (vStringValue (fileName));
96 File.source.language = getFileLanguage (vStringValue (fileName));
99 static boolean setSourceFileName (vString *const fileName)
101 boolean result = FALSE;
102 if (getFileLanguage (vStringValue (fileName)) != LANG_IGNORE)
104 vString *pathName;
105 if (isAbsolutePath (vStringValue (fileName)) || File.path == NULL)
106 pathName = vStringNewCopy (fileName);
107 else
108 pathName = combinePathAndFile (
109 vStringValue (File.path), vStringValue (fileName));
110 setSourceFileParameters (pathName);
111 result = TRUE;
113 return result;
117 * Line directive parsing
120 static int skipWhite (void)
122 int c;
124 c = getc (File.fp);
125 while (c == ' ' || c == '\t');
126 return c;
129 static unsigned long readLineNumber (void)
131 unsigned long lNum = 0;
132 int c = skipWhite ();
133 while (c != EOF && isdigit (c))
135 lNum = (lNum * 10) + (c - '0');
136 c = getc (File.fp);
138 ungetc (c, File.fp);
139 if (c != ' ' && c != '\t')
140 lNum = 0;
142 return lNum;
145 /* While ANSI only permits lines of the form:
146 * # line n "filename"
147 * Earlier compilers generated lines of the form
148 * # n filename
149 * GNU C will output lines of the form:
150 * # n "filename"
151 * So we need to be fairly flexible in what we accept.
153 static vString *readFileName (void)
155 vString *const fileName = vStringNew ();
156 boolean quoteDelimited = FALSE;
157 int c = skipWhite ();
159 if (c == '"')
161 c = getc (File.fp); /* skip double-quote */
162 quoteDelimited = TRUE;
164 while (c != EOF && c != '\n' &&
165 (quoteDelimited ? (c != '"') : (c != ' ' && c != '\t')))
167 vStringPut (fileName, c);
168 c = getc (File.fp);
170 if (c == '\n')
171 ungetc (c, File.fp);
172 vStringPut (fileName, '\0');
174 return fileName;
177 static boolean parseLineDirective (void)
179 boolean result = FALSE;
180 int c = skipWhite ();
181 DebugStatement ( const char* lineStr = ""; )
183 if (isdigit (c))
185 ungetc (c, File.fp);
186 result = TRUE;
188 else if (c == 'l' && getc (File.fp) == 'i' &&
189 getc (File.fp) == 'n' && getc (File.fp) == 'e')
191 c = getc (File.fp);
192 if (c == ' ' || c == '\t')
194 DebugStatement ( lineStr = "line"; )
195 result = TRUE;
198 if (result)
200 const unsigned long lNum = readLineNumber ();
201 if (lNum == 0)
202 result = FALSE;
203 else
205 vString *const fileName = readFileName ();
206 if (vStringLength (fileName) == 0)
208 File.source.lineNumber = lNum - 1; /* applies to NEXT line */
209 DebugStatement ( debugPrintf (DEBUG_RAW, "#%s %ld", lineStr, lNum); )
211 else if (setSourceFileName (fileName))
213 File.source.lineNumber = lNum - 1; /* applies to NEXT line */
214 DebugStatement ( debugPrintf (DEBUG_RAW, "#%s %ld \"%s\"",
215 lineStr, lNum, vStringValue (fileName)); )
218 if (Option.include.fileNames && vStringLength (fileName) > 0 &&
219 lNum == 1)
221 tagEntryInfo tag;
222 initTagEntry (&tag, baseFilename (vStringValue (fileName)));
224 tag.isFileEntry = TRUE;
225 tag.lineNumberEntry = TRUE;
226 tag.lineNumber = 1;
227 tag.kindName = "file";
228 tag.kind = 'F';
230 makeTagEntry (&tag);
232 vStringDelete (fileName);
233 result = TRUE;
236 return result;
240 * Source file I/O operations
243 /* This function opens a source file, and resets the line counter. If it
244 * fails, it will display an error message and leave the File.fp set to NULL.
246 extern boolean fileOpen (const char *const fileName, const langType language)
248 #ifdef VMS
249 const char *const openMode = "r";
250 #else
251 const char *const openMode = "rb";
252 #endif
253 boolean opened = FALSE;
255 /* If another file was already open, then close it.
257 if (File.fp != NULL)
259 fclose (File.fp); /* close any open source file */
260 File.fp = NULL;
263 File.fp = fopen (fileName, openMode);
264 if (File.fp == NULL)
265 error (WARNING | PERROR, "cannot open \"%s\"", fileName);
266 else
268 opened = TRUE;
270 setInputFileName (fileName);
271 fgetpos (File.fp, &StartOfLine);
272 fgetpos (File.fp, &File.filePosition);
273 File.currentLine = NULL;
274 File.language = language;
275 File.lineNumber = 0L;
276 File.eof = FALSE;
277 File.newLine = TRUE;
279 if (File.line != NULL)
280 vStringClear (File.line);
282 setSourceFileParameters (vStringNewInit (fileName));
283 File.source.lineNumber = 0L;
285 verbose ("OPENING %s as %s language %sfile\n", fileName,
286 getLanguageName (language),
287 File.source.isHeader ? "include " : "");
289 return opened;
292 extern void fileClose (void)
294 if (File.fp != NULL)
296 /* The line count of the file is 1 too big, since it is one-based
297 * and is incremented upon each newline.
299 if (Option.printTotals)
301 fileStatus *status = eStat (vStringValue (File.name));
302 addTotals (0, File.lineNumber - 1L, status->size);
304 fclose (File.fp);
305 File.fp = NULL;
309 extern boolean fileEOF (void)
311 return File.eof;
314 /* Action to take for each encountered source newline.
316 static void fileNewline (void)
318 File.filePosition = StartOfLine;
319 File.newLine = FALSE;
320 File.lineNumber++;
321 File.source.lineNumber++;
322 DebugStatement ( if (Option.breakLine == File.lineNumber) lineBreak (); )
323 DebugStatement ( debugPrintf (DEBUG_RAW, "%6ld: ", File.lineNumber); )
326 /* This function reads a single character from the stream, performing newline
327 * canonicalization.
329 static int iFileGetc (void)
331 int c;
332 readnext:
333 c = getc (File.fp);
335 /* If previous character was a newline, then we're starting a line.
337 if (File.newLine && c != EOF)
339 fileNewline ();
340 if (c == '#' && Option.lineDirectives)
342 if (parseLineDirective ())
343 goto readnext;
344 else
346 fsetpos (File.fp, &StartOfLine);
347 c = getc (File.fp);
352 if (c == EOF)
353 File.eof = TRUE;
354 else if (c == NEWLINE)
356 File.newLine = TRUE;
357 fgetpos (File.fp, &StartOfLine);
359 else if (c == CRETURN)
361 /* Turn line breaks into a canonical form. The three commonly
362 * used forms if line breaks: LF (UNIX/Mac OS X), CR (Mac OS 9),
363 * and CR-LF (MS-DOS) are converted into a generic newline.
365 #ifndef macintosh
366 const int next = getc (File.fp); /* is CR followed by LF? */
367 if (next != NEWLINE)
368 ungetc (next, File.fp);
369 else
370 #endif
372 c = NEWLINE; /* convert CR into newline */
373 File.newLine = TRUE;
374 fgetpos (File.fp, &StartOfLine);
377 DebugStatement ( debugPutc (DEBUG_RAW, c); )
378 return c;
381 extern void fileUngetc (int c)
383 File.ungetch = c;
386 static vString *iFileGetLine (void)
388 vString *result = NULL;
389 int c;
390 if (File.line == NULL)
391 File.line = vStringNew ();
392 vStringClear (File.line);
395 c = iFileGetc ();
396 if (c != EOF)
397 vStringPut (File.line, c);
398 if (c == '\n' || (c == EOF && vStringLength (File.line) > 0))
400 vStringTerminate (File.line);
401 #ifdef HAVE_REGEX
402 if (vStringLength (File.line) > 0)
403 matchRegex (File.line, File.source.language);
404 #endif
405 result = File.line;
406 break;
408 } while (c != EOF);
409 Assert (result != NULL || File.eof);
410 return result;
413 /* Do not mix use of fileReadLine () and fileGetc () for the same file.
415 extern int fileGetc (void)
417 int c;
419 /* If there is an ungotten character, then return it. Don't do any
420 * other processing on it, though, because we already did that the
421 * first time it was read through fileGetc ().
423 if (File.ungetch != '\0')
425 c = File.ungetch;
426 File.ungetch = '\0';
427 return c; /* return here to avoid re-calling debugPutc () */
431 if (File.currentLine != NULL)
433 c = *File.currentLine++;
434 if (c == '\0')
435 File.currentLine = NULL;
437 else
439 vString* const line = iFileGetLine ();
440 if (line != NULL)
441 File.currentLine = (unsigned char*) vStringValue (line);
442 if (File.currentLine == NULL)
443 c = EOF;
444 else
445 c = '\0';
447 } while (c == '\0');
448 DebugStatement ( debugPutc (DEBUG_READ, c); )
449 return c;
452 extern int fileSkipToCharacter (int c)
454 int d;
457 d = fileGetc ();
458 } while (d != EOF && d != c);
459 return d;
462 /* An alternative interface to fileGetc (). Do not mix use of fileReadLine()
463 * and fileGetc() for the same file. The returned string does not contain
464 * the terminating newline. A NULL return value means that all lines in the
465 * file have been read and we are at the end of file.
467 extern const unsigned char *fileReadLine (void)
469 vString* const line = iFileGetLine ();
470 const unsigned char* result = NULL;
471 if (line != NULL)
473 result = (const unsigned char*) vStringValue (line);
474 vStringStripNewline (line);
475 DebugStatement ( debugPrintf (DEBUG_READ, "%s\n", result); )
477 return result;
481 * Source file line reading with automatic buffer sizing
483 extern char *readLine (vString *const vLine, FILE *const fp)
485 char *result = NULL;
487 vStringClear (vLine);
488 if (fp == NULL) /* to free memory allocated to buffer */
489 error (FATAL, "NULL file pointer");
490 else
492 boolean reReadLine;
494 /* If reading the line places any character other than a null or a
495 * newline at the last character position in the buffer (one less
496 * than the buffer size), then we must resize the buffer and
497 * reattempt to read the line.
501 char *const pLastChar = vStringValue (vLine) + vStringSize (vLine) -2;
502 fpos_t startOfLine;
504 fgetpos (fp, &startOfLine);
505 reReadLine = FALSE;
506 *pLastChar = '\0';
507 result = fgets (vStringValue (vLine), (int) vStringSize (vLine), fp);
508 if (result == NULL)
510 if (! feof (fp))
511 error (FATAL | PERROR, "Failure on attempt to read file");
513 else if (*pLastChar != '\0' &&
514 *pLastChar != '\n' && *pLastChar != '\r')
516 /* buffer overflow */
517 reReadLine = vStringAutoResize (vLine);
518 if (reReadLine)
519 fsetpos (fp, &startOfLine);
520 else
521 error (FATAL | PERROR, "input line too big; out of memory");
523 else
525 char* eol;
526 vStringSetLength (vLine);
527 /* canonicalize new line */
528 eol = vStringValue (vLine) + vStringLength (vLine) - 1;
529 if (*eol == '\r')
530 *eol = '\n';
531 else if (*(eol - 1) == '\r' && *eol == '\n')
533 *(eol - 1) = '\n';
534 *eol = '\0';
535 --vLine->length;
538 } while (reReadLine);
540 return result;
543 /* Places into the line buffer the contents of the line referenced by
544 * "location".
546 extern char *readSourceLine (
547 vString *const vLine, fpos_t location, long *const pSeekValue)
549 fpos_t orignalPosition;
550 char *result;
552 fgetpos (File.fp, &orignalPosition);
553 fsetpos (File.fp, &location);
554 if (pSeekValue != NULL)
555 *pSeekValue = ftell (File.fp);
556 result = readLine (vLine, File.fp);
557 if (result == NULL)
558 error (FATAL, "Unexpected end of file: %s", vStringValue (File.name));
559 fsetpos (File.fp, &orignalPosition);
561 return result;
564 /* vi:set tabstop=4 shiftwidth=4: */