2
* $Id: read.c,v 1.7 2006/05/30 04:37:12 darren Exp $
4
* Copyright (c) 1996-2002, Darren Hiebert
6
* This source code is released for free distribution under the terms of the
7
* GNU General Public License.
9
* This module contains low level source and tag file read functions (newline
10
* conversion for source files are performed at this level).
16
#include "general.h" /* must always come first */
21
#include "string_util.h"
34
inputFile File; /* globally read through macros */
35
static fpos_t StartOfLine; /* holds deferred position of start of line */
38
* FUNCTION DEFINITIONS
41
extern void freeSourceFileResources (void)
43
vStringDelete (File.name);
44
vStringDelete (File.path);
45
vStringDelete (File.source.name);
46
vStringDelete (File.line);
49
static char *load_file(const char *fileName) {
54
fp = fopen(fileName, "rb");
60
fseek(fp, 0, SEEK_END);
62
fseek(fp, 0, SEEK_SET);
63
buf = (char *)malloc(len+1);
66
long bytes = fread(buf, sizeof(char), len, fp);
72
buf[len] = 0; // make it null terminated string
78
* Source file access functions
81
static void setInputFileName (const char *const fileName)
83
const char *const head = fileName;
84
const char *const tail = baseFilename (head);
86
if (File.name != NULL)
87
vStringDelete (File.name);
88
File.name = vStringNewInit (fileName);
90
if (File.path != NULL)
91
vStringDelete (File.path);
96
const size_t length = tail - head - 1;
97
File.path = vStringNew ();
98
vStringNCopyS (File.path, fileName, length);
102
static void setSourceFileParameters (vString *const fileName)
104
if (File.source.name != NULL)
105
vStringDelete (File.source.name);
106
File.source.name = fileName;
108
if (File.source.tagPath != NULL)
109
eFree (File.source.tagPath);
110
if (! Option.tagRelative || isAbsolutePath (vStringValue (fileName)))
111
File.source.tagPath = eStrdup (vStringValue (fileName));
113
File.source.tagPath =
114
relativeFilename (vStringValue (fileName), TagFile.directory);
116
if (vStringLength (fileName) > TagFile.max.file)
117
TagFile.max.file = vStringLength (fileName);
119
File.source.isHeader = isIncludeFile (vStringValue (fileName));
120
File.source.language = getFileLanguage (vStringValue (fileName));
123
static boolean setSourceFileName (vString *const fileName)
125
boolean result = FALSE;
126
if (getFileLanguage (vStringValue (fileName)) != LANG_IGNORE)
129
if (isAbsolutePath (vStringValue (fileName)) || File.path == NULL)
130
pathName = vStringNewCopy (fileName);
132
pathName = combinePathAndFile (
133
vStringValue (File.path), vStringValue (fileName));
134
setSourceFileParameters (pathName);
141
* Line directive parsing
144
static int skipWhite (void)
149
while (c == ' ' || c == '\t');
153
static unsigned long readLineNumber (void)
155
unsigned long lNum = 0;
156
int c = skipWhite ();
157
while (c != EOF && isdigit (c))
159
lNum = (lNum * 10) + (c - '0');
163
if (c != ' ' && c != '\t')
169
/* While ANSI only permits lines of the form:
170
* # line n "filename"
171
* Earlier compilers generated lines of the form
173
* GNU C will output lines of the form:
175
* So we need to be fairly flexible in what we accept.
177
static vString *readFileName (void)
179
vString *const fileName = vStringNew ();
180
boolean quoteDelimited = FALSE;
181
int c = skipWhite ();
185
c = getc (File.fp); /* skip double-quote */
186
quoteDelimited = TRUE;
188
while (c != EOF && c != '\n' &&
189
(quoteDelimited ? (c != '"') : (c != ' ' && c != '\t')))
191
vStringPut (fileName, c);
196
vStringPut (fileName, '\0');
201
static boolean parseLineDirective (void)
203
boolean result = FALSE;
204
int c = skipWhite ();
205
DebugStatement ( const char* lineStr = ""; )
212
else if (c == 'l' && getc (File.fp) == 'i' &&
213
getc (File.fp) == 'n' && getc (File.fp) == 'e')
216
if (c == ' ' || c == '\t')
218
DebugStatement ( lineStr = "line"; )
224
const unsigned long lNum = readLineNumber ();
229
vString *const fileName = readFileName ();
230
if (vStringLength (fileName) == 0)
232
File.source.lineNumber = lNum - 1; /* applies to NEXT line */
233
DebugStatement ( debugPrintf (DEBUG_RAW, "#%s %ld", lineStr, lNum); )
235
else if (setSourceFileName (fileName))
237
File.source.lineNumber = lNum - 1; /* applies to NEXT line */
238
DebugStatement ( debugPrintf (DEBUG_RAW, "#%s %ld \"%s\"",
239
lineStr, lNum, vStringValue (fileName)); )
242
if (Option.include.fileNames && vStringLength (fileName) > 0 &&
246
initTagEntry (&tag, baseFilename (vStringValue (fileName)));
248
tag.isFileEntry = TRUE;
249
tag.lineNumberEntry = TRUE;
251
tag.kindName = "file";
256
vStringDelete (fileName);
264
* Source file I/O operations
267
/* This function opens a source file, and resets the line counter. If it
268
* fails, it will display an error message and leave the File.fp set to NULL.
270
extern boolean fileOpen (const char *const fileName, const langType language)
273
const char *const openMode = "r";
275
const char *const openMode = "rb";
277
boolean opened = FALSE;
279
/* If another file was already open, then close it.
283
fclose (File.fp); /* close any open source file */
287
File.fp = fopen (fileName, openMode);
289
error (WARNING | PERROR, "cannot open \"%s\"", fileName);
294
setInputFileName (fileName);
295
fgetpos (File.fp, &StartOfLine);
296
fgetpos (File.fp, &File.filePosition);
297
File.currentLine = NULL;
298
File.language = language;
299
File.lineNumber = 0L;
303
if (File.line != NULL)
304
vStringClear (File.line);
306
setSourceFileParameters (vStringNewInit (fileName));
307
File.source.lineNumber = 0L;
309
verbose ("OPENING %s as %s language %sfile\n", fileName,
310
getLanguageName (language),
311
File.source.isHeader ? "include " : "");
316
extern void fileClose (void)
320
/* The line count of the file is 1 too big, since it is one-based
321
* and is incremented upon each newline.
323
if (Option.printTotals)
325
fileStatus *status = eStat (vStringValue (File.name));
326
addTotals (0, File.lineNumber - 1L, status->size);
333
extern boolean fileEOF (void)
338
/* Action to take for each encountered source newline.
340
static void fileNewline (void)
342
File.filePosition = StartOfLine;
343
File.newLine = FALSE;
345
File.source.lineNumber++;
346
DebugStatement ( if (Option.breakLine == File.lineNumber) lineBreak (); )
347
DebugStatement ( debugPrintf (DEBUG_RAW, "%6ld: ", File.lineNumber); )
350
/* This function reads a single character from the stream, performing newline
353
static int iFileGetc (void)
359
/* If previous character was a newline, then we're starting a line.
361
if (File.newLine && c != EOF)
364
if (c == '#' && Option.lineDirectives)
366
if (parseLineDirective ())
370
fsetpos (File.fp, &StartOfLine);
378
else if (c == NEWLINE)
381
fgetpos (File.fp, &StartOfLine);
383
else if (c == CRETURN)
385
/* Turn line breaks into a canonical form. The three commonly
386
* used forms if line breaks: LF (UNIX), CR (MacIntosh), and
387
* CR-LF (MS-DOS) are converted into a generic newline.
389
const int next = getc (File.fp); /* is CR followed by LF? */
392
ungetc (next, File.fp);
394
c = NEWLINE; /* convert CR into newline */
396
fgetpos (File.fp, &StartOfLine);
398
DebugStatement ( debugPutc (DEBUG_RAW, c); )
402
extern void fileUngetc (int c)
407
static vString *iFileGetLine (void)
409
static list_t *replacements = (list_t *)0;
410
static int first = 1;
412
vString *result = NULL;
414
if (File.line == NULL)
415
File.line = vStringNew ();
416
vStringClear (File.line);
421
vStringPut (File.line, c);
422
if (c == '\n' || (c == EOF && vStringLength (File.line) > 0))
424
vStringTerminate (File.line);
426
if (vStringLength (File.line) > 0)
427
matchRegex (File.line, File.source.language);
433
Assert (result != NULL || File.eof);
435
/* try to load the file once */
437
char *content = (char*)0;
438
char *file_name = getenv("CTAGS_REPLACEMENTS");
443
content = load_file(file_name);
445
replacements = string_split(content, "=");
451
if( result && replacements && replacements->size ) {
454
char *src = result->buffer;
457
list_node_t *node = replacements->head;
460
tmp = string_replace(new_str, ((string_pair_t*)node->data)->key, ((string_pair_t*)node->data)->data);
468
/* advance to next item in the list */
472
if(new_str != result->buffer) {
473
vStringClear(File.line);
474
vStringCatS(File.line, new_str);
482
/* Do not mix use of fileReadLine () and fileGetc () for the same file.
484
extern int fileGetc (void)
488
/* If there is an ungotten character, then return it. Don't do any
489
* other processing on it, though, because we already did that the
490
* first time it was read through fileGetc ().
492
if (File.ungetch != '\0')
496
return c; /* return here to avoid re-calling debugPutc () */
500
if (File.currentLine != NULL)
502
c = *File.currentLine++;
504
File.currentLine = NULL;
508
vString* const line = iFileGetLine ();
510
File.currentLine = (unsigned char*) vStringValue (line);
511
if (File.currentLine == NULL)
517
DebugStatement ( debugPutc (DEBUG_READ, c); )
521
/* An alternative interface to fileGetc (). Do not mix use of fileReadLine()
522
* and fileGetc() 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 *fileReadLine (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
* Source file line reading with automatic buffer sizing
542
extern char *readLine (vString *const vLine, FILE *const fp)
546
vStringClear (vLine);
547
if (fp == 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
fgetpos (fp, &startOfLine);
566
result = fgets (vStringValue (vLine), (int) vStringSize (vLine), fp);
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
fsetpos (fp, &startOfLine);
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 (*(eol - 1) == '\r' && *eol == '\n')
597
} while (reReadLine);
602
/* Places into the line buffer the contents of the line referenced by
605
extern char *readSourceLine (
606
vString *const vLine, fpos_t location, long *const pSeekValue)
608
fpos_t orignalPosition;
611
fgetpos (File.fp, &orignalPosition);
612
fsetpos (File.fp, &location);
613
if (pSeekValue != NULL)
614
*pSeekValue = ftell (File.fp);
615
result = readLine (vLine, File.fp);
617
error (FATAL, "Unexpected end of file: %s", vStringValue (File.name));
618
fsetpos (File.fp, &orignalPosition);
623
extern char *readSourceLines (vString* const vLine, fpos_t location, fpos_t endPos)
625
fpos_t orignalPosition;
627
long startpos, endpos, currpos;
628
vString* const tmpstr = vStringNew();
630
fgetpos (File.fp, &orignalPosition);
631
fsetpos (File.fp, &location);
633
startpos = ftell(File.fp);
634
/* set the cursor at the end position */
635
fsetpos (File.fp, &endPos);
636
endpos = ftell(File.fp);
638
/* set pointer to start point */
639
fsetpos (File.fp, &location);
644
result = readLine (tmpstr, File.fp);
646
error (FATAL, "Unexpected end of file: %s", vStringValue (File.name));
648
vStringCat(vLine, tmpstr);
649
currpos = ftell(File.fp);
650
if(currpos > endpos){
655
fsetpos (File.fp, &orignalPosition);
656
vStringDelete(tmpstr);
657
return vLine->buffer;
660
/* vi:set tabstop=4 shiftwidth=4: */