Rename readLine() to readLineRaw()
[geany-mirror.git] / ctags / main / read.c
blob9a4f73152f0b65f586934ac4a6cd7495296eb916
1 /*
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).
13 * INCLUDE FILES
15 #include "general.h" /* must always come first */
17 #include <string.h>
18 #include <ctype.h>
19 #include <glib/gstdio.h>
21 #define FILE_WRITE
22 #include "read.h"
23 #include "entry.h"
24 #include "main.h"
25 #include "options.h"
28 * DATA DEFINITIONS
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);
61 if (tail == head)
62 File.path = NULL;
63 else
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);
80 else
81 File.source.tagPath =
82 vStringNewOwn (relativeFilename (vStringValue (fileName),
83 TagFile.directory));
85 if (vStringLength (fileName) > TagFile.max.file)
86 TagFile.max.file = vStringLength (fileName);
88 File.source.isHeader = isIncludeFile (vStringValue (fileName));
89 if (language != -1)
90 File.source.language = language;
91 else
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)
100 vString *pathName;
101 if (isAbsolutePath (vStringValue (fileName)) || File.path == NULL)
102 pathName = vStringNewCopy (fileName);
103 else
104 pathName = combinePathAndFile (vStringValue (File.path),
105 vStringValue (fileName));
106 setSourceFileParameters (pathName, -1);
107 result = TRUE;
109 return result;
113 * Line directive parsing
116 static int skipWhite (void)
118 int c;
120 c = mio_getc (File.fp);
121 while (c == ' ' || c == '\t');
122 return c;
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')
136 lNum = 0;
138 return lNum;
141 /* While ANSI only permits lines of the form:
142 * # line n "filename"
143 * Earlier compilers generated lines of the form
144 * # n filename
145 * GNU C will output lines of the form:
146 * # n "filename"
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 ();
155 if (c == '"')
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);
166 if (c == '\n')
167 mio_ungetc (File.fp, c);
168 vStringPut (fileName, '\0');
170 return fileName;
173 static boolean parseLineDirective (void)
175 boolean result = FALSE;
176 int c = skipWhite ();
177 DebugStatement ( const char* lineStr = ""; )
179 if (isdigit (c))
181 mio_ungetc (File.fp, c);
182 result = TRUE;
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"; )
191 result = TRUE;
194 if (result)
196 const unsigned long lNum = readLineNumber ();
197 if (lNum == 0)
198 result = FALSE;
199 else
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 &&
215 lNum == 1)
217 tagEntryInfo tag;
218 initTagEntry (&tag, baseFilename (vStringValue (fileName)));
220 tag.isFileEntry = TRUE;
221 tag.lineNumberEntry = TRUE;
222 tag.lineNumber = 1;
223 tag.kindName = "file";
224 tag.kind = 'F';
226 makeTagEntry (&tag);
228 vStringDelete (fileName);
229 result = TRUE;
232 return result;
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)
244 #ifdef VMS
245 const char *const openMode = "r";
246 #else
247 const char *const openMode = "rb";
248 #endif
249 boolean opened = FALSE;
251 /* If another file was already open, then close it.
253 if (File.fp != NULL)
255 mio_free (File.fp); /* close any open source file */
256 File.fp = NULL;
259 File.fp = mio_new_file_full (fileName, openMode, g_fopen, fclose);
260 if (File.fp == NULL)
261 error (WARNING | PERROR, "cannot open \"%s\"", fileName);
262 else
264 opened = TRUE;
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;
271 File.eof = FALSE;
272 File.newLine = TRUE;
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 " : "");
284 return opened;
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 */
300 File.fp = NULL;
303 /* check if we got a good buffer */
304 if (buffer == NULL || buffer_size == 0) {
305 opened = FALSE;
306 return opened;
309 opened = TRUE;
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;
317 File.eof = FALSE;
318 File.newLine = TRUE;
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 " : "");
330 return opened;
333 extern void fileClose (void)
335 if (File.fp != NULL)
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)));
344 mio_free (File.fp);
345 File.fp = NULL;
349 extern boolean fileEOF (void)
351 return File.eof;
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
367 * canonicalization.
369 static int iFileGetc (void)
371 int c;
372 readnext:
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)
379 fileNewline ();
380 if (c == '#' && Option.lineDirectives)
382 if (parseLineDirective ())
383 goto readnext;
384 else
386 mio_setpos (File.fp, &StartOfLine);
388 c = mio_getc (File.fp);
393 if (c == EOF)
394 File.eof = TRUE;
395 else if (c == NEWLINE)
397 File.newLine = TRUE;
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? */
408 if (next != NEWLINE)
409 mio_ungetc (File.fp, next);
411 c = NEWLINE; /* convert CR into newline */
412 File.newLine = TRUE;
413 mio_getpos (File.fp, &StartOfLine);
415 DebugStatement ( debugPutc (DEBUG_RAW, c); )
416 return 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;
432 int c;
433 if (File.line == NULL)
434 File.line = vStringNew ();
435 vStringClear (File.line);
438 c = iFileGetc ();
439 if (c != EOF)
440 vStringPut (File.line, c);
441 if (c == '\n' || (c == EOF && vStringLength (File.line) > 0))
443 vStringTerminate (File.line);
444 #ifdef HAVE_REGEX
445 if (vStringLength (File.line) > 0)
446 matchRegex (File.line, File.source.language);
447 #endif
448 result = File.line;
449 break;
451 } while (c != EOF);
452 Assert (result != NULL || File.eof);
453 return result;
456 /* Do not mix use of readLineFromInputFile () and fileGetc () for the same file.
458 extern int getcFromInputFile (void)
460 int c;
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++;
476 if (c == '\0')
477 File.currentLine = NULL;
479 else
481 vString* const line = iFileGetLine ();
482 if (line != NULL)
483 File.currentLine = (unsigned char*) vStringValue (line);
484 if (File.currentLine == NULL)
485 c = EOF;
486 else
487 c = '\0';
489 } while (c == '\0');
490 DebugStatement ( debugPutc (DEBUG_READ, c); )
491 return 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);
503 else
504 return def;
507 extern int skipToCharacterInInputFile (int c)
509 int d;
512 d = getcFromInputFile ();
513 } while (d != EOF && d != c);
514 return d;
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;
526 if (line != NULL)
528 result = (const unsigned char*) vStringValue (line);
529 vStringStripNewline (line);
530 DebugStatement ( debugPrintf (DEBUG_READ, "%s\n", result); )
532 return result;
536 * Raw file line reading with automatic buffer sizing
538 extern char *readLineRaw (vString *const vLine, MIO *const fp)
540 char *result = NULL;
542 vStringClear (vLine);
543 if (fp == NULL) /* to free memory allocated to buffer */
544 error (FATAL, "NULL file pointer");
545 else
547 boolean reReadLine;
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;
557 long startOfLine;
559 startOfLine = mio_tell(fp);
560 reReadLine = FALSE;
561 *pLastChar = '\0';
562 result = mio_gets (fp, vStringValue (vLine), (int) vStringSize (vLine));
563 if (result == NULL)
565 if (! mio_eof (fp))
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);
573 if (reReadLine)
574 mio_seek (fp, startOfLine, SEEK_SET);
575 else
576 error (FATAL | PERROR, "input line too big; out of memory");
578 else
580 char* eol;
581 vStringSetLength (vLine);
582 /* canonicalize new line */
583 eol = vStringValue (vLine) + vStringLength (vLine) - 1;
584 if (*eol == '\r')
585 *eol = '\n';
586 else if (*(eol - 1) == '\r' && *eol == '\n')
588 *(eol - 1) = '\n';
589 *eol = '\0';
590 --vLine->length;
593 } while (reReadLine);
595 return result;
598 /* Places into the line buffer the contents of the line referenced by
599 * "location".
601 extern char *readSourceLine (vString *const vLine, MIOPos location,
602 long *const pSeekValue)
604 MIOPos orignalPosition;
605 char *result;
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);
612 if (result == NULL)
613 error (FATAL, "Unexpected end of file: %s", getInputFileName ());
614 mio_setpos (File.fp, &orignalPosition);
616 return result;
619 /* vi:set tabstop=4 shiftwidth=4: */