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 MIOPos StartOfLine
; /* holds deferred position of start of line */
35 * FUNCTION DEFINITIONS
38 extern void freeSourceFileResources (void)
40 vStringDelete (File
.input
.name
);
41 vStringDelete (File
.path
);
42 vStringDelete (File
.source
.name
);
43 vStringDelete (File
.line
);
47 * Source file access functions
50 static void setInputFileName (const char *const fileName
)
52 const char *const head
= fileName
;
53 const char *const tail
= baseFilename (head
);
55 if (File
.input
.name
!= NULL
)
56 vStringDelete (File
.input
.name
);
57 File
.input
.name
= vStringNewInit (fileName
);
59 if (File
.path
!= NULL
)
60 vStringDelete (File
.path
);
65 const size_t length
= tail
- head
- 1;
66 File
.path
= vStringNew ();
67 vStringNCopyS (File
.path
, fileName
, length
);
70 static void setSourceFileParameters (vString
*const fileName
, const langType language
)
72 if (File
.source
.name
!= NULL
)
73 vStringDelete (File
.source
.name
);
74 File
.source
.name
= fileName
;
76 if (File
.source
.tagPath
!= NULL
)
77 eFree (File
.source
.tagPath
);
78 if (! Option
.tagRelative
|| isAbsolutePath (vStringValue (fileName
)))
79 File
.source
.tagPath
= vStringNewCopy (fileName
);
82 vStringNewOwn (relativeFilename (vStringValue (fileName
),
85 if (vStringLength (fileName
) > TagFile
.max
.file
)
86 TagFile
.max
.file
= vStringLength (fileName
);
88 File
.source
.isHeader
= isIncludeFile (vStringValue (fileName
));
90 File
.source
.language
= language
;
92 File
.source
.language
= getFileLanguage (vStringValue (fileName
));
95 static boolean
setSourceFileName (vString
*const fileName
)
97 boolean result
= FALSE
;
98 if (getFileLanguage (vStringValue (fileName
)) != LANG_IGNORE
)
101 if (isAbsolutePath (vStringValue (fileName
)) || File
.path
== NULL
)
102 pathName
= vStringNewCopy (fileName
);
104 pathName
= combinePathAndFile (vStringValue (File
.path
),
105 vStringValue (fileName
));
106 setSourceFileParameters (pathName
, -1);
113 * Line directive parsing
116 static int skipWhite (void)
120 c
= mio_getc (File
.fp
);
121 while (c
== ' ' || c
== '\t');
125 static unsigned long readLineNumber (void)
127 unsigned long lNum
= 0;
128 int c
= skipWhite ();
129 while (c
!= EOF
&& isdigit (c
))
131 lNum
= (lNum
* 10) + (c
- '0');
132 c
= mio_getc (File
.fp
);
134 mio_ungetc (File
.fp
, c
);
135 if (c
!= ' ' && c
!= '\t')
141 /* While ANSI only permits lines of the form:
142 * # line n "filename"
143 * Earlier compilers generated lines of the form
145 * GNU C will output lines of the form:
147 * So we need to be fairly flexible in what we accept.
149 static vString
*readFileName (void)
151 vString
*const fileName
= vStringNew ();
152 boolean quoteDelimited
= FALSE
;
153 int c
= skipWhite ();
157 c
= mio_getc (File
.fp
); /* skip double-quote */
158 quoteDelimited
= TRUE
;
160 while (c
!= EOF
&& c
!= '\n' &&
161 (quoteDelimited
? (c
!= '"') : (c
!= ' ' && c
!= '\t')))
163 vStringPut (fileName
, c
);
164 c
= mio_getc (File
.fp
);
167 mio_ungetc (File
.fp
, c
);
168 vStringPut (fileName
, '\0');
173 static boolean
parseLineDirective (void)
175 boolean result
= FALSE
;
176 int c
= skipWhite ();
177 DebugStatement ( const char* lineStr
= ""; )
181 mio_ungetc (File
.fp
, c
);
184 else if (c
== 'l' && mio_getc (File
.fp
) == 'i' &&
185 mio_getc (File
.fp
) == 'n' && mio_getc (File
.fp
) == 'e')
187 c
= mio_getc (File
.fp
);
188 if (c
== ' ' || c
== '\t')
190 DebugStatement ( lineStr
= "line"; )
196 const unsigned long lNum
= readLineNumber ();
201 vString
*const fileName
= readFileName ();
202 if (vStringLength (fileName
) == 0)
204 File
.source
.lineNumber
= lNum
- 1; /* applies to NEXT line */
205 DebugStatement ( debugPrintf (DEBUG_RAW
, "#%s %ld", lineStr
, lNum
); )
207 else if (setSourceFileName (fileName
))
209 File
.source
.lineNumber
= lNum
- 1; /* applies to NEXT line */
210 DebugStatement ( debugPrintf (DEBUG_RAW
, "#%s %ld \"%s\"",
211 lineStr
, lNum
, vStringValue (fileName
)); )
214 if (Option
.include
.fileNames
&& vStringLength (fileName
) > 0 &&
218 initTagEntry (&tag
, baseFilename (vStringValue (fileName
)));
220 tag
.isFileEntry
= TRUE
;
221 tag
.lineNumberEntry
= TRUE
;
223 tag
.kindName
= "file";
228 vStringDelete (fileName
);
236 * Source file I/O operations
239 /* This function opens a source file, and resets the line counter. If it
240 * fails, it will display an error message and leave the File.fp set to NULL.
242 extern boolean
fileOpen (const char *const fileName
, const langType language
)
245 const char *const openMode
= "r";
247 const char *const openMode
= "rb";
249 boolean opened
= FALSE
;
251 /* If another file was already open, then close it.
255 mio_free (File
.fp
); /* close any open source file */
259 File
.fp
= mio_new_file_full (fileName
, openMode
, g_fopen
, fclose
);
261 error (WARNING
| PERROR
, "cannot open \"%s\"", fileName
);
266 setInputFileName (fileName
);
267 mio_getpos (File
.fp
, &StartOfLine
);
268 mio_getpos (File
.fp
, &File
.filePosition
);
269 File
.currentLine
= NULL
;
270 File
.input
.lineNumber
= 0L;
274 if (File
.line
!= NULL
)
275 vStringClear (File
.line
);
277 setSourceFileParameters (vStringNewInit (fileName
), language
);
278 File
.source
.lineNumber
= 0L;
280 verbose ("OPENING %s as %s language %sfile\n", fileName
,
281 getLanguageName (language
),
282 File
.source
.isHeader
? "include " : "");
287 /* The user should take care of allocate and free the buffer param.
288 * This func is NOT THREAD SAFE.
289 * The user should not tamper with the buffer while this func is executing.
291 extern boolean
bufferOpen (unsigned char *buffer
, size_t buffer_size
,
292 const char *const fileName
, const langType language
)
294 boolean opened
= FALSE
;
296 /* Check whether a file of a buffer were already open, then close them.
298 if (File
.fp
!= NULL
) {
299 mio_free (File
.fp
); /* close any open source file */
303 /* check if we got a good buffer */
304 if (buffer
== NULL
|| buffer_size
== 0) {
311 File
.fp
= mio_new_memory (buffer
, buffer_size
, NULL
, NULL
);
312 setInputFileName (fileName
);
313 mio_getpos (File
.fp
, &StartOfLine
);
314 mio_getpos (File
.fp
, &File
.filePosition
);
315 File
.currentLine
= NULL
;
316 File
.input
.lineNumber
= 0L;
320 if (File
.line
!= NULL
)
321 vStringClear (File
.line
);
323 setSourceFileParameters (vStringNewInit (fileName
), language
);
324 File
.source
.lineNumber
= 0L;
326 verbose ("OPENING %s as %s language %sfile\n", fileName
,
327 getLanguageName (language
),
328 File
.source
.isHeader
? "include " : "");
333 extern void fileClose (void)
337 /* The line count of the file is 1 too big, since it is one-based
338 * and is incremented upon each newline.
340 if (Option
.printTotals
)
341 addTotals (0, File
.input
.lineNumber
- 1L,
342 getFileSize (vStringValue (File
.input
.name
)));
349 extern boolean
fileEOF (void)
354 /* Action to take for each encountered source newline.
356 static void fileNewline (void)
358 File
.filePosition
= StartOfLine
;
359 File
.newLine
= FALSE
;
360 File
.input
.lineNumber
++;
361 File
.source
.lineNumber
++;
362 DebugStatement ( if (Option
.breakLine
== File
.lineNumber
) lineBreak (); )
363 DebugStatement ( debugPrintf (DEBUG_RAW
, "%6ld: ", File
.lineNumber
); )
366 /* This function reads a single character from the stream, performing newline
369 static int iFileGetc (void)
373 c
= mio_getc (File
.fp
);
375 /* If previous character was a newline, then we're starting a line.
377 if (File
.newLine
&& c
!= EOF
)
380 if (c
== '#' && Option
.lineDirectives
)
382 if (parseLineDirective ())
386 mio_setpos (File
.fp
, &StartOfLine
);
388 c
= mio_getc (File
.fp
);
395 else if (c
== NEWLINE
)
398 mio_getpos (File
.fp
, &StartOfLine
);
400 else if (c
== CRETURN
)
402 /* Turn line breaks into a canonical form. The three commonly
403 * used forms if line breaks: LF (UNIX), CR (MacIntosh), and
404 * CR-LF (MS-DOS) are converted into a generic newline.
406 const int next
= mio_getc (File
.fp
); /* is CR followed by LF? */
409 mio_ungetc (File
.fp
, next
);
411 c
= NEWLINE
; /* convert CR into newline */
413 mio_getpos (File
.fp
, &StartOfLine
);
415 DebugStatement ( debugPutc (DEBUG_RAW
, c
); )
419 extern void ungetcToInputFile (int c
)
421 const size_t len
= sizeof File
.ungetchBuf
/ sizeof File
.ungetchBuf
[0];
423 Assert (File
.ungetchIdx
< len
);
424 /* we cannot rely on the assertion that might be disabled in non-debug mode */
425 if (File
.ungetchIdx
< len
)
426 File
.ungetchBuf
[File
.ungetchIdx
++] = c
;
429 static vString
*iFileGetLine (void)
431 vString
*result
= NULL
;
433 if (File
.line
== NULL
)
434 File
.line
= vStringNew ();
435 vStringClear (File
.line
);
440 vStringPut (File
.line
, c
);
441 if (c
== '\n' || (c
== EOF
&& vStringLength (File
.line
) > 0))
443 vStringTerminate (File
.line
);
445 if (vStringLength (File
.line
) > 0)
446 matchRegex (File
.line
, File
.source
.language
);
452 Assert (result
!= NULL
|| File
.eof
);
456 /* Do not mix use of readLineFromInputFile () and fileGetc () for the same file.
458 extern int getcFromInputFile (void)
462 /* If there is an ungotten character, then return it. Don't do any
463 * other processing on it, though, because we already did that the
464 * first time it was read through fileGetc ().
466 if (File
.ungetchIdx
> 0)
468 c
= File
.ungetchBuf
[--File
.ungetchIdx
];
469 return c
; /* return here to avoid re-calling debugPutc () */
473 if (File
.currentLine
!= NULL
)
475 c
= *File
.currentLine
++;
477 File
.currentLine
= NULL
;
481 vString
* const line
= iFileGetLine ();
483 File
.currentLine
= (unsigned char*) vStringValue (line
);
484 if (File
.currentLine
== NULL
)
490 DebugStatement ( debugPutc (DEBUG_READ
, c
); )
494 /* returns the nth previous character (0 meaning current), or def if nth cannot
495 * be accessed. Note that this can't access previous line data. */
496 extern int getNthPrevCFromInputFile (unsigned int nth
, int def
)
498 const unsigned char *base
= (unsigned char *) vStringValue (File
.line
);
499 const unsigned int offset
= File
.ungetchIdx
+ 1 + nth
;
501 if (File
.currentLine
!= NULL
&& File
.currentLine
>= base
+ offset
)
502 return (int) *(File
.currentLine
- offset
);
507 extern int skipToCharacterInInputFile (int c
)
512 d
= getcFromInputFile ();
513 } while (d
!= EOF
&& d
!= c
);
517 /* An alternative interface to fileGetc (). Do not mix use of readLineFromInputFile()
518 * and fileGetc() for the same file. The returned string does not contain
519 * the terminating newline. A NULL return value means that all lines in the
520 * file have been read and we are at the end of file.
522 extern const unsigned char *readLineFromInputFile (void)
524 vString
* const line
= iFileGetLine ();
525 const unsigned char* result
= NULL
;
528 result
= (const unsigned char*) vStringValue (line
);
529 vStringStripNewline (line
);
530 DebugStatement ( debugPrintf (DEBUG_READ
, "%s\n", result
); )
536 * Raw file line reading with automatic buffer sizing
538 extern char *readLineRaw (vString
*const vLine
, MIO
*const fp
)
542 vStringClear (vLine
);
543 if (fp
== NULL
) /* to free memory allocated to buffer */
544 error (FATAL
, "NULL file pointer");
549 /* If reading the line places any character other than a null or a
550 * newline at the last character position in the buffer (one less
551 * than the buffer size), then we must resize the buffer and
552 * reattempt to read the line.
556 char *const pLastChar
= vStringValue (vLine
) + vStringSize (vLine
) -2;
559 startOfLine
= mio_tell(fp
);
562 result
= mio_gets (fp
, vStringValue (vLine
), (int) vStringSize (vLine
));
566 error (FATAL
| PERROR
, "Failure on attempt to read file");
568 else if (*pLastChar
!= '\0' &&
569 *pLastChar
!= '\n' && *pLastChar
!= '\r')
571 /* buffer overflow */
572 reReadLine
= vStringAutoResize (vLine
);
574 mio_seek (fp
, startOfLine
, SEEK_SET
);
576 error (FATAL
| PERROR
, "input line too big; out of memory");
581 vStringSetLength (vLine
);
582 /* canonicalize new line */
583 eol
= vStringValue (vLine
) + vStringLength (vLine
) - 1;
586 else if (*(eol
- 1) == '\r' && *eol
== '\n')
593 } while (reReadLine
);
598 /* Places into the line buffer the contents of the line referenced by
601 extern char *readSourceLine (vString
*const vLine
, MIOPos location
,
602 long *const pSeekValue
)
604 MIOPos orignalPosition
;
607 mio_getpos (File
.fp
, &orignalPosition
);
608 mio_setpos (File
.fp
, &location
);
609 if (pSeekValue
!= NULL
)
610 *pSeekValue
= mio_tell (File
.fp
);
611 result
= readLineRaw (vLine
, File
.fp
);
613 error (FATAL
, "Unexpected end of file: %s", getInputFileName ());
614 mio_setpos (File
.fp
, &orignalPosition
);
619 /* vi:set tabstop=4 shiftwidth=4: */