FreeBasic: Update keywords
[geany-mirror.git] / tagmanager / ctags / read.c
blob2c4537f472fcd65c4a3552aaa3b43d310591a086
1 /*
3 * Copyright (c) 1996-2001, Darren Hiebert
5 * This source code is released for free distribution under the terms of the
6 * GNU General Public License.
8 * This module contains low level source and tag file read functions (newline
9 * conversion for source files are performed at this level).
13 * INCLUDE FILES
15 #include "general.h" /* must always come first */
17 #include <string.h>
18 #include <ctype.h>
19 #include <glib/gstdio.h>
21 #define FILE_WRITE
22 #include "read.h"
23 #include "entry.h"
24 #include "main.h"
25 #include "options.h"
28 * DATA DEFINITIONS
30 inputFile File; /* globally read through macros */
31 static MIOPos StartOfLine; /* holds deferred position of start of line */
35 /* Read a character choosing automatically between file or buffer, depending
36 * on which mode we are.
38 #define readNextChar() (mio_getc (File.mio))
40 /* Replaces ungetc() for file. In case of buffer we'll perform the same action:
41 * fpBufferPosition-- and write of the param char into the buf.
43 #define pushBackChar(c) (mio_ungetc (File.mio, c))
46 * FUNCTION DEFINITIONS
49 extern void freeSourceFileResources (void)
51 vStringDelete (File.name);
52 vStringDelete (File.path);
53 vStringDelete (File.source.name);
54 vStringDelete (File.line);
58 * Source file access functions
61 static void setInputFileName (const char *const fileName)
63 const char *const head = fileName;
64 const char *const tail = baseFilename (head);
66 if (File.name != NULL)
67 vStringDelete (File.name);
68 File.name = vStringNewInit (fileName);
70 if (File.path != NULL)
71 vStringDelete (File.path);
72 if (tail == head)
73 File.path = NULL;
74 else
76 const size_t length = tail - head - 1;
77 File.path = vStringNew ();
78 vStringNCopyS (File.path, fileName, length);
81 static void setSourceFileParameters (vString *const fileName, const langType language)
83 if (File.source.name != NULL)
84 vStringDelete (File.source.name);
85 File.source.name = fileName;
87 if (File.source.tagPath != NULL)
88 eFree (File.source.tagPath);
89 if (! Option.tagRelative || isAbsolutePath (vStringValue (fileName)))
90 File.source.tagPath = eStrdup (vStringValue (fileName));
91 else
92 File.source.tagPath =
93 relativeFilename (vStringValue (fileName), TagFile.directory);
95 if (vStringLength (fileName) > TagFile.max.file)
96 TagFile.max.file = vStringLength (fileName);
98 File.source.isHeader = isIncludeFile (vStringValue (fileName));
99 if (language != -1)
100 File.source.language = language;
101 else
102 File.source.language = getFileLanguage (vStringValue (fileName));
105 static boolean setSourceFileName (vString *const fileName)
107 boolean result = FALSE;
108 if (getFileLanguage (vStringValue (fileName)) != LANG_IGNORE)
110 vString *pathName;
111 if (isAbsolutePath (vStringValue (fileName)) || File.path == NULL)
112 pathName = vStringNewCopy (fileName);
113 else
114 pathName = combinePathAndFile (vStringValue (File.path),
115 vStringValue (fileName));
116 setSourceFileParameters (pathName, -1);
117 result = TRUE;
119 return result;
123 * Line directive parsing
126 static int skipWhite (void)
128 int c;
130 c = readNextChar ();
131 while (c == ' ' || c == '\t');
132 return c;
135 static unsigned long readLineNumber (void)
137 unsigned long lNum = 0;
138 int c = skipWhite ();
139 while (c != EOF && isdigit (c))
141 lNum = (lNum * 10) + (c - '0');
142 c = readNextChar ();
144 pushBackChar (c);
145 if (c != ' ' && c != '\t')
146 lNum = 0;
148 return lNum;
151 /* While ANSI only permits lines of the form:
152 * # line n "filename"
153 * Earlier compilers generated lines of the form
154 * # n filename
155 * GNU C will output lines of the form:
156 * # n "filename"
157 * So we need to be fairly flexible in what we accept.
159 static vString *readFileName (void)
161 vString *const fileName = vStringNew ();
162 boolean quoteDelimited = FALSE;
163 int c = skipWhite ();
165 if (c == '"')
167 c = readNextChar (); /* skip double-quote */
168 quoteDelimited = TRUE;
170 while (c != EOF && c != '\n' &&
171 (quoteDelimited ? (c != '"') : (c != ' ' && c != '\t')))
173 vStringPut (fileName, c);
174 c = readNextChar ();
176 if (c == '\n')
177 pushBackChar (c);
178 vStringPut (fileName, '\0');
180 return fileName;
183 static boolean parseLineDirective (void)
185 boolean result = FALSE;
186 int c = skipWhite ();
187 DebugStatement ( const char* lineStr = ""; )
189 if (isdigit (c))
191 pushBackChar (c);
192 result = TRUE;
194 else if (c == 'l' && readNextChar () == 'i' &&
195 readNextChar () == 'n' && readNextChar () == 'e')
197 c = readNextChar ();
198 if (c == ' ' || c == '\t')
200 DebugStatement ( lineStr = "line"; )
201 result = TRUE;
204 if (result)
206 const unsigned long lNum = readLineNumber ();
207 if (lNum == 0)
208 result = FALSE;
209 else
211 vString *const fileName = readFileName ();
212 if (vStringLength (fileName) == 0)
214 File.source.lineNumber = lNum - 1; /* applies to NEXT line */
215 DebugStatement ( debugPrintf (DEBUG_RAW, "#%s %ld", lineStr, lNum); )
217 else if (setSourceFileName (fileName))
219 File.source.lineNumber = lNum - 1; /* applies to NEXT line */
220 DebugStatement ( debugPrintf (DEBUG_RAW, "#%s %ld \"%s\"",
221 lineStr, lNum, vStringValue (fileName)); )
224 if (Option.include.fileNames && vStringLength (fileName) > 0 &&
225 lNum == 1)
227 tagEntryInfo tag;
228 initTagEntry (&tag, baseFilename (vStringValue (fileName)));
230 tag.isFileEntry = TRUE;
231 tag.lineNumberEntry = TRUE;
232 tag.lineNumber = 1;
233 tag.kindName = "file";
234 tag.kind = 'F';
236 makeTagEntry (&tag);
238 vStringDelete (fileName);
239 result = TRUE;
242 return result;
246 * Source file I/O operations
249 /* This function opens a source file, and resets the line counter. If it
250 * fails, it will display an error message and leave the File.fp set to NULL.
252 extern boolean fileOpen (const char *const fileName, const langType language)
254 #ifdef VMS
255 const char *const openMode = "r";
256 #else
257 const char *const openMode = "rb";
258 #endif
259 boolean opened = FALSE;
261 /* If another file was already open, then close it.
263 if (File.mio != NULL)
265 mio_free (File.mio); /* close any open source file */
266 File.mio = NULL;
269 File.mio = mio_new_file_full (fileName, openMode, g_fopen, fclose);
270 if (File.mio == NULL)
271 error (WARNING | PERROR, "cannot open \"%s\"", fileName);
272 else
274 opened = TRUE;
276 setInputFileName (fileName);
277 mio_getpos (File.mio, &StartOfLine);
278 mio_getpos (File.mio, &File.filePosition);
279 File.currentLine = NULL;
280 File.lineNumber = 0L;
281 File.eof = FALSE;
282 File.newLine = TRUE;
284 if (File.line != NULL)
285 vStringClear (File.line);
287 setSourceFileParameters (vStringNewInit (fileName), language);
288 File.source.lineNumber = 0L;
290 verbose ("OPENING %s as %s language %sfile\n", fileName,
291 getLanguageName (language),
292 File.source.isHeader ? "include " : "");
294 return opened;
297 /* The user should take care of allocate and free the buffer param.
298 * This func is NOT THREAD SAFE.
299 * The user should not tamper with the buffer while this func is executing.
301 extern boolean bufferOpen (unsigned char *buffer, int buffer_size,
302 const char *const fileName, const langType language )
304 boolean opened = FALSE;
306 /* Check whether a file of a buffer were already open, then close them.
308 if (File.mio != NULL) {
309 mio_free (File.mio); /* close any open source file */
310 File.mio = NULL;
313 /* check if we got a good buffer */
314 if (buffer == NULL || buffer_size == 0) {
315 opened = FALSE;
316 return opened;
319 opened = TRUE;
321 File.mio = mio_new_memory (buffer, buffer_size, NULL, NULL);
322 setInputFileName (fileName);
323 mio_getpos (File.mio, &StartOfLine);
324 mio_getpos (File.mio, &File.filePosition);
325 File.currentLine = NULL;
326 File.lineNumber = 0L;
327 File.eof = FALSE;
328 File.newLine = TRUE;
330 if (File.line != NULL)
331 vStringClear (File.line);
333 setSourceFileParameters (vStringNewInit (fileName), language);
334 File.source.lineNumber = 0L;
336 verbose ("OPENING %s as %s language %sfile\n", fileName,
337 getLanguageName (language),
338 File.source.isHeader ? "include " : "");
340 return opened;
343 extern void fileClose (void)
345 if (File.mio != NULL)
347 /* The line count of the file is 1 too big, since it is one-based
348 * and is incremented upon each newline.
350 if (Option.printTotals)
351 addTotals (0, File.lineNumber - 1L,
352 getFileSize (vStringValue (File.name)));
354 mio_free (File.mio);
355 File.mio = NULL;
359 extern boolean fileEOF (void)
361 return File.eof;
364 /* Action to take for each encountered source newline.
366 static void fileNewline (void)
368 File.filePosition = StartOfLine;
369 File.newLine = FALSE;
370 File.lineNumber++;
371 File.source.lineNumber++;
372 DebugStatement ( if (Option.breakLine == File.lineNumber) lineBreak (); )
373 DebugStatement ( debugPrintf (DEBUG_RAW, "%6ld: ", File.lineNumber); )
376 /* This function reads a single character from the stream, performing newline
377 * canonicalization.
379 static int iFileGetc (void)
381 int c;
382 readnext:
383 c = readNextChar ();
385 /* If previous character was a newline, then we're starting a line.
387 if (File.newLine && c != EOF)
389 fileNewline ();
390 if (c == '#' && Option.lineDirectives)
392 if (parseLineDirective ())
393 goto readnext;
394 else
396 mio_setpos (File.mio, &StartOfLine);
398 c = readNextChar ();
403 if (c == EOF)
404 File.eof = TRUE;
405 else if (c == NEWLINE)
407 File.newLine = TRUE;
408 mio_getpos (File.mio, &StartOfLine);
410 else if (c == CRETURN)
412 /* Turn line breaks into a canonical form. The three commonly
413 * used forms if line breaks: LF (UNIX), CR (MacIntosh), and
414 * CR-LF (MS-DOS) are converted into a generic newline.
416 const int next = readNextChar (); /* is CR followed by LF? */
418 if (next != NEWLINE)
419 pushBackChar (next);
421 c = NEWLINE; /* convert CR into newline */
422 File.newLine = TRUE;
423 mio_getpos (File.mio, &StartOfLine);
425 DebugStatement ( debugPutc (DEBUG_RAW, c); )
426 return c;
429 extern void fileUngetc (int c)
431 File.ungetch = c;
434 static vString *iFileGetLine (void)
436 vString *result = NULL;
437 int c;
438 if (File.line == NULL)
439 File.line = vStringNew ();
440 vStringClear (File.line);
443 c = iFileGetc ();
444 if (c != EOF)
445 vStringPut (File.line, c);
446 if (c == '\n' || (c == EOF && vStringLength (File.line) > 0))
448 vStringTerminate (File.line);
449 #ifdef HAVE_REGEX
450 if (vStringLength (File.line) > 0)
451 matchRegex (File.line, File.source.language);
452 #endif
453 result = File.line;
454 break;
456 } while (c != EOF);
457 Assert (result != NULL || File.eof);
458 return result;
461 /* Do not mix use of fileReadLine () and fileGetc () for the same file.
463 extern int fileGetc (void)
465 int c;
467 /* If there is an ungotten character, then return it. Don't do any
468 * other processing on it, though, because we already did that the
469 * first time it was read through fileGetc ().
471 if (File.ungetch != '\0')
473 c = File.ungetch;
474 File.ungetch = '\0';
475 return c; /* return here to avoid re-calling debugPutc () */
479 if (File.currentLine != NULL)
481 c = *File.currentLine++;
482 if (c == '\0')
483 File.currentLine = NULL;
485 else
487 vString* const line = iFileGetLine ();
488 if (line != NULL)
489 File.currentLine = (unsigned char*) vStringValue (line);
490 if (File.currentLine == NULL)
491 c = EOF;
492 else
493 c = '\0';
495 } while (c == '\0');
496 DebugStatement ( debugPutc (DEBUG_READ, c); )
497 return c;
500 extern int fileSkipToCharacter (int c)
502 int d;
505 d = fileGetc ();
506 } while (d != EOF && d != c);
507 return d;
510 /* An alternative interface to fileGetc (). Do not mix use of fileReadLine()
511 * and fileGetc() for the same file. The returned string does not contain
512 * the terminating newline. A NULL return value means that all lines in the
513 * file have been read and we are at the end of file.
515 extern const unsigned char *fileReadLine (void)
517 vString* const line = iFileGetLine ();
518 const unsigned char* result = NULL;
519 if (line != NULL)
521 result = (const unsigned char*) vStringValue (line);
522 vStringStripNewline (line);
523 DebugStatement ( debugPrintf (DEBUG_READ, "%s\n", result); )
525 return result;
530 * Source file line reading with automatic buffer sizing
532 extern char *readLine (vString *const vLine, MIO *const mio)
534 char *result = NULL;
536 vStringClear (vLine);
537 if (mio == NULL) /* to free memory allocated to buffer */
538 error (FATAL, "NULL MIO pointer");
539 else
541 boolean reReadLine;
543 /* If reading the line places any character other than a null or a
544 * newline at the last character position in the buffer (one less
545 * than the buffer size), then we must resize the buffer and
546 * reattempt to read the line.
550 char *const pLastChar = vStringValue (vLine) + vStringSize (vLine) -2;
551 MIOPos startOfLine;
553 mio_getpos (mio, &startOfLine);
554 reReadLine = FALSE;
555 *pLastChar = '\0';
556 result = mio_gets (mio, vStringValue (vLine), (int) vStringSize (vLine));
557 if (result == NULL)
559 if (! mio_eof (mio))
560 error (FATAL | PERROR, "Failure on attempt to read file");
562 else if (*pLastChar != '\0' &&
563 *pLastChar != '\n' && *pLastChar != '\r')
565 /* buffer overflow */
566 reReadLine = vStringAutoResize (vLine);
567 if (reReadLine)
568 mio_setpos (mio, &startOfLine);
569 else
570 error (FATAL | PERROR, "input line too big; out of memory");
572 else
574 char* eol;
575 vStringSetLength (vLine);
576 /* canonicalize new line */
577 eol = vStringValue (vLine) + vStringLength (vLine) - 1;
578 if (*eol == '\r')
579 *eol = '\n';
580 else if (*(eol - 1) == '\r' && *eol == '\n')
582 *(eol - 1) = '\n';
583 *eol = '\0';
584 --vLine->length;
587 } while (reReadLine);
589 return result;
592 /* Places into the line buffer the contents of the line referenced by
593 * "location".
595 extern char *readSourceLine (vString *const vLine, MIOPos location,
596 long *const pSeekValue)
598 MIOPos orignalPosition;
599 char *result;
601 mio_getpos (File.mio, &orignalPosition);
602 mio_setpos (File.mio, &location);
603 if (pSeekValue != NULL)
604 *pSeekValue = mio_tell (File.mio);
605 result = readLine (vLine, File.mio);
606 if (result == NULL)
607 error (FATAL, "Unexpected end of file: %s", vStringValue (File.name));
608 mio_setpos (File.mio, &orignalPosition);
610 return result;
613 /* vi:set tabstop=8 shiftwidth=4: */