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 /* Read a character choosing automatically between file or buffer, depending
36 * on which mode we are.
38 #define readNextChar() (mio_getc (File.mio))
40 /* Replaces ungetc() for file. In case of buffer we'll perform the same action:
41 * fpBufferPosition-- and write of the param char into the buf.
43 #define pushBackChar(c) (mio_ungetc (File.mio, c))
46 * FUNCTION DEFINITIONS
49 extern void freeSourceFileResources (void)
51 vStringDelete (File
.name
);
52 vStringDelete (File
.path
);
53 vStringDelete (File
.source
.name
);
54 vStringDelete (File
.line
);
58 * Source file access functions
61 static void setInputFileName (const char *const fileName
)
63 const char *const head
= fileName
;
64 const char *const tail
= baseFilename (head
);
66 if (File
.name
!= NULL
)
67 vStringDelete (File
.name
);
68 File
.name
= vStringNewInit (fileName
);
70 if (File
.path
!= NULL
)
71 vStringDelete (File
.path
);
76 const size_t length
= tail
- head
- 1;
77 File
.path
= vStringNew ();
78 vStringNCopyS (File
.path
, fileName
, length
);
81 static void setSourceFileParameters (vString
*const fileName
, const langType language
)
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
));
100 File
.source
.language
= language
;
102 File
.source
.language
= getFileLanguage (vStringValue (fileName
));
105 static boolean
setSourceFileName (vString
*const fileName
)
107 boolean result
= FALSE
;
108 if (getFileLanguage (vStringValue (fileName
)) != LANG_IGNORE
)
111 if (isAbsolutePath (vStringValue (fileName
)) || File
.path
== NULL
)
112 pathName
= vStringNewCopy (fileName
);
114 pathName
= combinePathAndFile (vStringValue (File
.path
),
115 vStringValue (fileName
));
116 setSourceFileParameters (pathName
, -1);
123 * Line directive parsing
126 static int skipWhite (void)
131 while (c
== ' ' || c
== '\t');
135 static unsigned long readLineNumber (void)
137 unsigned long lNum
= 0;
138 int c
= skipWhite ();
139 while (c
!= EOF
&& isdigit (c
))
141 lNum
= (lNum
* 10) + (c
- '0');
145 if (c
!= ' ' && c
!= '\t')
151 /* While ANSI only permits lines of the form:
152 * # line n "filename"
153 * Earlier compilers generated lines of the form
155 * GNU C will output lines of the form:
157 * So we need to be fairly flexible in what we accept.
159 static vString
*readFileName (void)
161 vString
*const fileName
= vStringNew ();
162 boolean quoteDelimited
= FALSE
;
163 int c
= skipWhite ();
167 c
= readNextChar (); /* skip double-quote */
168 quoteDelimited
= TRUE
;
170 while (c
!= EOF
&& c
!= '\n' &&
171 (quoteDelimited
? (c
!= '"') : (c
!= ' ' && c
!= '\t')))
173 vStringPut (fileName
, c
);
178 vStringPut (fileName
, '\0');
183 static boolean
parseLineDirective (void)
185 boolean result
= FALSE
;
186 int c
= skipWhite ();
187 DebugStatement ( const char* lineStr
= ""; )
194 else if (c
== 'l' && readNextChar () == 'i' &&
195 readNextChar () == 'n' && readNextChar () == 'e')
198 if (c
== ' ' || c
== '\t')
200 DebugStatement ( lineStr
= "line"; )
206 const unsigned long lNum
= readLineNumber ();
211 vString
*const fileName
= readFileName ();
212 if (vStringLength (fileName
) == 0)
214 File
.source
.lineNumber
= lNum
- 1; /* applies to NEXT line */
215 DebugStatement ( debugPrintf (DEBUG_RAW
, "#%s %ld", lineStr
, lNum
); )
217 else if (setSourceFileName (fileName
))
219 File
.source
.lineNumber
= lNum
- 1; /* applies to NEXT line */
220 DebugStatement ( debugPrintf (DEBUG_RAW
, "#%s %ld \"%s\"",
221 lineStr
, lNum
, vStringValue (fileName
)); )
224 if (Option
.include
.fileNames
&& vStringLength (fileName
) > 0 &&
228 initTagEntry (&tag
, baseFilename (vStringValue (fileName
)));
230 tag
.isFileEntry
= TRUE
;
231 tag
.lineNumberEntry
= TRUE
;
233 tag
.kindName
= "file";
238 vStringDelete (fileName
);
246 * Source file I/O operations
249 /* This function opens a source file, and resets the line counter. If it
250 * fails, it will display an error message and leave the File.fp set to NULL.
252 extern boolean
fileOpen (const char *const fileName
, const langType language
)
255 const char *const openMode
= "r";
257 const char *const openMode
= "rb";
259 boolean opened
= FALSE
;
261 /* If another file was already open, then close it.
263 if (File
.mio
!= NULL
)
265 mio_free (File
.mio
); /* close any open source file */
269 File
.mio
= mio_new_file_full (fileName
, openMode
, g_fopen
, fclose
);
270 if (File
.mio
== NULL
)
271 error (WARNING
| PERROR
, "cannot open \"%s\"", fileName
);
276 setInputFileName (fileName
);
277 mio_getpos (File
.mio
, &StartOfLine
);
278 mio_getpos (File
.mio
, &File
.filePosition
);
279 File
.currentLine
= NULL
;
280 File
.lineNumber
= 0L;
284 if (File
.line
!= NULL
)
285 vStringClear (File
.line
);
287 setSourceFileParameters (vStringNewInit (fileName
), language
);
288 File
.source
.lineNumber
= 0L;
290 verbose ("OPENING %s as %s language %sfile\n", fileName
,
291 getLanguageName (language
),
292 File
.source
.isHeader
? "include " : "");
297 /* The user should take care of allocate and free the buffer param.
298 * This func is NOT THREAD SAFE.
299 * The user should not tamper with the buffer while this func is executing.
301 extern boolean
bufferOpen (unsigned char *buffer
, int buffer_size
,
302 const char *const fileName
, const langType language
)
304 boolean opened
= FALSE
;
306 /* Check whether a file of a buffer were already open, then close them.
308 if (File
.mio
!= NULL
) {
309 mio_free (File
.mio
); /* close any open source file */
313 /* check if we got a good buffer */
314 if (buffer
== NULL
|| buffer_size
== 0) {
321 File
.mio
= mio_new_memory (buffer
, buffer_size
, NULL
, NULL
);
322 setInputFileName (fileName
);
323 mio_getpos (File
.mio
, &StartOfLine
);
324 mio_getpos (File
.mio
, &File
.filePosition
);
325 File
.currentLine
= NULL
;
326 File
.lineNumber
= 0L;
330 if (File
.line
!= NULL
)
331 vStringClear (File
.line
);
333 setSourceFileParameters (vStringNewInit (fileName
), language
);
334 File
.source
.lineNumber
= 0L;
336 verbose ("OPENING %s as %s language %sfile\n", fileName
,
337 getLanguageName (language
),
338 File
.source
.isHeader
? "include " : "");
343 extern void fileClose (void)
345 if (File
.mio
!= NULL
)
347 /* The line count of the file is 1 too big, since it is one-based
348 * and is incremented upon each newline.
350 if (Option
.printTotals
)
351 addTotals (0, File
.lineNumber
- 1L,
352 getFileSize (vStringValue (File
.name
)));
359 extern boolean
fileEOF (void)
364 /* Action to take for each encountered source newline.
366 static void fileNewline (void)
368 File
.filePosition
= StartOfLine
;
369 File
.newLine
= FALSE
;
371 File
.source
.lineNumber
++;
372 DebugStatement ( if (Option
.breakLine
== File
.lineNumber
) lineBreak (); )
373 DebugStatement ( debugPrintf (DEBUG_RAW
, "%6ld: ", File
.lineNumber
); )
376 /* This function reads a single character from the stream, performing newline
379 static int iFileGetc (void)
385 /* If previous character was a newline, then we're starting a line.
387 if (File
.newLine
&& c
!= EOF
)
390 if (c
== '#' && Option
.lineDirectives
)
392 if (parseLineDirective ())
396 mio_setpos (File
.mio
, &StartOfLine
);
405 else if (c
== NEWLINE
)
408 mio_getpos (File
.mio
, &StartOfLine
);
410 else if (c
== CRETURN
)
412 /* Turn line breaks into a canonical form. The three commonly
413 * used forms if line breaks: LF (UNIX), CR (MacIntosh), and
414 * CR-LF (MS-DOS) are converted into a generic newline.
416 const int next
= readNextChar (); /* is CR followed by LF? */
421 c
= NEWLINE
; /* convert CR into newline */
423 mio_getpos (File
.mio
, &StartOfLine
);
425 DebugStatement ( debugPutc (DEBUG_RAW
, c
); )
429 extern void fileUngetc (int c
)
434 static vString
*iFileGetLine (void)
436 vString
*result
= NULL
;
438 if (File
.line
== NULL
)
439 File
.line
= vStringNew ();
440 vStringClear (File
.line
);
445 vStringPut (File
.line
, c
);
446 if (c
== '\n' || (c
== EOF
&& vStringLength (File
.line
) > 0))
448 vStringTerminate (File
.line
);
450 if (vStringLength (File
.line
) > 0)
451 matchRegex (File
.line
, File
.source
.language
);
457 Assert (result
!= NULL
|| File
.eof
);
461 /* Do not mix use of fileReadLine () and fileGetc () for the same file.
463 extern int fileGetc (void)
467 /* If there is an ungotten character, then return it. Don't do any
468 * other processing on it, though, because we already did that the
469 * first time it was read through fileGetc ().
471 if (File
.ungetch
!= '\0')
475 return c
; /* return here to avoid re-calling debugPutc () */
479 if (File
.currentLine
!= NULL
)
481 c
= *File
.currentLine
++;
483 File
.currentLine
= NULL
;
487 vString
* const line
= iFileGetLine ();
489 File
.currentLine
= (unsigned char*) vStringValue (line
);
490 if (File
.currentLine
== NULL
)
496 DebugStatement ( debugPutc (DEBUG_READ
, c
); )
500 extern int fileSkipToCharacter (int c
)
506 } while (d
!= EOF
&& d
!= c
);
510 /* An alternative interface to fileGetc (). Do not mix use of fileReadLine()
511 * and fileGetc() for the same file. The returned string does not contain
512 * the terminating newline. A NULL return value means that all lines in the
513 * file have been read and we are at the end of file.
515 extern const unsigned char *fileReadLine (void)
517 vString
* const line
= iFileGetLine ();
518 const unsigned char* result
= NULL
;
521 result
= (const unsigned char*) vStringValue (line
);
522 vStringStripNewline (line
);
523 DebugStatement ( debugPrintf (DEBUG_READ
, "%s\n", result
); )
530 * Source file line reading with automatic buffer sizing
532 extern char *readLine (vString
*const vLine
, MIO
*const mio
)
536 vStringClear (vLine
);
537 if (mio
== NULL
) /* to free memory allocated to buffer */
538 error (FATAL
, "NULL MIO pointer");
543 /* If reading the line places any character other than a null or a
544 * newline at the last character position in the buffer (one less
545 * than the buffer size), then we must resize the buffer and
546 * reattempt to read the line.
550 char *const pLastChar
= vStringValue (vLine
) + vStringSize (vLine
) -2;
553 mio_getpos (mio
, &startOfLine
);
556 result
= mio_gets (mio
, vStringValue (vLine
), (int) vStringSize (vLine
));
560 error (FATAL
| PERROR
, "Failure on attempt to read file");
562 else if (*pLastChar
!= '\0' &&
563 *pLastChar
!= '\n' && *pLastChar
!= '\r')
565 /* buffer overflow */
566 reReadLine
= vStringAutoResize (vLine
);
568 mio_setpos (mio
, &startOfLine
);
570 error (FATAL
| PERROR
, "input line too big; out of memory");
575 vStringSetLength (vLine
);
576 /* canonicalize new line */
577 eol
= vStringValue (vLine
) + vStringLength (vLine
) - 1;
580 else if (*(eol
- 1) == '\r' && *eol
== '\n')
587 } while (reReadLine
);
592 /* Places into the line buffer the contents of the line referenced by
595 extern char *readSourceLine (vString
*const vLine
, MIOPos location
,
596 long *const pSeekValue
)
598 MIOPos orignalPosition
;
601 mio_getpos (File
.mio
, &orignalPosition
);
602 mio_setpos (File
.mio
, &location
);
603 if (pSeekValue
!= NULL
)
604 *pSeekValue
= mio_tell (File
.mio
);
605 result
= readLine (vLine
, File
.mio
);
607 error (FATAL
, "Unexpected end of file: %s", vStringValue (File
.name
));
608 mio_setpos (File
.mio
, &orignalPosition
);
613 /* vi:set tabstop=8 shiftwidth=4: */