2008-04-16 Johannes Schmid <jhs@gnome.org>
[anjuta-git-plugin.git] / tagmanager / read.c
blobe570372f8d6af60dfffd99d0876d324a44131617
1 /*
2 * $Id$
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 */
34 static int bufferStartOfLine; /* the same as StartOfLine but for buffer */
37 * FUNCTION DEFINITIONS
41 * FORWARD
44 static int readNextChar (void);
45 static int pushBackChar (int c);
48 extern void freeSourceFileResources (void)
50 vStringDelete (File.name);
51 vStringDelete (File.path);
52 vStringDelete (File.source.name);
53 vStringDelete (File.line);
57 * Source file access functions
60 static void setInputFileName (const char *const fileName)
62 const char *const head = fileName;
63 const char *const tail = baseFilename (head);
65 if (File.name != NULL)
66 vStringDelete (File.name);
67 File.name = vStringNewInit (fileName);
69 if (File.path != NULL)
70 vStringDelete (File.path);
71 if (tail == head)
72 File.path = NULL;
73 else
75 const size_t length = tail - head - 1;
76 File.path = vStringNew ();
77 vStringNCopyS (File.path, fileName, length);
81 static void setSourceFileParameters (vString *const fileName)
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 File.source.language = getFileLanguage (vStringValue (fileName));
102 static boolean setSourceFileName (vString *const fileName)
104 boolean result = FALSE;
105 if (getFileLanguage (vStringValue (fileName)) != LANG_IGNORE)
107 vString *pathName;
108 if (isAbsolutePath (vStringValue (fileName)) || File.path == NULL)
109 pathName = vStringNewCopy (fileName);
110 else
111 pathName = combinePathAndFile (
112 vStringValue (File.path), vStringValue (fileName));
113 setSourceFileParameters (pathName);
114 result = TRUE;
116 return result;
120 * Line directive parsing
123 static int skipWhite (void)
125 int c;
127 c = readNextChar ();
128 while (c == ' ' || c == '\t');
129 return c;
132 static unsigned long readLineNumber (void)
134 unsigned long lNum = 0;
135 int c = skipWhite ();
136 while (c != EOF && isdigit (c))
138 lNum = (lNum * 10) + (c - '0');
139 c = readNextChar ();
141 pushBackChar (c);
142 if (c != ' ' && c != '\t')
143 lNum = 0;
145 return lNum;
148 /* While ANSI only permits lines of the form:
149 * # line n "filename"
150 * Earlier compilers generated lines of the form
151 * # n filename
152 * GNU C will output lines of the form:
153 * # n "filename"
154 * So we need to be fairly flexible in what we accept.
156 static vString *readFileName (void)
158 vString *const fileName = vStringNew ();
159 boolean quoteDelimited = FALSE;
160 int c = skipWhite ();
162 if (c == '"')
164 c = readNextChar (); /* skip double-quote */
165 quoteDelimited = TRUE;
167 while (c != EOF && c != '\n' &&
168 (quoteDelimited ? (c != '"') : (c != ' ' && c != '\t')))
170 vStringPut (fileName, c);
171 c = readNextChar ();
173 if (c == '\n')
174 pushBackChar (c);
175 vStringPut (fileName, '\0');
177 return fileName;
180 static boolean parseLineDirective (void)
182 boolean result = FALSE;
183 int c = skipWhite ();
184 DebugStatement ( const char* lineStr = ""; )
186 if (isdigit (c))
188 pushBackChar (c);
189 result = TRUE;
191 else if (c == 'l' && readNextChar () == 'i' &&
192 readNextChar () == 'n' && readNextChar () == 'e')
194 c = readNextChar ();
195 if (c == ' ' || c == '\t')
197 DebugStatement ( lineStr = "line"; )
198 result = TRUE;
201 if (result)
203 const unsigned long lNum = readLineNumber ();
204 if (lNum == 0)
205 result = FALSE;
206 else
208 vString *const fileName = readFileName ();
209 if (vStringLength (fileName) == 0)
211 File.source.lineNumber = lNum - 1; /* applies to NEXT line */
212 DebugStatement ( debugPrintf (DEBUG_RAW, "#%s %ld", lineStr, lNum); )
214 else if (setSourceFileName (fileName))
216 File.source.lineNumber = lNum - 1; /* applies to NEXT line */
217 DebugStatement ( debugPrintf (DEBUG_RAW, "#%s %ld \"%s\"",
218 lineStr, lNum, vStringValue (fileName)); )
221 if (Option.include.fileNames && vStringLength (fileName) > 0 &&
222 lNum == 1)
224 tagEntryInfo tag;
225 initTagEntry (&tag, baseFilename (vStringValue (fileName)));
227 tag.isFileEntry = TRUE;
228 tag.lineNumberEntry = TRUE;
229 tag.lineNumber = 1;
230 tag.kindName = "file";
231 tag.kind = 'F';
233 makeTagEntry (&tag);
235 vStringDelete (fileName);
236 result = TRUE;
239 return result;
243 * Source file I/O operations
246 /* This function opens a source file, and resets the line counter. If it
247 * fails, it will display an error message and leave the File.fp set to NULL.
249 extern boolean fileOpen (const char *const fileName, const langType language)
251 #ifdef VMS
252 const char *const openMode = "r";
253 #else
254 const char *const openMode = "rb";
255 #endif
256 boolean opened = FALSE;
258 /* If another file was already open, then close it.
260 if (File.fp != NULL)
262 fclose (File.fp); /* close any open source file */
263 File.fp = NULL;
266 File.fp = fopen (fileName, openMode);
267 if (File.fp == NULL)
268 error (WARNING | PERROR, "cannot open \"%s\"", fileName);
269 else
271 opened = TRUE;
273 setInputFileName (fileName);
274 fgetpos (File.fp, &StartOfLine);
275 fgetpos (File.fp, &File.filePosition);
276 File.currentLine = NULL;
277 File.language = language;
278 File.lineNumber = 0L;
279 File.eof = FALSE;
280 File.newLine = TRUE;
282 if (File.line != NULL)
283 vStringClear (File.line);
285 setSourceFileParameters (vStringNewInit (fileName));
286 File.source.lineNumber = 0L;
288 verbose ("OPENING %s as %s language %sfile\n", fileName,
289 getLanguageName (language),
290 File.source.isHeader ? "include " : "");
292 return opened;
295 /* The user should take care of allocate and free the buffer param.
296 * This func is NOT THREAD SAFE.
297 * The user should not tamper with the buffer while this func is executing.
299 extern boolean bufferOpen (unsigned char *buffer, int buffer_size,
300 const char *const fileName, const langType language )
302 boolean opened = FALSE;
304 /* Check whether a file of a buffer were already open, then close them.
306 if (File.fp != NULL) {
307 fclose (File.fp); /* close any open source file */
308 File.fp = NULL;
311 if (File.fpBuffer != NULL) {
312 error(PERROR, "An unallocated buffer was found. Please check you called \
313 correctly bufferClose ()\n");
314 File.fpBuffer = NULL;
317 /* check if we got a good buffer */
318 if (buffer == NULL || buffer_size == 0) {
319 opened = FALSE;
320 return opened;
323 opened = TRUE;
325 File.fpBuffer = buffer;
326 setInputFileName (fileName);
327 bufferStartOfLine = 0;
328 File.fpBufferPosition = 0;
329 File.fpBufferSize = buffer_size;
330 File.currentLine = NULL;
331 File.language = language;
332 File.lineNumber = 0L;
333 File.eof = FALSE;
334 File.newLine = TRUE;
336 if (File.line != NULL)
337 vStringClear (File.line);
339 setSourceFileParameters (vStringNewInit (fileName));
340 File.source.lineNumber = 0L;
342 verbose ("OPENING %s as %s language %sfile\n", fileName,
343 getLanguageName (language),
344 File.source.isHeader ? "include " : "");
346 return opened;
350 extern void fileClose (void)
352 if (File.fp != NULL)
354 /* The line count of the file is 1 too big, since it is one-based
355 * and is incremented upon each newline.
357 if (Option.printTotals)
359 fileStatus *status = eStat (vStringValue (File.name));
360 addTotals (0, File.lineNumber - 1L, status->size);
362 fclose (File.fp);
363 File.fp = NULL;
367 /* user should take care of freeing the buffer */
368 extern void bufferClose (void)
370 if (File.fpBuffer != NULL) {
371 File.fpBuffer = NULL;
376 extern boolean fileEOF (void)
378 return File.eof;
381 /* Action to take for each encountered source newline.
383 static void fileNewline (void)
385 File.filePosition = StartOfLine;
386 File.newLine = FALSE;
387 File.lineNumber++;
388 File.source.lineNumber++;
389 DebugStatement ( if (Option.breakLine == File.lineNumber) lineBreak (); )
390 DebugStatement ( debugPrintf (DEBUG_RAW, "%6ld: ", File.lineNumber); )
393 /* This function reads a single character from the stream, performing newline
394 * canonicalization.
396 static int iFileGetc (void)
398 int c;
399 readnext:
400 c = readNextChar ();
402 /* If previous character was a newline, then we're starting a line.
404 if (File.newLine && c != EOF)
406 fileNewline ();
407 if (c == '#' && Option.lineDirectives)
409 if (parseLineDirective ())
410 goto readnext;
411 else
413 /* FIXME: find out a better way to do this check */
414 if (File.fp != NULL)
415 fsetpos (File.fp, &StartOfLine);
416 else
417 File.fpBufferPosition = bufferStartOfLine;
419 c = readNextChar ();
424 if (c == EOF)
425 File.eof = TRUE;
426 else if (c == NEWLINE)
428 File.newLine = TRUE;
429 if (File.fp != NULL) /* we have a file */
430 fgetpos (File.fp, &StartOfLine);
431 else /* it's a buffer */
432 bufferStartOfLine = File.fpBufferPosition;
435 else if (c == CRETURN)
437 /* Turn line breaks into a canonical form. The three commonly
438 * used forms if line breaks: LF (UNIX), CR (MacIntosh), and
439 * CR-LF (MS-DOS) are converted into a generic newline.
441 const int next = readNextChar (); /* is CR followed by LF? */
443 if (next != NEWLINE)
444 pushBackChar (next);
446 c = NEWLINE; /* convert CR into newline */
447 File.newLine = TRUE;
448 if (File.fp != NULL)
449 fgetpos (File.fp, &StartOfLine);
450 else
451 bufferStartOfLine = File.fpBufferPosition;
454 DebugStatement ( debugPutc (DEBUG_RAW, c); )
455 return c;
458 extern void fileUngetc (int c)
460 File.ungetch = c;
463 static vString *iFileGetLine (void)
465 vString *result = NULL;
466 int c;
467 if (File.line == NULL)
468 File.line = vStringNew ();
469 vStringClear (File.line);
472 c = iFileGetc ();
473 if (c != EOF)
474 vStringPut (File.line, c);
475 if (c == '\n' || (c == EOF && vStringLength (File.line) > 0))
477 vStringTerminate (File.line);
478 #ifdef HAVE_REGEX
479 if (vStringLength (File.line) > 0)
480 matchRegex (File.line, File.source.language);
481 #endif
482 result = File.line;
483 break;
485 } while (c != EOF);
486 Assert (result != NULL || File.eof);
487 return result;
490 /* Do not mix use of fileReadLine () and fileGetc () for the same file.
492 extern int fileGetc (void)
494 int c;
496 /* If there is an ungotten character, then return it. Don't do any
497 * other processing on it, though, because we already did that the
498 * first time it was read through fileGetc ().
500 if (File.ungetch != '\0')
502 c = File.ungetch;
503 File.ungetch = '\0';
504 return c; /* return here to avoid re-calling debugPutc () */
508 if (File.currentLine != NULL)
510 c = *File.currentLine++;
511 if (c == '\0')
512 File.currentLine = NULL;
514 else
516 vString* const line = iFileGetLine ();
517 if (line != NULL)
518 File.currentLine = (unsigned char*) vStringValue (line);
519 if (File.currentLine == NULL)
520 c = EOF;
521 else
522 c = '\0';
524 } while (c == '\0');
525 DebugStatement ( debugPutc (DEBUG_READ, c); )
526 return c;
529 /* An alternative interface to fileGetc (). Do not mix use of fileReadLine()
530 * and fileGetc() for the same file. The returned string does not contain
531 * the terminating newline. A NULL return value means that all lines in the
532 * file have been read and we are at the end of file.
534 extern const unsigned char *fileReadLine (void)
536 vString* const line = iFileGetLine ();
537 const unsigned char* result = NULL;
538 if (line != NULL)
540 result = (const unsigned char*) vStringValue (line);
541 vStringStripNewline (line);
542 DebugStatement ( debugPrintf (DEBUG_READ, "%s\n", result); )
544 return result;
547 /* Read a character choosing automatically between file or buffer, depending
548 * on which mode we are.
550 static int readNextChar(void)
552 if (File.fp != NULL) {
553 return getc(File.fp);
555 else {
556 int c;
557 if (File.fpBufferPosition >= File.fpBufferSize)
558 return EOF;
560 c = File.fpBuffer[File.fpBufferPosition];
561 File.fpBufferPosition++;
563 return c;
567 /* Replaces ungetc() for file. In case of buffer we'll perform the same action:
568 * fpBufferPosition-- and write of the param char into the buf.
570 static int pushBackChar (int c)
573 if (File.fp != NULL) {
574 return ungetc (c, File.fp);
576 else {
577 File.fpBufferPosition--;
578 if (File.fpBufferPosition < 0)
579 return EOF;
580 File.fpBuffer[File.fpBufferPosition] = c;
581 return File.fpBuffer[File.fpBufferPosition];
586 /* replacement for fsetpos, applied to a buffer */
587 extern void setBufPos (int new_position)
589 File.fpBufferPosition = new_position;
592 /* replacement for fgetpos, applied to a buffer */
593 extern int getBufPos (void)
595 return File.fpBufferPosition;
598 extern boolean useFile (void)
600 if (File.fp != NULL)
601 return TRUE;
602 else
603 return FALSE;
608 * Source file line reading with automatic buffer sizing
609 * Does not perform file/buffer checks. Only file is supported.
611 extern char *readLine (vString *const vLine, FILE *const fp)
613 char *result = NULL;
615 vStringClear (vLine);
616 if (fp == NULL) /* to free memory allocated to buffer */
617 error (FATAL, "NULL file pointer");
618 else
620 boolean reReadLine;
622 /* If reading the line places any character other than a null or a
623 * newline at the last character position in the buffer (one less
624 * than the buffer size), then we must resize the buffer and
625 * reattempt to read the line.
629 char *const pLastChar = vStringValue (vLine) + vStringSize (vLine) -2;
630 fpos_t startOfLine;
632 fgetpos (fp, &startOfLine);
633 reReadLine = FALSE;
634 *pLastChar = '\0';
635 result = fgets (vStringValue (vLine), (int) vStringSize (vLine), fp);
636 if (result == NULL)
638 if (! feof (fp))
639 error (FATAL | PERROR, "Failure on attempt to read file");
641 else if (*pLastChar != '\0' &&
642 *pLastChar != '\n' && *pLastChar != '\r')
644 /* buffer overflow */
645 reReadLine = vStringAutoResize (vLine);
646 if (reReadLine)
647 fsetpos (fp, &startOfLine);
648 else
649 error (FATAL | PERROR, "input line too big; out of memory");
651 else
653 char* eol;
654 vStringSetLength (vLine);
655 /* canonicalize new line */
656 eol = vStringValue (vLine) + vStringLength (vLine) - 1;
657 if (*eol == '\r')
658 *eol = '\n';
659 else if (*(eol - 1) == '\r' && *eol == '\n')
661 *(eol - 1) = '\n';
662 *eol = '\0';
663 --vLine->length;
666 } while (reReadLine);
668 return result;
671 /* Places into the line buffer the contents of the line referenced by
672 * "location".
674 extern char *readSourceLine (
675 vString *const vLine, fpos_t location, long *const pSeekValue)
677 fpos_t orignalPosition;
678 char *result;
680 fgetpos (File.fp, &orignalPosition);
681 fsetpos (File.fp, &location);
682 if (pSeekValue != NULL)
683 *pSeekValue = ftell (File.fp);
684 result = readLine (vLine, File.fp);
685 if (result == NULL)
686 error (FATAL, "Unexpected end of file: %s", vStringValue (File.name));
687 fsetpos (File.fp, &orignalPosition);
689 return result;
692 /* vi:set tabstop=4 shiftwidth=4: */