www.pudn.com > vim53src.zip > read.c
/***************************************************************************** * $Id: read.c,v 6.6 1998/08/01 05:16:20 darren Exp $ * * Copyright (c) 1996-1998, 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 (line * splicing and newline conversion for source files are performed at this * level). *****************************************************************************/ /*============================================================================ = Include files ============================================================================*/ #ifdef HAVE_CONFIG_H # include#endif #include /* to define INT_MAX or MAXINT */ #include "ctags.h" /*============================================================================ = Data declarations ============================================================================*/ typedef struct _langTab { const char *extension; langType language; } langTab; /*============================================================================ = Data definitions ============================================================================*/ sourceFile File = { NULL, NULL, 0L, -1L, '\0', FALSE, FALSE, LANG_IGNORE }; /*============================================================================ = Function prototypes ============================================================================*/ static void fileNewline __ARGS((void)); static boolean resizeLineBuffer __ARGS((lineBuf *const pLine)); /*============================================================================ = Function definitions ============================================================================*/ /*---------------------------------------------------------------------------- * Generic line reading with automatic buffer sizing *--------------------------------------------------------------------------*/ extern void freeLineBuffer( pLine ) lineBuf *const pLine; { if (pLine->buffer != NULL) free(pLine->buffer); pLine->buffer = NULL; pLine->size = 0; } static boolean resizeLineBuffer( pLine ) lineBuf *const pLine; { boolean ok = FALSE; if (pLine->size <= INT_MAX / 2) { const int newSize = pLine->size * 2; char *const newBuffer = (char *)realloc(pLine->buffer,(size_t)newSize); if (newBuffer != NULL) { pLine->size = newSize; pLine->buffer = newBuffer; ok = TRUE; } } return ok; } /* Read a newline terminated line from 'fp' and if it overflows the buffer * specified by 'pLine', increase the buffer. The buffer never shrinks. The * size of the buffer will always be the longest line encountered so far. */ extern char *readLine( pLine, fp ) lineBuf *const pLine; FILE *const fp; { char *line = NULL; if (fp != NULL) /* to free memory allocated to buffer */ { boolean reReadLine; if (pLine->buffer == NULL) /* if buffer not yet allocated... */ { pLine->size = 32; pLine->buffer = (char *)malloc((size_t)pLine->size); } /* 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. */ if (pLine->buffer != NULL) do { const long startOfLine = ftell(fp); char *const pLastChar = pLine->buffer + pLine->size - 2; reReadLine = FALSE; *pLastChar = '\0'; line = fgets(pLine->buffer, pLine->size, fp); if (*pLastChar != '\0' && *pLastChar != '\n') /* overflow */ { if ((reReadLine = resizeLineBuffer(pLine))) fseek(fp, startOfLine, SEEK_SET); } } while (reReadLine); } return line; } /*---------------------------------------------------------------------------- * Source file access functions *--------------------------------------------------------------------------*/ /* 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( fileName, language, isHeader ) const char *const fileName; const langType language; const boolean isHeader; { 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, "rb"); /* must be binary mode for fseek() */ if (File.fp == NULL) error(WARNING | PERROR, "cannot open \"%s\"", fileName); else { opened = TRUE; File.name = fileName; File.lineNumber = 0L; File.seek = 0L; File.afterNL = TRUE; if (strlen(fileName) > TagFile.max.file) TagFile.max.file = strlen(fileName); /* Determine whether this is a header File. */ File.isHeader = isHeader; File.language = language; DebugStatement( debugOpen(fileName, File.isHeader, File.language); ) } return opened; } extern void fileClose() { 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) addTotals(0, File.lineNumber - 1L, getFileSize(File.name)); fclose(File.fp); File.fp = NULL; } } /* Action to take for each encountered source newline. */ static void fileNewline() { File.afterNL = FALSE; File.seek = ftell(File.fp); ++File.lineNumber; DebugStatement( if (Option.breakLine == File.lineNumber) lineBreak(); ) } /* This function reads a single character from the stream. */ extern int fileGetc() { boolean escaped = FALSE; 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. */ if (File.ungetch != '\0') { c = File.ungetch; File.ungetch = '\0'; return c; /* return here to avoid re-calling debugPutc() */ } nextChar: /* not structured, but faster for this critical path */ /* If previous character was a newline, then we're starting a line. */ if (File.afterNL) fileNewline(); c = getc(File.fp); switch (c) { default: if (escaped) { ungetc(c, File.fp); /* return character after BACKSLASH */ c = BACKSLASH; } break; case BACKSLASH: /* test for line splicing */ if (escaped) ungetc(c, File.fp); /* push back one just read */ else { escaped = TRUE; /* defer test until next character */ goto nextChar; } break; /* The following cases 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. */ case CRETURN: { const int next = getc(File.fp); /* is CR followed by LF? */ /* If this is a carriage-return/line-feed pair, treat it as one * newline, throwing away the line-feed. */ if (next != NEWLINE) ungetc(next, File.fp); } c = NEWLINE; /* convert CR into newline */ case NEWLINE: File.afterNL = TRUE; if (escaped) /* check for line splicing */ { DebugStatement( debugPutc(BACKSLASH, DEBUG_RAW); /* print the characters */ debugPutc(c, DEBUG_RAW); /* we're throwing away */ ) escaped = FALSE; /* BACKSLASH now fully processed */ goto nextChar; /* through away "\NEWLINE" */ } break; } DebugStatement( debugPutc(c, DEBUG_RAW); ) return c; } extern void fileUngetc( c ) int c; { File.ungetch = c; } /* Places into the line buffer the contents of the line referenced by * "location". */ extern char *getSourceLine( pLine, location ) lineBuf *const pLine; const long location; { const long orignalPosition = ftell(File.fp); char *line; fseek(File.fp, location, SEEK_SET); line = readLine(pLine, File.fp); fseek(File.fp, orignalPosition, SEEK_SET); return line; } /* vi:set tabstop=8 shiftwidth=4: */