2 * Copyright (c) 1996-2002, Darren Hiebert
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License version 2 or (at your option) any later version.
7 * This module contains low level input and tag file read functions (newline
8 * conversion for input files are performed at this level).
14 #include "general.h" /* must always come first */
18 #include <glib/gstdio.h>
31 inputFile File
; /* globally read through macros */
32 static MIOPos StartOfLine
; /* holds deferred position of start of line */
36 * FUNCTION DEFINITIONS
39 extern kindOption
*getInputLanguageFileKind (void)
41 return getLanguageFileKind (File
.input
.language
);
44 extern void freeSourceFileResources (void)
46 vStringDelete (File
.input
.name
);
47 vStringDelete (File
.path
);
48 vStringDelete (File
.source
.name
);
49 vStringDelete (File
.line
);
53 * Input file access functions
56 static void setInputFileName (const char *const fileName
)
58 const char *const head
= fileName
;
59 const char *const tail
= baseFilename (head
);
61 if (File
.input
.name
!= NULL
)
62 vStringDelete (File
.input
.name
);
63 File
.input
.name
= vStringNewInit (fileName
);
65 if (File
.path
!= NULL
)
66 vStringDelete (File
.path
);
71 const size_t length
= tail
- head
- 1;
72 File
.path
= vStringNew ();
73 vStringNCopyS (File
.path
, fileName
, length
);
76 static void setSourceFileParameters (vString
*const fileName
, const langType language
)
78 if (File
.source
.name
!= NULL
)
79 vStringDelete (File
.source
.name
);
80 if (File
.input
.name
!= NULL
)
81 vStringDelete (File
.input
.name
);
82 File
.source
.name
= fileName
;
83 File
.input
.name
= vStringNewCopy(fileName
);
85 if (File
.source
.tagPath
!= NULL
)
86 eFree (File
.source
.tagPath
);
87 if (! Option
.tagRelative
|| isAbsolutePath (vStringValue (fileName
)))
88 File
.source
.tagPath
= vStringNewCopy (fileName
);
91 vStringNewOwn (relativeFilename (vStringValue (fileName
),
94 if (vStringLength (fileName
) > TagFile
.max
.file
)
95 TagFile
.max
.file
= vStringLength (fileName
);
97 File
.source
.isHeader
= isIncludeFile (vStringValue (fileName
));
98 File
.input
.isHeader
= File
.source
.isHeader
;
100 File
.source
.language
= language
;
102 File
.source
.language
= getFileLanguage (vStringValue (fileName
));
103 File
.input
.language
= File
.source
.language
;
106 static boolean
setSourceFileName (vString
*const fileName
)
108 const langType language
= getFileLanguage (vStringValue (fileName
));
109 boolean result
= FALSE
;
110 if (language
!= LANG_IGNORE
)
113 if (isAbsolutePath (vStringValue (fileName
)) || File
.path
== NULL
)
114 pathName
= vStringNewCopy (fileName
);
116 pathName
= combinePathAndFile (vStringValue (File
.path
),
117 vStringValue (fileName
));
118 setSourceFileParameters (pathName
, -1);
125 * Line directive parsing
128 static int skipWhite (void)
132 c
= mio_getc (File
.mio
);
133 while (c
== ' ' || c
== '\t');
137 static unsigned long readLineNumber (void)
139 unsigned long lNum
= 0;
140 int c
= skipWhite ();
141 while (c
!= EOF
&& isdigit (c
))
143 lNum
= (lNum
* 10) + (c
- '0');
144 c
= mio_getc (File
.mio
);
146 mio_ungetc (File
.mio
, c
);
147 if (c
!= ' ' && c
!= '\t')
153 /* While ANSI only permits lines of the form:
154 * # line n "filename"
155 * Earlier compilers generated lines of the form
157 * GNU C will output lines of the form:
159 * So we need to be fairly flexible in what we accept.
161 static vString
*readFileName (void)
163 vString
*const fileName
= vStringNew ();
164 boolean quoteDelimited
= FALSE
;
165 int c
= skipWhite ();
169 c
= mio_getc (File
.mio
); /* skip double-quote */
170 quoteDelimited
= TRUE
;
172 while (c
!= EOF
&& c
!= '\n' &&
173 (quoteDelimited
? (c
!= '"') : (c
!= ' ' && c
!= '\t')))
175 vStringPut (fileName
, c
);
176 c
= mio_getc (File
.mio
);
179 mio_ungetc (File
.mio
, c
);
180 vStringPut (fileName
, '\0');
185 static boolean
parseLineDirective (void)
187 boolean result
= FALSE
;
188 int c
= skipWhite ();
189 DebugStatement ( const char* lineStr
= ""; )
193 mio_ungetc (File
.mio
, c
);
196 else if (c
== 'l' && mio_getc (File
.mio
) == 'i' &&
197 mio_getc (File
.mio
) == 'n' && mio_getc (File
.mio
) == 'e')
199 c
= mio_getc (File
.mio
);
200 if (c
== ' ' || c
== '\t')
202 DebugStatement ( lineStr
= "line"; )
208 const unsigned long lNum
= readLineNumber ();
213 vString
*const fileName
= readFileName ();
214 if (vStringLength (fileName
) == 0)
216 File
.source
.lineNumber
= lNum
- 1; /* applies to NEXT line */
217 DebugStatement ( debugPrintf (DEBUG_RAW
, "#%s %ld", lineStr
, lNum
); )
219 else if (setSourceFileName (fileName
))
221 File
.source
.lineNumber
= lNum
- 1; /* applies to NEXT line */
222 DebugStatement ( debugPrintf (DEBUG_RAW
, "#%s %ld \"%s\"",
223 lineStr
, lNum
, vStringValue (fileName
)); )
226 if (Option
.include
.fileNames
&& vStringLength (fileName
) > 0 &&
230 initTagEntry (&tag
, baseFilename (vStringValue (fileName
)), getInputLanguageFileKind ());
232 tag
.isFileEntry
= TRUE
;
233 tag
.lineNumberEntry
= TRUE
;
238 vStringDelete (fileName
);
246 * Input file I/O operations
249 /* This function opens an input file, and resets the line counter. If it
250 * fails, it will display an error message and leave the File.mio set to NULL.
252 extern boolean
fileOpen (const char *const fileName
, const langType language
)
254 const char *const openMode
= "rb";
255 boolean opened
= FALSE
;
257 /* If another file was already open, then close it.
259 if (File
.mio
!= NULL
)
261 mio_free (File
.mio
); /* close any open source file */
265 File
.mio
= mio_new_file_full (fileName
, openMode
, g_fopen
, fclose
);
266 if (File
.mio
== NULL
)
267 error (WARNING
| PERROR
, "cannot open \"%s\"", fileName
);
272 setInputFileName (fileName
);
273 mio_getpos (File
.mio
, &StartOfLine
);
274 mio_getpos (File
.mio
, &File
.filePosition
);
275 File
.currentLine
= NULL
;
276 File
.input
.lineNumber
= 0L;
280 if (File
.line
!= NULL
)
281 vStringClear (File
.line
);
283 setSourceFileParameters (vStringNewInit (fileName
), language
);
284 File
.source
.lineNumber
= 0L;
286 verbose ("OPENING %s as %s language %sfile\n", fileName
,
287 getLanguageName (language
),
288 File
.source
.isHeader
? "include " : "");
293 /* The user should take care of allocate and free the buffer param.
294 * This func is NOT THREAD SAFE.
295 * The user should not tamper with the buffer while this func is executing.
297 extern boolean
bufferOpen (unsigned char *buffer
, size_t buffer_size
,
298 const char *const fileName
, const langType language
)
300 boolean opened
= FALSE
;
302 /* Check whether a file of a buffer were already open, then close them.
304 if (File
.mio
!= NULL
) {
305 mio_free (File
.mio
); /* close any open source file */
309 /* check if we got a good buffer */
310 if (buffer
== NULL
|| buffer_size
== 0) {
317 File
.mio
= mio_new_memory (buffer
, buffer_size
, NULL
, NULL
);
318 setInputFileName (fileName
);
319 mio_getpos (File
.mio
, &StartOfLine
);
320 mio_getpos (File
.mio
, &File
.filePosition
);
321 File
.currentLine
= NULL
;
322 File
.input
.lineNumber
= 0L;
326 if (File
.line
!= NULL
)
327 vStringClear (File
.line
);
329 setSourceFileParameters (vStringNewInit (fileName
), language
);
330 File
.source
.lineNumber
= 0L;
332 verbose ("OPENING %s as %s language %sfile\n", fileName
,
333 getLanguageName (language
),
334 File
.source
.isHeader
? "include " : "");
339 extern void fileClose (void)
341 if (File
.mio
!= NULL
)
343 /* The line count of the file is 1 too big, since it is one-based
344 * and is incremented upon each newline.
346 if (Option
.printTotals
)
347 addTotals (0, File
.input
.lineNumber
- 1L,
348 getFileSize (vStringValue (File
.input
.name
)));
355 extern boolean
fileEOF (void)
360 /* Action to take for each encountered input newline.
362 static void fileNewline (void)
364 File
.filePosition
= StartOfLine
;
365 File
.newLine
= FALSE
;
366 File
.input
.lineNumber
++;
367 File
.source
.lineNumber
++;
368 DebugStatement ( if (Option
.breakLine
== File
.lineNumber
) lineBreak (); )
369 DebugStatement ( debugPrintf (DEBUG_RAW
, "%6ld: ", File
.lineNumber
); )
372 /* This function reads a single character from the stream, performing newline
375 static int iFileGetc (void)
379 c
= mio_getc (File
.mio
);
381 /* If previous character was a newline, then we're starting a line.
383 if (File
.newLine
&& c
!= EOF
)
386 if (c
== '#' && Option
.lineDirectives
)
388 if (parseLineDirective ())
392 mio_setpos (File
.mio
, &StartOfLine
);
393 c
= mio_getc (File
.mio
);
400 else if (c
== NEWLINE
)
403 mio_getpos (File
.mio
, &StartOfLine
);
405 else if (c
== CRETURN
)
407 /* Turn line breaks into a canonical form. The three commonly
408 * used forms if line breaks: LF (UNIX/Mac OS X), CR (Mac OS 9),
409 * and CR-LF (MS-DOS) are converted into a generic newline.
411 const int next
= mio_getc (File
.mio
); /* is CR followed by LF? */
413 mio_ungetc (File
.mio
, next
);
415 c
= NEWLINE
; /* convert CR into newline */
417 mio_getpos (File
.mio
, &StartOfLine
);
419 DebugStatement ( debugPutc (DEBUG_RAW
, c
); )
423 extern void ungetcToInputFile (int c
)
425 const size_t len
= ARRAY_SIZE (File
.ungetchBuf
);
427 Assert (File
.ungetchIdx
< len
);
428 /* we cannot rely on the assertion that might be disabled in non-debug mode */
429 if (File
.ungetchIdx
< len
)
430 File
.ungetchBuf
[File
.ungetchIdx
++] = c
;
433 static vString
*iFileGetLine (void)
435 vString
*result
= NULL
;
437 if (File
.line
== NULL
)
438 File
.line
= vStringNew ();
439 vStringClear (File
.line
);
444 vStringPut (File
.line
, c
);
445 if (c
== '\n' || (c
== EOF
&& vStringLength (File
.line
) > 0))
447 vStringTerminate (File
.line
);
449 if (vStringLength (File
.line
) > 0)
450 matchRegex (File
.line
, File
.source
.language
);
456 Assert (result
!= NULL
|| File
.eof
);
460 /* Do not mix use of readLineFromInputFile () and getcFromInputFile () for the same file.
462 extern int getcFromInputFile (void)
466 /* If there is an ungotten character, then return it. Don't do any
467 * other processing on it, though, because we already did that the
468 * first time it was read through getcFromInputFile ().
470 if (File
.ungetchIdx
> 0)
472 c
= File
.ungetchBuf
[--File
.ungetchIdx
];
473 return c
; /* return here to avoid re-calling debugPutc () */
477 if (File
.currentLine
!= NULL
)
479 c
= *File
.currentLine
++;
481 File
.currentLine
= NULL
;
485 vString
* const line
= iFileGetLine ();
487 File
.currentLine
= (unsigned char*) vStringValue (line
);
488 if (File
.currentLine
== NULL
)
494 DebugStatement ( debugPutc (DEBUG_READ
, c
); )
498 /* returns the nth previous character (0 meaning current), or def if nth cannot
499 * be accessed. Note that this can't access previous line data. */
500 extern int getNthPrevCFromInputFile (unsigned int nth
, int def
)
502 const unsigned char *base
= (unsigned char *) vStringValue (File
.line
);
503 const unsigned int offset
= File
.ungetchIdx
+ 1 + nth
;
505 if (File
.currentLine
!= NULL
&& File
.currentLine
>= base
+ offset
)
506 return (int) *(File
.currentLine
- offset
);
511 extern int skipToCharacterInInputFile (int c
)
516 d
= getcFromInputFile ();
517 } while (d
!= EOF
&& d
!= c
);
521 /* An alternative interface to getcFromInputFile (). Do not mix use of readLineFromInputFile()
522 * and getcFromInputFile() for the same file. The returned string does not contain
523 * the terminating newline. A NULL return value means that all lines in the
524 * file have been read and we are at the end of file.
526 extern const unsigned char *readLineFromInputFile (void)
528 vString
* const line
= iFileGetLine ();
529 const unsigned char* result
= NULL
;
532 result
= (const unsigned char*) vStringValue (line
);
533 vStringStripNewline (line
);
534 DebugStatement ( debugPrintf (DEBUG_READ
, "%s\n", result
); )
540 * Raw file line reading with automatic buffer sizing
542 extern char *readLineRaw (vString
*const vLine
, MIO
*const mio
)
546 vStringClear (vLine
);
547 if (mio
== NULL
) /* to free memory allocated to buffer */
548 error (FATAL
, "NULL file pointer");
553 /* If reading the line places any character other than a null or a
554 * newline at the last character position in the buffer (one less
555 * than the buffer size), then we must resize the buffer and
556 * reattempt to read the line.
560 char *const pLastChar
= vStringValue (vLine
) + vStringSize (vLine
) -2;
563 startOfLine
= mio_tell(mio
);
566 result
= mio_gets (mio
, vStringValue (vLine
), (int) vStringSize (vLine
));
570 error (FATAL
| PERROR
, "Failure on attempt to read file");
572 else if (*pLastChar
!= '\0' &&
573 *pLastChar
!= '\n' && *pLastChar
!= '\r')
575 /* buffer overflow */
576 reReadLine
= vStringAutoResize (vLine
);
578 mio_seek (mio
, startOfLine
, SEEK_SET
);
580 error (FATAL
| PERROR
, "input line too big; out of memory");
585 vStringSetLength (vLine
);
586 /* canonicalize new line */
587 eol
= vStringValue (vLine
) + vStringLength (vLine
) - 1;
590 else if (vStringLength (vLine
) != 1 && *(eol
- 1) == '\r' && *eol
== '\n')
597 } while (reReadLine
);
602 /* Places into the line buffer the contents of the line referenced by
605 extern char *readLineFromBypass (
606 vString
*const vLine
, MIOPos location
, long *const pSeekValue
)
608 MIOPos orignalPosition
;
611 mio_getpos (File
.mio
, &orignalPosition
);
612 mio_setpos (File
.mio
, &location
);
613 if (pSeekValue
!= NULL
)
614 *pSeekValue
= mio_tell (File
.mio
);
615 result
= readLineRaw (vLine
, File
.mio
);
616 mio_setpos (File
.mio
, &orignalPosition
);
617 /* If the file is empty, we can't get the line
618 for location 0. readLineFromBypass doesn't know
619 what itself should do; just report it to the caller. */
623 /* vi:set tabstop=4 shiftwidth=4: */