Rename MIO variables from fp to mio
[geany-mirror.git] / ctags / main / read.c
blob380d5b9fc492944c9145f1331f2deb3dd232aa28
1 /*
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).
9 */
12 * INCLUDE FILES
14 #include "general.h" /* must always come first */
16 #include <string.h>
17 #include <ctype.h>
18 #include <glib/gstdio.h>
20 #define FILE_WRITE
21 #include "read.h"
22 #include "debug.h"
23 #include "entry.h"
24 #include "main.h"
25 #include "routines.h"
26 #include "options.h"
29 * DATA DEFINITIONS
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);
67 if (tail == head)
68 File.path = NULL;
69 else
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);
89 else
90 File.source.tagPath =
91 vStringNewOwn (relativeFilename (vStringValue (fileName),
92 TagFile.directory));
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;
99 if (language != -1)
100 File.source.language = language;
101 else
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)
112 vString *pathName;
113 if (isAbsolutePath (vStringValue (fileName)) || File.path == NULL)
114 pathName = vStringNewCopy (fileName);
115 else
116 pathName = combinePathAndFile (vStringValue (File.path),
117 vStringValue (fileName));
118 setSourceFileParameters (pathName, -1);
119 result = TRUE;
121 return result;
125 * Line directive parsing
128 static int skipWhite (void)
130 int c;
132 c = mio_getc (File.mio);
133 while (c == ' ' || c == '\t');
134 return c;
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')
148 lNum = 0;
150 return lNum;
153 /* While ANSI only permits lines of the form:
154 * # line n "filename"
155 * Earlier compilers generated lines of the form
156 * # n filename
157 * GNU C will output lines of the form:
158 * # n "filename"
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 ();
167 if (c == '"')
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);
178 if (c == '\n')
179 mio_ungetc (File.mio, c);
180 vStringPut (fileName, '\0');
182 return fileName;
185 static boolean parseLineDirective (void)
187 boolean result = FALSE;
188 int c = skipWhite ();
189 DebugStatement ( const char* lineStr = ""; )
191 if (isdigit (c))
193 mio_ungetc (File.mio, c);
194 result = TRUE;
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"; )
203 result = TRUE;
206 if (result)
208 const unsigned long lNum = readLineNumber ();
209 if (lNum == 0)
210 result = FALSE;
211 else
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 &&
227 lNum == 1)
229 tagEntryInfo tag;
230 initTagEntry (&tag, baseFilename (vStringValue (fileName)), getInputLanguageFileKind ());
232 tag.isFileEntry = TRUE;
233 tag.lineNumberEntry = TRUE;
234 tag.lineNumber = 1;
236 makeTagEntry (&tag);
238 vStringDelete (fileName);
239 result = TRUE;
242 return result;
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 */
262 File.mio = NULL;
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);
268 else
270 opened = TRUE;
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;
277 File.eof = FALSE;
278 File.newLine = TRUE;
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 " : "");
290 return opened;
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 */
306 File.mio = NULL;
309 /* check if we got a good buffer */
310 if (buffer == NULL || buffer_size == 0) {
311 opened = FALSE;
312 return opened;
315 opened = TRUE;
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;
323 File.eof = FALSE;
324 File.newLine = TRUE;
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 " : "");
336 return opened;
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)));
350 mio_free (File.mio);
351 File.mio = NULL;
355 extern boolean fileEOF (void)
357 return File.eof;
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
373 * canonicalization.
375 static int iFileGetc (void)
377 int c;
378 readnext:
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)
385 fileNewline ();
386 if (c == '#' && Option.lineDirectives)
388 if (parseLineDirective ())
389 goto readnext;
390 else
392 mio_setpos (File.mio, &StartOfLine);
393 c = mio_getc (File.mio);
398 if (c == EOF)
399 File.eof = TRUE;
400 else if (c == NEWLINE)
402 File.newLine = TRUE;
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? */
412 if (next != NEWLINE)
413 mio_ungetc (File.mio, next);
415 c = NEWLINE; /* convert CR into newline */
416 File.newLine = TRUE;
417 mio_getpos (File.mio, &StartOfLine);
419 DebugStatement ( debugPutc (DEBUG_RAW, c); )
420 return 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;
436 int c;
437 if (File.line == NULL)
438 File.line = vStringNew ();
439 vStringClear (File.line);
442 c = iFileGetc ();
443 if (c != EOF)
444 vStringPut (File.line, c);
445 if (c == '\n' || (c == EOF && vStringLength (File.line) > 0))
447 vStringTerminate (File.line);
448 #ifdef HAVE_REGEX
449 if (vStringLength (File.line) > 0)
450 matchRegex (File.line, File.source.language);
451 #endif
452 result = File.line;
453 break;
455 } while (c != EOF);
456 Assert (result != NULL || File.eof);
457 return result;
460 /* Do not mix use of readLineFromInputFile () and getcFromInputFile () for the same file.
462 extern int getcFromInputFile (void)
464 int c;
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++;
480 if (c == '\0')
481 File.currentLine = NULL;
483 else
485 vString* const line = iFileGetLine ();
486 if (line != NULL)
487 File.currentLine = (unsigned char*) vStringValue (line);
488 if (File.currentLine == NULL)
489 c = EOF;
490 else
491 c = '\0';
493 } while (c == '\0');
494 DebugStatement ( debugPutc (DEBUG_READ, c); )
495 return 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);
507 else
508 return def;
511 extern int skipToCharacterInInputFile (int c)
513 int d;
516 d = getcFromInputFile ();
517 } while (d != EOF && d != c);
518 return d;
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;
530 if (line != NULL)
532 result = (const unsigned char*) vStringValue (line);
533 vStringStripNewline (line);
534 DebugStatement ( debugPrintf (DEBUG_READ, "%s\n", result); )
536 return result;
540 * Raw file line reading with automatic buffer sizing
542 extern char *readLineRaw (vString *const vLine, MIO *const mio)
544 char *result = NULL;
546 vStringClear (vLine);
547 if (mio == NULL) /* to free memory allocated to buffer */
548 error (FATAL, "NULL file pointer");
549 else
551 boolean reReadLine;
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;
561 long startOfLine;
563 startOfLine = mio_tell(mio);
564 reReadLine = FALSE;
565 *pLastChar = '\0';
566 result = mio_gets (mio, vStringValue (vLine), (int) vStringSize (vLine));
567 if (result == NULL)
569 if (! mio_eof (mio))
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);
577 if (reReadLine)
578 mio_seek (mio, startOfLine, SEEK_SET);
579 else
580 error (FATAL | PERROR, "input line too big; out of memory");
582 else
584 char* eol;
585 vStringSetLength (vLine);
586 /* canonicalize new line */
587 eol = vStringValue (vLine) + vStringLength (vLine) - 1;
588 if (*eol == '\r')
589 *eol = '\n';
590 else if (vStringLength (vLine) != 1 && *(eol - 1) == '\r' && *eol == '\n')
592 *(eol - 1) = '\n';
593 *eol = '\0';
594 --vLine->length;
597 } while (reReadLine);
599 return result;
602 /* Places into the line buffer the contents of the line referenced by
603 * "location".
605 extern char *readLineFromBypass (
606 vString *const vLine, MIOPos location, long *const pSeekValue)
608 MIOPos orignalPosition;
609 char *result;
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. */
620 return result;
623 /* vi:set tabstop=4 shiftwidth=4: */