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).
15 #include "general.h" /* must always come first */
19 #include <glib/gstdio.h>
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
);
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
));
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
));
93 File
.source
.language
= language
;
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
)
104 if (isAbsolutePath (vStringValue (fileName
)) || File
.path
== NULL
)
105 pathName
= vStringNewCopy (fileName
);
107 pathName
= combinePathAndFile (vStringValue (File
.path
),
108 vStringValue (fileName
));
109 setSourceFileParameters (pathName
, -1);
116 * Line directive parsing
119 static int skipWhite (void)
124 while (c
== ' ' || c
== '\t');
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');
138 if (c
!= ' ' && c
!= '\t')
144 /* While ANSI only permits lines of the form:
145 * # line n "filename"
146 * Earlier compilers generated lines of the form
148 * GNU C will output lines of the form:
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 ();
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
);
171 vStringPut (fileName
, '\0');
176 static boolean
parseLineDirective (void)
178 boolean result
= FALSE
;
179 int c
= skipWhite ();
180 DebugStatement ( const char* lineStr
= ""; )
187 else if (c
== 'l' && readNextChar () == 'i' &&
188 readNextChar () == 'n' && readNextChar () == 'e')
191 if (c
== ' ' || c
== '\t')
193 DebugStatement ( lineStr
= "line"; )
199 const unsigned long lNum
= readLineNumber ();
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 &&
221 initTagEntry (&tag
, baseFilename (vStringValue (fileName
)));
223 tag
.isFileEntry
= TRUE
;
224 tag
.lineNumberEntry
= TRUE
;
226 tag
.kindName
= "file";
231 vStringDelete (fileName
);
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
)
248 const char *const openMode
= "r";
250 const char *const openMode
= "rb";
252 boolean opened
= FALSE
;
254 /* If another file was already open, then close it.
258 fclose (File
.fp
); /* close any open source file */
262 File
.fp
= g_fopen (fileName
, openMode
);
264 error (WARNING
| PERROR
, "cannot open \"%s\"", fileName
);
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;
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 " : "");
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 */
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) {
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;
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 " : "");
345 extern void fileClose (void)
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
)));
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)
374 /* Action to take for each encountered source newline.
376 static void fileNewline (void)
378 File
.filePosition
= StartOfLine
;
379 File
.newLine
= FALSE
;
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
389 static int iFileGetc (void)
395 /* If previous character was a newline, then we're starting a line.
397 if (File
.newLine
&& c
!= EOF
)
400 if (c
== '#' && Option
.lineDirectives
)
402 if (parseLineDirective ())
406 /* FIXME: find out a better way to do this check */
408 fsetpos (File
.fp
, &StartOfLine
);
410 File
.fpBufferPosition
= bufferStartOfLine
;
419 else if (c
== NEWLINE
)
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? */
438 c
= NEWLINE
; /* convert CR into newline */
441 fgetpos (File
.fp
, &StartOfLine
);
443 bufferStartOfLine
= File
.fpBufferPosition
;
445 DebugStatement ( debugPutc (DEBUG_RAW
, c
); )
449 extern void fileUngetc (int c
)
454 static vString
*iFileGetLine (void)
456 vString
*result
= NULL
;
458 if (File
.line
== NULL
)
459 File
.line
= vStringNew ();
460 vStringClear (File
.line
);
465 vStringPut (File
.line
, c
);
466 if (c
== '\n' || (c
== EOF
&& vStringLength (File
.line
) > 0))
468 vStringTerminate (File
.line
);
470 if (vStringLength (File
.line
) > 0)
471 matchRegex (File
.line
, File
.source
.language
);
477 Assert (result
!= NULL
|| File
.eof
);
481 /* Do not mix use of fileReadLine () and fileGetc () for the same file.
483 extern int fileGetc (void)
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')
495 return c
; /* return here to avoid re-calling debugPutc () */
499 if (File
.currentLine
!= NULL
)
501 c
= *File
.currentLine
++;
503 File
.currentLine
= NULL
;
507 vString
* const line
= iFileGetLine ();
509 File
.currentLine
= (unsigned char*) vStringValue (line
);
510 if (File
.currentLine
== NULL
)
516 DebugStatement ( debugPutc (DEBUG_READ
, 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
;
531 result
= (const unsigned char*) vStringValue (line
);
532 vStringStripNewline (line
);
533 DebugStatement ( debugPrintf (DEBUG_READ
, "%s\n", 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
);
548 if (File
.fpBufferPosition
>= File
.fpBufferSize
)
551 c
= File
.fpBuffer
[File
.fpBufferPosition
];
552 File
.fpBufferPosition
++;
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
);
567 File
.fpBufferPosition
--;
568 if (File
.fpBufferPosition
< 0)
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)
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
)
604 vStringClear (vLine
);
605 if (fp
== NULL
) /* to free memory allocated to buffer */
606 error (FATAL
, "NULL file pointer");
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;
621 fgetpos (fp
, &startOfLine
);
624 result
= fgets (vStringValue (vLine
), (int) vStringSize (vLine
), 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
);
636 fsetpos (fp
, &startOfLine
);
638 error (FATAL
| PERROR
, "input line too big; out of memory");
643 vStringSetLength (vLine
);
644 /* canonicalize new line */
645 eol
= vStringValue (vLine
) + vStringLength (vLine
) - 1;
648 else if (*(eol
- 1) == '\r' && *eol
== '\n')
655 } while (reReadLine
);
660 /* Places into the line buffer the contents of the line referenced by
663 extern char *readSourceLine (vString
*const vLine
, fpos_t location
,
664 long *const pSeekValue
)
666 fpos_t orignalPosition
;
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
);
675 error (FATAL
, "Unexpected end of file: %s", vStringValue (File
.name
));
676 fsetpos (File
.fp
, &orignalPosition
);
681 /* vi:set tabstop=8 shiftwidth=4: */