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).
16 #include "general.h" /* must always come first */
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
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
);
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
));
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
)
108 if (isAbsolutePath (vStringValue (fileName
)) || File
.path
== NULL
)
109 pathName
= vStringNewCopy (fileName
);
111 pathName
= combinePathAndFile (
112 vStringValue (File
.path
), vStringValue (fileName
));
113 setSourceFileParameters (pathName
);
120 * Line directive parsing
123 static int skipWhite (void)
128 while (c
== ' ' || c
== '\t');
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');
142 if (c
!= ' ' && c
!= '\t')
148 /* While ANSI only permits lines of the form:
149 * # line n "filename"
150 * Earlier compilers generated lines of the form
152 * GNU C will output lines of the form:
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 ();
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
);
175 vStringPut (fileName
, '\0');
180 static boolean
parseLineDirective (void)
182 boolean result
= FALSE
;
183 int c
= skipWhite ();
184 DebugStatement ( const char* lineStr
= ""; )
191 else if (c
== 'l' && readNextChar () == 'i' &&
192 readNextChar () == 'n' && readNextChar () == 'e')
195 if (c
== ' ' || c
== '\t')
197 DebugStatement ( lineStr
= "line"; )
203 const unsigned long lNum
= readLineNumber ();
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 &&
225 initTagEntry (&tag
, baseFilename (vStringValue (fileName
)));
227 tag
.isFileEntry
= TRUE
;
228 tag
.lineNumberEntry
= TRUE
;
230 tag
.kindName
= "file";
235 vStringDelete (fileName
);
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
)
252 const char *const openMode
= "r";
254 const char *const openMode
= "rb";
256 boolean opened
= FALSE
;
258 /* If another file was already open, then close it.
262 fclose (File
.fp
); /* close any open source file */
266 File
.fp
= fopen (fileName
, openMode
);
268 error (WARNING
| PERROR
, "cannot open \"%s\"", fileName
);
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;
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 " : "");
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 */
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) {
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;
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 " : "");
350 extern void fileClose (void)
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
);
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)
381 /* Action to take for each encountered source newline.
383 static void fileNewline (void)
385 File
.filePosition
= StartOfLine
;
386 File
.newLine
= FALSE
;
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
396 static int iFileGetc (void)
402 /* If previous character was a newline, then we're starting a line.
404 if (File
.newLine
&& c
!= EOF
)
407 if (c
== '#' && Option
.lineDirectives
)
409 if (parseLineDirective ())
413 /* FIXME: find out a better way to do this check */
415 fsetpos (File
.fp
, &StartOfLine
);
417 File
.fpBufferPosition
= bufferStartOfLine
;
426 else if (c
== NEWLINE
)
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? */
446 c
= NEWLINE
; /* convert CR into newline */
449 fgetpos (File
.fp
, &StartOfLine
);
451 bufferStartOfLine
= File
.fpBufferPosition
;
454 DebugStatement ( debugPutc (DEBUG_RAW
, c
); )
458 extern void fileUngetc (int c
)
463 static vString
*iFileGetLine (void)
465 vString
*result
= NULL
;
467 if (File
.line
== NULL
)
468 File
.line
= vStringNew ();
469 vStringClear (File
.line
);
474 vStringPut (File
.line
, c
);
475 if (c
== '\n' || (c
== EOF
&& vStringLength (File
.line
) > 0))
477 vStringTerminate (File
.line
);
479 if (vStringLength (File
.line
) > 0)
480 matchRegex (File
.line
, File
.source
.language
);
486 Assert (result
!= NULL
|| File
.eof
);
490 /* Do not mix use of fileReadLine () and fileGetc () for the same file.
492 extern int fileGetc (void)
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')
504 return c
; /* return here to avoid re-calling debugPutc () */
508 if (File
.currentLine
!= NULL
)
510 c
= *File
.currentLine
++;
512 File
.currentLine
= NULL
;
516 vString
* const line
= iFileGetLine ();
518 File
.currentLine
= (unsigned char*) vStringValue (line
);
519 if (File
.currentLine
== NULL
)
525 DebugStatement ( debugPutc (DEBUG_READ
, 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
;
540 result
= (const unsigned char*) vStringValue (line
);
541 vStringStripNewline (line
);
542 DebugStatement ( debugPrintf (DEBUG_READ
, "%s\n", 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
);
557 if (File
.fpBufferPosition
>= File
.fpBufferSize
)
560 c
= File
.fpBuffer
[File
.fpBufferPosition
];
561 File
.fpBufferPosition
++;
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
);
577 File
.fpBufferPosition
--;
578 if (File
.fpBufferPosition
< 0)
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)
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
)
615 vStringClear (vLine
);
616 if (fp
== NULL
) /* to free memory allocated to buffer */
617 error (FATAL
, "NULL file pointer");
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;
632 fgetpos (fp
, &startOfLine
);
635 result
= fgets (vStringValue (vLine
), (int) vStringSize (vLine
), 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
);
647 fsetpos (fp
, &startOfLine
);
649 error (FATAL
| PERROR
, "input line too big; out of memory");
654 vStringSetLength (vLine
);
655 /* canonicalize new line */
656 eol
= vStringValue (vLine
) + vStringLength (vLine
) - 1;
659 else if (*(eol
- 1) == '\r' && *eol
== '\n')
666 } while (reReadLine
);
671 /* Places into the line buffer the contents of the line referenced by
674 extern char *readSourceLine (
675 vString
*const vLine
, fpos_t location
, long *const pSeekValue
)
677 fpos_t orignalPosition
;
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
);
686 error (FATAL
, "Unexpected end of file: %s", vStringValue (File
.name
));
687 fsetpos (File
.fp
, &orignalPosition
);
692 /* vi:set tabstop=4 shiftwidth=4: */