2 * $Id: read.c 708 2009-07-04 05:29:02Z dhiebert $
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 */
36 * FUNCTION DEFINITIONS
39 extern void freeSourceFileResources (void)
41 if (File
.name
!= NULL
)
42 vStringDelete (File
.name
);
43 if (File
.path
!= NULL
)
44 vStringDelete (File
.path
);
45 if (File
.source
.name
!= NULL
)
46 vStringDelete (File
.source
.name
);
47 if (File
.source
.tagPath
!= NULL
)
48 eFree (File
.source
.tagPath
);
49 if (File
.line
!= NULL
)
50 vStringDelete (File
.line
);
54 * Source file access functions
57 static void setInputFileName (const char *const fileName
)
59 const char *const head
= fileName
;
60 const char *const tail
= baseFilename (head
);
62 if (File
.name
!= NULL
)
63 vStringDelete (File
.name
);
64 File
.name
= vStringNewInit (fileName
);
66 if (File
.path
!= NULL
)
67 vStringDelete (File
.path
);
72 const size_t length
= tail
- head
- 1;
73 File
.path
= vStringNew ();
74 vStringNCopyS (File
.path
, fileName
, length
);
78 static void setSourceFileParameters (vString
*const fileName
)
80 if (File
.source
.name
!= NULL
)
81 vStringDelete (File
.source
.name
);
82 File
.source
.name
= fileName
;
84 if (File
.source
.tagPath
!= NULL
)
85 eFree (File
.source
.tagPath
);
86 if (! Option
.tagRelative
|| isAbsolutePath (vStringValue (fileName
)))
87 File
.source
.tagPath
= eStrdup (vStringValue (fileName
));
90 relativeFilename (vStringValue (fileName
), TagFile
.directory
);
92 if (vStringLength (fileName
) > TagFile
.max
.file
)
93 TagFile
.max
.file
= vStringLength (fileName
);
95 File
.source
.isHeader
= isIncludeFile (vStringValue (fileName
));
96 File
.source
.language
= getFileLanguage (vStringValue (fileName
));
99 static boolean
setSourceFileName (vString
*const fileName
)
101 boolean result
= FALSE
;
102 if (getFileLanguage (vStringValue (fileName
)) != LANG_IGNORE
)
105 if (isAbsolutePath (vStringValue (fileName
)) || File
.path
== NULL
)
106 pathName
= vStringNewCopy (fileName
);
108 pathName
= combinePathAndFile (
109 vStringValue (File
.path
), vStringValue (fileName
));
110 setSourceFileParameters (pathName
);
117 * Line directive parsing
120 static int skipWhite (void)
125 while (c
== ' ' || c
== '\t');
129 static unsigned long readLineNumber (void)
131 unsigned long lNum
= 0;
132 int c
= skipWhite ();
133 while (c
!= EOF
&& isdigit (c
))
135 lNum
= (lNum
* 10) + (c
- '0');
139 if (c
!= ' ' && c
!= '\t')
145 /* While ANSI only permits lines of the form:
146 * # line n "filename"
147 * Earlier compilers generated lines of the form
149 * GNU C will output lines of the form:
151 * So we need to be fairly flexible in what we accept.
153 static vString
*readFileName (void)
155 vString
*const fileName
= vStringNew ();
156 boolean quoteDelimited
= FALSE
;
157 int c
= skipWhite ();
161 c
= getc (File
.fp
); /* skip double-quote */
162 quoteDelimited
= TRUE
;
164 while (c
!= EOF
&& c
!= '\n' &&
165 (quoteDelimited
? (c
!= '"') : (c
!= ' ' && c
!= '\t')))
167 vStringPut (fileName
, c
);
172 vStringPut (fileName
, '\0');
177 static boolean
parseLineDirective (void)
179 boolean result
= FALSE
;
180 int c
= skipWhite ();
181 DebugStatement ( const char* lineStr
= ""; )
188 else if (c
== 'l' && getc (File
.fp
) == 'i' &&
189 getc (File
.fp
) == 'n' && getc (File
.fp
) == 'e')
192 if (c
== ' ' || c
== '\t')
194 DebugStatement ( lineStr
= "line"; )
200 const unsigned long lNum
= readLineNumber ();
205 vString
*const fileName
= readFileName ();
206 if (vStringLength (fileName
) == 0)
208 File
.source
.lineNumber
= lNum
- 1; /* applies to NEXT line */
209 DebugStatement ( debugPrintf (DEBUG_RAW
, "#%s %ld", lineStr
, lNum
); )
211 else if (setSourceFileName (fileName
))
213 File
.source
.lineNumber
= lNum
- 1; /* applies to NEXT line */
214 DebugStatement ( debugPrintf (DEBUG_RAW
, "#%s %ld \"%s\"",
215 lineStr
, lNum
, vStringValue (fileName
)); )
218 if (Option
.include
.fileNames
&& vStringLength (fileName
) > 0 &&
222 initTagEntry (&tag
, baseFilename (vStringValue (fileName
)));
224 tag
.isFileEntry
= TRUE
;
225 tag
.lineNumberEntry
= TRUE
;
227 tag
.kindName
= "file";
232 vStringDelete (fileName
);
240 * Source file I/O operations
243 /* This function opens a source file, and resets the line counter. If it
244 * fails, it will display an error message and leave the File.fp set to NULL.
246 extern boolean
fileOpen (const char *const fileName
, const langType language
)
249 const char *const openMode
= "r";
251 const char *const openMode
= "rb";
253 boolean opened
= FALSE
;
255 /* If another file was already open, then close it.
259 fclose (File
.fp
); /* close any open source file */
263 File
.fp
= fopen (fileName
, openMode
);
265 error (WARNING
| PERROR
, "cannot open \"%s\"", fileName
);
270 setInputFileName (fileName
);
271 fgetpos (File
.fp
, &StartOfLine
);
272 fgetpos (File
.fp
, &File
.filePosition
);
273 File
.currentLine
= NULL
;
274 File
.language
= language
;
275 File
.lineNumber
= 0L;
279 if (File
.line
!= NULL
)
280 vStringClear (File
.line
);
282 setSourceFileParameters (vStringNewInit (fileName
));
283 File
.source
.lineNumber
= 0L;
285 verbose ("OPENING %s as %s language %sfile\n", fileName
,
286 getLanguageName (language
),
287 File
.source
.isHeader
? "include " : "");
292 extern void fileClose (void)
296 /* The line count of the file is 1 too big, since it is one-based
297 * and is incremented upon each newline.
299 if (Option
.printTotals
)
301 fileStatus
*status
= eStat (vStringValue (File
.name
));
302 addTotals (0, File
.lineNumber
- 1L, status
->size
);
309 extern boolean
fileEOF (void)
314 /* Action to take for each encountered source newline.
316 static void fileNewline (void)
318 File
.filePosition
= StartOfLine
;
319 File
.newLine
= FALSE
;
321 File
.source
.lineNumber
++;
322 DebugStatement ( if (Option
.breakLine
== File
.lineNumber
) lineBreak (); )
323 DebugStatement ( debugPrintf (DEBUG_RAW
, "%6ld: ", File
.lineNumber
); )
326 /* This function reads a single character from the stream, performing newline
329 static int iFileGetc (void)
335 /* If previous character was a newline, then we're starting a line.
337 if (File
.newLine
&& c
!= EOF
)
340 if (c
== '#' && Option
.lineDirectives
)
342 if (parseLineDirective ())
346 fsetpos (File
.fp
, &StartOfLine
);
354 else if (c
== NEWLINE
)
357 fgetpos (File
.fp
, &StartOfLine
);
359 else if (c
== CRETURN
)
361 /* Turn line breaks into a canonical form. The three commonly
362 * used forms if line breaks: LF (UNIX/Mac OS X), CR (Mac OS 9),
363 * and CR-LF (MS-DOS) are converted into a generic newline.
366 const int next
= getc (File
.fp
); /* is CR followed by LF? */
368 ungetc (next
, File
.fp
);
372 c
= NEWLINE
; /* convert CR into newline */
374 fgetpos (File
.fp
, &StartOfLine
);
377 DebugStatement ( debugPutc (DEBUG_RAW
, c
); )
381 extern void fileUngetc (int c
)
386 static vString
*iFileGetLine (void)
388 vString
*result
= NULL
;
390 if (File
.line
== NULL
)
391 File
.line
= vStringNew ();
392 vStringClear (File
.line
);
397 vStringPut (File
.line
, c
);
398 if (c
== '\n' || (c
== EOF
&& vStringLength (File
.line
) > 0))
400 vStringTerminate (File
.line
);
402 if (vStringLength (File
.line
) > 0)
403 matchRegex (File
.line
, File
.source
.language
);
409 Assert (result
!= NULL
|| File
.eof
);
413 /* Do not mix use of fileReadLine () and fileGetc () for the same file.
415 extern int fileGetc (void)
419 /* If there is an ungotten character, then return it. Don't do any
420 * other processing on it, though, because we already did that the
421 * first time it was read through fileGetc ().
423 if (File
.ungetch
!= '\0')
427 return c
; /* return here to avoid re-calling debugPutc () */
431 if (File
.currentLine
!= NULL
)
433 c
= *File
.currentLine
++;
435 File
.currentLine
= NULL
;
439 vString
* const line
= iFileGetLine ();
441 File
.currentLine
= (unsigned char*) vStringValue (line
);
442 if (File
.currentLine
== NULL
)
448 DebugStatement ( debugPutc (DEBUG_READ
, c
); )
452 extern int fileSkipToCharacter (int c
)
458 } while (d
!= EOF
&& d
!= c
);
462 /* An alternative interface to fileGetc (). Do not mix use of fileReadLine()
463 * and fileGetc() for the same file. The returned string does not contain
464 * the terminating newline. A NULL return value means that all lines in the
465 * file have been read and we are at the end of file.
467 extern const unsigned char *fileReadLine (void)
469 vString
* const line
= iFileGetLine ();
470 const unsigned char* result
= NULL
;
473 result
= (const unsigned char*) vStringValue (line
);
474 vStringStripNewline (line
);
475 DebugStatement ( debugPrintf (DEBUG_READ
, "%s\n", result
); )
481 * Source file line reading with automatic buffer sizing
483 extern char *readLine (vString
*const vLine
, FILE *const fp
)
487 vStringClear (vLine
);
488 if (fp
== NULL
) /* to free memory allocated to buffer */
489 error (FATAL
, "NULL file pointer");
494 /* If reading the line places any character other than a null or a
495 * newline at the last character position in the buffer (one less
496 * than the buffer size), then we must resize the buffer and
497 * reattempt to read the line.
501 char *const pLastChar
= vStringValue (vLine
) + vStringSize (vLine
) -2;
504 fgetpos (fp
, &startOfLine
);
507 result
= fgets (vStringValue (vLine
), (int) vStringSize (vLine
), fp
);
511 error (FATAL
| PERROR
, "Failure on attempt to read file");
513 else if (*pLastChar
!= '\0' &&
514 *pLastChar
!= '\n' && *pLastChar
!= '\r')
516 /* buffer overflow */
517 reReadLine
= vStringAutoResize (vLine
);
519 fsetpos (fp
, &startOfLine
);
521 error (FATAL
| PERROR
, "input line too big; out of memory");
526 vStringSetLength (vLine
);
527 /* canonicalize new line */
528 eol
= vStringValue (vLine
) + vStringLength (vLine
) - 1;
531 else if (*(eol
- 1) == '\r' && *eol
== '\n')
538 } while (reReadLine
);
543 /* Places into the line buffer the contents of the line referenced by
546 extern char *readSourceLine (
547 vString
*const vLine
, fpos_t location
, long *const pSeekValue
)
549 fpos_t orignalPosition
;
552 fgetpos (File
.fp
, &orignalPosition
);
553 fsetpos (File
.fp
, &location
);
554 if (pSeekValue
!= NULL
)
555 *pSeekValue
= ftell (File
.fp
);
556 result
= readLine (vLine
, File
.fp
);
558 error (FATAL
, "Unexpected end of file: %s", vStringValue (File
.name
));
559 fsetpos (File
.fp
, &orignalPosition
);
564 /* vi:set tabstop=4 shiftwidth=4: */