📄 read.c
字号:
/** $Id: read.c,v 1.6 2002/06/19 05:27:17 darren Exp $** Copyright (c) 1996-2002, Darren Hiebert** This source code is released for free distribution under the terms of the* GNU General Public License.** This module contains low level source and tag file read functions (newline* conversion for source files are performed at this level).*//** INCLUDE FILES*/#include "general.h" /* must always come first */#include <string.h>#include <ctype.h>#define FILE_WRITE#include "read.h"#include "debug.h"#include "entry.h"#include "main.h"#include "routines.h"#include "options.h"/** DATA DEFINITIONS*/inputFile File; /* globally read through macros */static fpos_t StartOfLine; /* holds deferred position of start of line *//** FUNCTION DEFINITIONS*/extern void freeSourceFileResources (void){ vStringDelete (File.name); vStringDelete (File.path); vStringDelete (File.source.name); vStringDelete (File.line);}/* * Source file access functions */static void setInputFileName (const char *const fileName){ const char *const head = fileName; const char *const tail = baseFilename (head); if (File.name != NULL) vStringDelete (File.name); File.name = vStringNewInit (fileName); if (File.path != NULL) vStringDelete (File.path); if (tail == head) File.path = NULL; else { const size_t length = tail - head - 1; File.path = vStringNew (); vStringNCopyS (File.path, fileName, length); }}static void setSourceFileParameters (vString *const fileName){ if (File.source.name != NULL) vStringDelete (File.source.name); File.source.name = fileName; if (File.source.tagPath != NULL) eFree (File.source.tagPath); if (! Option.tagRelative || isAbsolutePath (vStringValue (fileName))) File.source.tagPath = eStrdup (vStringValue (fileName)); else File.source.tagPath = relativeFilename (vStringValue (fileName), TagFile.directory); if (vStringLength (fileName) > TagFile.max.file) TagFile.max.file = vStringLength (fileName); File.source.isHeader = isIncludeFile (vStringValue (fileName)); File.source.language = getFileLanguage (vStringValue (fileName));}static boolean setSourceFileName (vString *const fileName){ boolean result = FALSE; if (getFileLanguage (vStringValue (fileName)) != LANG_IGNORE) { vString *pathName; if (isAbsolutePath (vStringValue (fileName)) || File.path == NULL) pathName = vStringNewCopy (fileName); else pathName = combinePathAndFile (vStringValue (File.path), vStringValue (fileName)); setSourceFileParameters (pathName); result = TRUE; } return result;}/* * Line directive parsing */static int skipWhite (void){ int c; do c = getc (File.fp); while (c == ' ' || c == '\t'); return c;}static unsigned long readLineNumber (void){ unsigned long lNum = 0; int c = skipWhite (); while (c != EOF && isdigit (c)) { lNum = (lNum * 10) + (c - '0'); c = getc (File.fp); } ungetc (c, File.fp); if (c != ' ' && c != '\t') lNum = 0; return lNum;}/* While ANSI only permits lines of the form: * # line n "filename" * Earlier compilers generated lines of the form * # n filename * GNU C will output lines of the form: * # n "filename" * So we need to be fairly flexible in what we accept. */static vString *readFileName (void){ vString *const fileName = vStringNew (); boolean quoteDelimited = FALSE; int c = skipWhite (); if (c == '"') { c = getc (File.fp); /* skip double-quote */ quoteDelimited = TRUE; } while (c != EOF && c != '\n' && (quoteDelimited ? (c != '"') : (c != ' ' && c != '\t'))) { vStringPut (fileName, c); c = getc (File.fp); } if (c == '\n') ungetc (c, File.fp); vStringPut (fileName, '\0'); return fileName;}static boolean parseLineDirective (void){ boolean result = FALSE; int c = skipWhite (); DebugStatement ( const char* lineStr = ""; ) if (isdigit (c)) { ungetc (c, File.fp); result = TRUE; } else if (c == 'l' && getc (File.fp) == 'i' && getc (File.fp) == 'n' && getc (File.fp) == 'e') { c = getc (File.fp); if (c == ' ' || c == '\t') { DebugStatement ( lineStr = "line"; ) result = TRUE; } } if (result) { const unsigned long lNum = readLineNumber (); if (lNum == 0) result = FALSE; else { vString *const fileName = readFileName (); if (vStringLength (fileName) == 0) { File.source.lineNumber = lNum - 1; /* applies to NEXT line */ DebugStatement ( debugPrintf (DEBUG_RAW, "#%s %ld", lineStr, lNum); ) } else if (setSourceFileName (fileName)) { File.source.lineNumber = lNum - 1; /* applies to NEXT line */ DebugStatement ( debugPrintf (DEBUG_RAW, "#%s %ld \"%s\"", lineStr, lNum, vStringValue (fileName)); ) } if (Option.include.fileNames && vStringLength (fileName) > 0 && lNum == 1) { tagEntryInfo tag; initTagEntry (&tag, baseFilename (vStringValue (fileName))); tag.isFileEntry = TRUE; tag.lineNumberEntry = TRUE; tag.lineNumber = 1; tag.kindName = "file"; tag.kind = 'F'; makeTagEntry (&tag); } vStringDelete (fileName); result = TRUE; } } return result;}/* * Source file I/O operations *//* This function opens a source file, and resets the line counter. If it * fails, it will display an error message and leave the File.fp set to NULL. */extern boolean fileOpen (const char *const fileName, const langType language){#ifdef VMS const char *const openMode = "r";#else const char *const openMode = "rb";#endif boolean opened = FALSE; /* If another file was already open, then close it. */ if (File.fp != NULL) { fclose (File.fp); /* close any open source file */ File.fp = NULL; } File.fp = fopen (fileName, openMode); if (File.fp == NULL) error (WARNING | PERROR, "cannot open \"%s\"", fileName); else { opened = TRUE; setInputFileName (fileName); fgetpos (File.fp, &StartOfLine); fgetpos (File.fp, &File.filePosition); File.currentLine = NULL; File.language = language; File.lineNumber = 0L; File.eof = FALSE; File.newLine = TRUE; if (File.line != NULL) vStringClear (File.line); setSourceFileParameters (vStringNewInit (fileName)); File.source.lineNumber = 0L; verbose ("OPENING %s as %s language %sfile\n", fileName, getLanguageName (language), File.source.isHeader ? "include " : ""); } return opened;}extern void fileClose (void){ if (File.fp != NULL) { /* The line count of the file is 1 too big, since it is one-based * and is incremented upon each newline. */ if (Option.printTotals) { fileStatus *status = eStat (vStringValue (File.name)); addTotals (0, File.lineNumber - 1L, status->size); } fclose (File.fp); File.fp = NULL; }}extern boolean fileEOF (void){ return File.eof;}/* Action to take for each encountered source newline. */static void fileNewline (void){ File.filePosition = StartOfLine; File.newLine = FALSE; File.lineNumber++; File.source.lineNumber++; DebugStatement ( if (Option.breakLine == File.lineNumber) lineBreak (); ) DebugStatement ( debugPrintf (DEBUG_RAW, "%6ld: ", File.lineNumber); )}/* This function reads a single character from the stream, performing newline * canonicalization. */static int iFileGetc (void){ int c;readnext: c = getc (File.fp); /* If previous character was a newline, then we're starting a line. */ if (File.newLine && c != EOF) { fileNewline (); if (c == '#' && Option.lineDirectives) { if (parseLineDirective ()) goto readnext; else { fsetpos (File.fp, &StartOfLine); c = getc (File.fp); } } } if (c == EOF) File.eof = TRUE; else if (c == NEWLINE) { File.newLine = TRUE; fgetpos (File.fp, &StartOfLine); } else if (c == CRETURN) { /* Turn line breaks into a canonical form. The three commonly * used forms if line breaks: LF (UNIX), CR (MacIntosh), and * CR-LF (MS-DOS) are converted into a generic newline. */ const int next = getc (File.fp); /* is CR followed by LF? */ if (next != NEWLINE) ungetc (next, File.fp); c = NEWLINE; /* convert CR into newline */ File.newLine = TRUE; fgetpos (File.fp, &StartOfLine); } DebugStatement ( debugPutc (DEBUG_RAW, c); ) return c;}extern void fileUngetc (int c){ File.ungetch = c;}static vString *iFileGetLine (void){ vString *result = NULL; int c; if (File.line == NULL) File.line = vStringNew (); vStringClear (File.line); do { c = iFileGetc (); if (c != EOF) vStringPut (File.line, c); if (c == '\n' || (c == EOF && vStringLength (File.line) > 0)) { vStringTerminate (File.line);#ifdef HAVE_REGEX if (vStringLength (File.line) > 0) matchRegex (File.line, File.source.language);#endif result = File.line; break; } } while (c != EOF); Assert (result != NULL || File.eof); return result;}/* Do not mix use of fileReadLine () and fileGetc () for the same file. */extern int fileGetc (void){ int c; /* If there is an ungotten character, then return it. Don't do any * other processing on it, though, because we already did that the * first time it was read through fileGetc (). */ if (File.ungetch != '\0') { c = File.ungetch; File.ungetch = '\0'; return c; /* return here to avoid re-calling debugPutc () */ } do { if (File.currentLine != NULL) { c = *File.currentLine++; if (c == '\0') File.currentLine = NULL; } else { vString* const line = iFileGetLine (); if (line != NULL) File.currentLine = (unsigned char*) vStringValue (line); if (File.currentLine == NULL) c = EOF; else c = '\0'; } } while (c == '\0'); DebugStatement ( debugPutc (DEBUG_READ, c); ) return c;}/* An alternative interface to fileGetc (). Do not mix use of fileReadLine() * and fileGetc() for the same file. The returned string does not contain * the terminating newline. A NULL return value means that all lines in the * file have been read and we are at the end of file. */extern const unsigned char *fileReadLine (void){ vString* const line = iFileGetLine (); const unsigned char* result = NULL; if (line != NULL) { result = (const unsigned char*) vStringValue (line); vStringStripNewline (line); DebugStatement ( debugPrintf (DEBUG_READ, "%s\n", result); ) } return result;}/* * Source file line reading with automatic buffer sizing */extern char *readLine (vString *const vLine, FILE *const fp){ char *result = NULL; vStringClear (vLine); if (fp == NULL) /* to free memory allocated to buffer */ error (FATAL, "NULL file pointer"); else { boolean reReadLine; /* If reading the line places any character other than a null or a * newline at the last character position in the buffer (one less * than the buffer size), then we must resize the buffer and * reattempt to read the line. */ do { char *const pLastChar = vStringValue (vLine) + vStringSize (vLine) -2; fpos_t startOfLine; fgetpos (fp, &startOfLine); reReadLine = FALSE; *pLastChar = '\0'; result = fgets (vStringValue (vLine), (int) vStringSize (vLine), fp); if (result == NULL) { if (! feof (fp)) error (FATAL | PERROR, "Failure on attempt to read file"); } else if (*pLastChar != '\0' && *pLastChar != '\n' && *pLastChar != '\r') { /* buffer overflow */ reReadLine = vStringAutoResize (vLine); if (reReadLine) fsetpos (fp, &startOfLine); else error (FATAL | PERROR, "input line too big; out of memory"); } else { char* eol; vStringSetLength (vLine); /* canonicalize new line */ eol = vStringValue (vLine) + vStringLength (vLine) - 1; if (*eol == '\r') *eol = '\n'; else if (*(eol - 1) == '\r' && *eol == '\n') { *(eol - 1) = '\n'; *eol = '\0'; --vLine->length; } } } while (reReadLine); } return result;}/* Places into the line buffer the contents of the line referenced by * "location". */extern char *readSourceLine (vString *const vLine, fpos_t location, long *const pSeekValue){ fpos_t orignalPosition; char *result; fgetpos (File.fp, &orignalPosition); fsetpos (File.fp, &location); if (pSeekValue != NULL) *pSeekValue = ftell (File.fp); result = readLine (vLine, File.fp); if (result == NULL) error (FATAL, "Unexpected end of file: %s", vStringValue (File.name)); fsetpos (File.fp, &orignalPosition); return result;}/* vi:set tabstop=8 shiftwidth=4: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -