r5123 | ntrel | 2010-08-10 17:12:24 +0100 (Tue, 10 Aug 2010) | 4 lines
[geany-mirror.git] / tagmanager / read.c
blobfbd0616de3b8a66f665fc46508e06212a275b48a
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 fpos_t StartOfLine; /* holds deferred position of start of line */
32 static int bufferStartOfLine; /* the same as StartOfLine but for buffer */
35 static int readNextChar (void);
36 static int pushBackChar (int c);
39 * FUNCTION DEFINITIONS
42 extern void freeSourceFileResources (void)
44 vStringDelete (File.name);
45 vStringDelete (File.path);
46 vStringDelete (File.source.name);
47 vStringDelete (File.line);
51 * Source file access functions
54 static void setInputFileName (const char *const fileName)
56 const char *const head = fileName;
57 const char *const tail = baseFilename (head);
59 if (File.name != NULL)
60 vStringDelete (File.name);
61 File.name = vStringNewInit (fileName);
63 if (File.path != NULL)
64 vStringDelete (File.path);
65 if (tail == head)
66 File.path = NULL;
67 else
69 const size_t length = tail - head - 1;
70 File.path = vStringNew ();
71 vStringNCopyS (File.path, fileName, length);
74 static void setSourceFileParameters (vString *const fileName, const langType language)
76 if (File.source.name != NULL)
77 vStringDelete (File.source.name);
78 File.source.name = fileName;
80 if (File.source.tagPath != NULL)
81 eFree (File.source.tagPath);
82 if (! Option.tagRelative || isAbsolutePath (vStringValue (fileName)))
83 File.source.tagPath = eStrdup (vStringValue (fileName));
84 else
85 File.source.tagPath =
86 relativeFilename (vStringValue (fileName), TagFile.directory);
88 if (vStringLength (fileName) > TagFile.max.file)
89 TagFile.max.file = vStringLength (fileName);
91 File.source.isHeader = isIncludeFile (vStringValue (fileName));
92 if (language != -1)
93 File.source.language = language;
94 else
95 File.source.language = getFileLanguage (vStringValue (fileName));
98 static boolean setSourceFileName (vString *const fileName)
100 boolean result = FALSE;
101 if (getFileLanguage (vStringValue (fileName)) != LANG_IGNORE)
103 vString *pathName;
104 if (isAbsolutePath (vStringValue (fileName)) || File.path == NULL)
105 pathName = vStringNewCopy (fileName);
106 else
107 pathName = combinePathAndFile (vStringValue (File.path),
108 vStringValue (fileName));
109 setSourceFileParameters (pathName, -1);
110 result = TRUE;
112 return result;
116 * Line directive parsing
119 static int skipWhite (void)
121 int c;
123 c = readNextChar ();
124 while (c == ' ' || c == '\t');
125 return c;
128 static unsigned long readLineNumber (void)
130 unsigned long lNum = 0;
131 int c = skipWhite ();
132 while (c != EOF && isdigit (c))
134 lNum = (lNum * 10) + (c - '0');
135 c = readNextChar ();
137 pushBackChar (c);
138 if (c != ' ' && c != '\t')
139 lNum = 0;
141 return lNum;
144 /* While ANSI only permits lines of the form:
145 * # line n "filename"
146 * Earlier compilers generated lines of the form
147 * # n filename
148 * GNU C will output lines of the form:
149 * # n "filename"
150 * So we need to be fairly flexible in what we accept.
152 static vString *readFileName (void)
154 vString *const fileName = vStringNew ();
155 boolean quoteDelimited = FALSE;
156 int c = skipWhite ();
158 if (c == '"')
160 c = readNextChar (); /* skip double-quote */
161 quoteDelimited = TRUE;
163 while (c != EOF && c != '\n' &&
164 (quoteDelimited ? (c != '"') : (c != ' ' && c != '\t')))
166 vStringPut (fileName, c);
167 c = readNextChar ();
169 if (c == '\n')
170 pushBackChar (c);
171 vStringPut (fileName, '\0');
173 return fileName;
176 static boolean parseLineDirective (void)
178 boolean result = FALSE;
179 int c = skipWhite ();
180 DebugStatement ( const char* lineStr = ""; )
182 if (isdigit (c))
184 pushBackChar (c);
185 result = TRUE;
187 else if (c == 'l' && readNextChar () == 'i' &&
188 readNextChar () == 'n' && readNextChar () == 'e')
190 c = readNextChar ();
191 if (c == ' ' || c == '\t')
193 DebugStatement ( lineStr = "line"; )
194 result = TRUE;
197 if (result)
199 const unsigned long lNum = readLineNumber ();
200 if (lNum == 0)
201 result = FALSE;
202 else
204 vString *const fileName = readFileName ();
205 if (vStringLength (fileName) == 0)
207 File.source.lineNumber = lNum - 1; /* applies to NEXT line */
208 DebugStatement ( debugPrintf (DEBUG_RAW, "#%s %ld", lineStr, lNum); )
210 else if (setSourceFileName (fileName))
212 File.source.lineNumber = lNum - 1; /* applies to NEXT line */
213 DebugStatement ( debugPrintf (DEBUG_RAW, "#%s %ld \"%s\"",
214 lineStr, lNum, vStringValue (fileName)); )
217 if (Option.include.fileNames && vStringLength (fileName) > 0 &&
218 lNum == 1)
220 tagEntryInfo tag;
221 initTagEntry (&tag, baseFilename (vStringValue (fileName)));
223 tag.isFileEntry = TRUE;
224 tag.lineNumberEntry = TRUE;
225 tag.lineNumber = 1;
226 tag.kindName = "file";
227 tag.kind = 'F';
229 makeTagEntry (&tag);
231 vStringDelete (fileName);
232 result = TRUE;
235 return result;
239 * Source file I/O operations
242 /* This function opens a source file, and resets the line counter. If it
243 * fails, it will display an error message and leave the File.fp set to NULL.
245 extern boolean fileOpen (const char *const fileName, const langType language)
247 #ifdef VMS
248 const char *const openMode = "r";
249 #else
250 const char *const openMode = "rb";
251 #endif
252 boolean opened = FALSE;
254 /* If another file was already open, then close it.
256 if (File.fp != NULL)
258 fclose (File.fp); /* close any open source file */
259 File.fp = NULL;
262 File.fp = g_fopen (fileName, openMode);
263 if (File.fp == NULL)
264 error (WARNING | PERROR, "cannot open \"%s\"", fileName);
265 else
267 opened = TRUE;
269 setInputFileName (fileName);
270 fgetpos (File.fp, &StartOfLine);
271 fgetpos (File.fp, &File.filePosition);
272 File.currentLine = NULL;
273 File.language = language;
274 File.lineNumber = 0L;
275 File.eof = FALSE;
276 File.newLine = TRUE;
278 if (File.line != NULL)
279 vStringClear (File.line);
281 setSourceFileParameters (vStringNewInit (fileName), language);
282 File.source.lineNumber = 0L;
284 verbose ("OPENING %s as %s language %sfile\n", fileName,
285 getLanguageName (language),
286 File.source.isHeader ? "include " : "");
288 return opened;
291 /* The user should take care of allocate and free the buffer param.
292 * This func is NOT THREAD SAFE.
293 * The user should not tamper with the buffer while this func is executing.
295 extern boolean bufferOpen (unsigned char *buffer, int buffer_size,
296 const char *const fileName, const langType language )
298 boolean opened = FALSE;
300 /* Check whether a file of a buffer were already open, then close them.
302 if (File.fp != NULL) {
303 fclose (File.fp); /* close any open source file */
304 File.fp = NULL;
307 if (File.fpBuffer != NULL) {
308 error(PERROR, "An unallocated buffer was found. Please check you called \
309 correctly bufferClose ()\n");
310 File.fpBuffer = 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.fpBuffer = buffer;
322 setInputFileName (fileName);
323 bufferStartOfLine = 0;
324 File.fpBufferPosition = 0;
325 File.fpBufferSize = buffer_size;
326 File.currentLine = NULL;
327 File.language = language;
328 File.lineNumber = 0L;
329 File.eof = FALSE;
330 File.newLine = TRUE;
332 if (File.line != NULL)
333 vStringClear (File.line);
335 setSourceFileParameters (vStringNewInit (fileName), language);
336 File.source.lineNumber = 0L;
338 verbose ("OPENING %s as %s language %sfile\n", fileName,
339 getLanguageName (language),
340 File.source.isHeader ? "include " : "");
342 return opened;
345 extern void fileClose (void)
347 if (File.fp != NULL)
349 /* The line count of the file is 1 too big, since it is one-based
350 * and is incremented upon each newline.
352 if (Option.printTotals)
353 addTotals (0, File.lineNumber - 1L,
354 getFileSize (vStringValue (File.name)));
356 fclose (File.fp);
357 File.fp = NULL;
361 /* user should take care of freeing the buffer */
362 extern void bufferClose (void)
364 if (File.fpBuffer != NULL) {
365 File.fpBuffer = NULL;
369 extern boolean fileEOF (void)
371 return File.eof;
374 /* Action to take for each encountered source newline.
376 static void fileNewline (void)
378 File.filePosition = StartOfLine;
379 File.newLine = FALSE;
380 File.lineNumber++;
381 File.source.lineNumber++;
382 DebugStatement ( if (Option.breakLine == File.lineNumber) lineBreak (); )
383 DebugStatement ( debugPrintf (DEBUG_RAW, "%6ld: ", File.lineNumber); )
386 /* This function reads a single character from the stream, performing newline
387 * canonicalization.
389 static int iFileGetc (void)
391 int c;
392 readnext:
393 c = readNextChar ();
395 /* If previous character was a newline, then we're starting a line.
397 if (File.newLine && c != EOF)
399 fileNewline ();
400 if (c == '#' && Option.lineDirectives)
402 if (parseLineDirective ())
403 goto readnext;
404 else
406 /* FIXME: find out a better way to do this check */
407 if (File.fp != NULL)
408 fsetpos (File.fp, &StartOfLine);
409 else
410 File.fpBufferPosition = bufferStartOfLine;
412 c = readNextChar ();
417 if (c == EOF)
418 File.eof = TRUE;
419 else if (c == NEWLINE)
421 File.newLine = TRUE;
422 if (File.fp != NULL) /* we have a file */
423 fgetpos (File.fp, &StartOfLine);
424 else /* it's a buffer */
425 bufferStartOfLine = File.fpBufferPosition;
427 else if (c == CRETURN)
429 /* Turn line breaks into a canonical form. The three commonly
430 * used forms if line breaks: LF (UNIX), CR (MacIntosh), and
431 * CR-LF (MS-DOS) are converted into a generic newline.
433 const int next = readNextChar (); /* is CR followed by LF? */
435 if (next != NEWLINE)
436 pushBackChar (next);
438 c = NEWLINE; /* convert CR into newline */
439 File.newLine = TRUE;
440 if (File.fp != NULL)
441 fgetpos (File.fp, &StartOfLine);
442 else
443 bufferStartOfLine = File.fpBufferPosition;
445 DebugStatement ( debugPutc (DEBUG_RAW, c); )
446 return c;
449 extern void fileUngetc (int c)
451 File.ungetch = c;
454 static vString *iFileGetLine (void)
456 vString *result = NULL;
457 int c;
458 if (File.line == NULL)
459 File.line = vStringNew ();
460 vStringClear (File.line);
463 c = iFileGetc ();
464 if (c != EOF)
465 vStringPut (File.line, c);
466 if (c == '\n' || (c == EOF && vStringLength (File.line) > 0))
468 vStringTerminate (File.line);
469 #ifdef HAVE_REGEX
470 if (vStringLength (File.line) > 0)
471 matchRegex (File.line, File.source.language);
472 #endif
473 result = File.line;
474 break;
476 } while (c != EOF);
477 Assert (result != NULL || File.eof);
478 return result;
481 /* Do not mix use of fileReadLine () and fileGetc () for the same file.
483 extern int fileGetc (void)
485 int c;
487 /* If there is an ungotten character, then return it. Don't do any
488 * other processing on it, though, because we already did that the
489 * first time it was read through fileGetc ().
491 if (File.ungetch != '\0')
493 c = File.ungetch;
494 File.ungetch = '\0';
495 return c; /* return here to avoid re-calling debugPutc () */
499 if (File.currentLine != NULL)
501 c = *File.currentLine++;
502 if (c == '\0')
503 File.currentLine = NULL;
505 else
507 vString* const line = iFileGetLine ();
508 if (line != NULL)
509 File.currentLine = (unsigned char*) vStringValue (line);
510 if (File.currentLine == NULL)
511 c = EOF;
512 else
513 c = '\0';
515 } while (c == '\0');
516 DebugStatement ( debugPutc (DEBUG_READ, c); )
517 return c;
520 /* An alternative interface to fileGetc (). Do not mix use of fileReadLine()
521 * and fileGetc() for the same file. The returned string does not contain
522 * the terminating newline. A NULL return value means that all lines in the
523 * file have been read and we are at the end of file.
525 extern const unsigned char *fileReadLine (void)
527 vString* const line = iFileGetLine ();
528 const unsigned char* result = NULL;
529 if (line != NULL)
531 result = (const unsigned char*) vStringValue (line);
532 vStringStripNewline (line);
533 DebugStatement ( debugPrintf (DEBUG_READ, "%s\n", result); )
535 return result;
538 /* Read a character choosing automatically between file or buffer, depending
539 * on which mode we are.
541 static int readNextChar(void)
543 if (File.fp != NULL) {
544 return getc(File.fp);
546 else {
547 int c;
548 if (File.fpBufferPosition >= File.fpBufferSize)
549 return EOF;
551 c = File.fpBuffer[File.fpBufferPosition];
552 File.fpBufferPosition++;
554 return c;
558 /* Replaces ungetc() for file. In case of buffer we'll perform the same action:
559 * fpBufferPosition-- and write of the param char into the buf.
561 static int pushBackChar (int c)
563 if (File.fp != NULL) {
564 return ungetc (c, File.fp);
566 else {
567 File.fpBufferPosition--;
568 if (File.fpBufferPosition < 0)
569 return EOF;
570 File.fpBuffer[File.fpBufferPosition] = c;
571 return File.fpBuffer[File.fpBufferPosition];
576 /* replacement for fsetpos, applied to a buffer */
577 extern void setBufPos (int new_position)
579 File.fpBufferPosition = new_position;
582 /* replacement for fgetpos, applied to a buffer */
583 extern int getBufPos (void)
585 return File.fpBufferPosition;
588 extern boolean useFile (void)
590 if (File.fp != NULL)
591 return TRUE;
592 else
593 return FALSE;
597 * Source file line reading with automatic buffer sizing
598 * Does not perform file/buffer checks. Only file is supported.
600 extern char *readLine (vString *const vLine, FILE *const fp)
602 char *result = NULL;
604 vStringClear (vLine);
605 if (fp == NULL) /* to free memory allocated to buffer */
606 error (FATAL, "NULL file pointer");
607 else
609 boolean reReadLine;
611 /* If reading the line places any character other than a null or a
612 * newline at the last character position in the buffer (one less
613 * than the buffer size), then we must resize the buffer and
614 * reattempt to read the line.
618 char *const pLastChar = vStringValue (vLine) + vStringSize (vLine) -2;
619 fpos_t startOfLine;
621 fgetpos (fp, &startOfLine);
622 reReadLine = FALSE;
623 *pLastChar = '\0';
624 result = fgets (vStringValue (vLine), (int) vStringSize (vLine), fp);
625 if (result == NULL)
627 if (! feof (fp))
628 error (FATAL | PERROR, "Failure on attempt to read file");
630 else if (*pLastChar != '\0' &&
631 *pLastChar != '\n' && *pLastChar != '\r')
633 /* buffer overflow */
634 reReadLine = vStringAutoResize (vLine);
635 if (reReadLine)
636 fsetpos (fp, &startOfLine);
637 else
638 error (FATAL | PERROR, "input line too big; out of memory");
640 else
642 char* eol;
643 vStringSetLength (vLine);
644 /* canonicalize new line */
645 eol = vStringValue (vLine) + vStringLength (vLine) - 1;
646 if (*eol == '\r')
647 *eol = '\n';
648 else if (*(eol - 1) == '\r' && *eol == '\n')
650 *(eol - 1) = '\n';
651 *eol = '\0';
652 --vLine->length;
655 } while (reReadLine);
657 return result;
660 /* Places into the line buffer the contents of the line referenced by
661 * "location".
663 extern char *readSourceLine (vString *const vLine, fpos_t location,
664 long *const pSeekValue)
666 fpos_t orignalPosition;
667 char *result;
669 fgetpos (File.fp, &orignalPosition);
670 fsetpos (File.fp, &location);
671 if (pSeekValue != NULL)
672 *pSeekValue = ftell (File.fp);
673 result = readLine (vLine, File.fp);
674 if (result == NULL)
675 error (FATAL, "Unexpected end of file: %s", vStringValue (File.name));
676 fsetpos (File.fp, &orignalPosition);
678 return result;
681 /* vi:set tabstop=8 shiftwidth=4: */