/* readline.c -- Copyright 1993, 1994 Liam R. E. Quin. * All Rights Reserved. * This code is NOT in the public domain. * See the file COPYRIGHT for full details. */ /* $Id: freadln.c,v 1.6 2001/05/31 03:50:13 liam Exp $ */ #include "error.h" #include #include #include "globals.h" #include #ifdef HAVE_STDLIB_H # include #else # include #endif #include "emalloc.h" #include "lqutil.h" #ifndef LQT_READLINE_SLOP /* the buffer for lines can be this much larger than * the last actually read line. */ # define LQT_READLINE_SLOP 30 #endif static unsigned int LineLength = 0; static char *LQTpReadLineBuffer = 0; /* * LQU_fReadLine * Utilities/Files * *

Reads the next input line from the given file into a static buffer. * The buffer is allocated with malloc and resized dynamically, but * is owned by LQU_fReadLine and should not be free'd or overwritten.

*

The LQU_StealReadLineBuffer function can be used to obtain the * buffer; LQU_fReadLine will allocate a new one the next time it * is called.

*

The given Flags are treated as for * LQU_fReadFile, which currently calls this routine directly. * Note that, as for LQU_fReadFile, blank lines are skipped if the * corresponding flag is given. In this case, LQU_fReadLine will never * return a pointer to a blank line, but will continue reading lines * from the file until a non-blank one is found.

* * a pointer to the line, in Line, and also the number of bytes in * the line; -1 is returned on EOF, in which case the Line pointer should * not be used. *
*/ API int LQU_fReadLine(f, Linep, Flags) FILE *f; char **Linep; int Flags; { register char *p; static int LinesRead = 0; unsigned long TotalSoFar = 0L; int c; /* NOTE: we mantain a running average of linelengths, and use * this as a heuristic for the initial buffer size. At the end, * we realloc() if we over-estimated by more than one byte. */ TryAgain: *Linep = 0; p = (*Linep); while ((c = getc(f)) != EOF) { /* Look for things to skip before allocating any space: */ if (c == '\\' && (Flags & LQUF_ESCAPEOK)) { if ((c = getc(f)) == EOF) break; else if (c == '\n') continue; /* join lines together */ } else if (c == '#' && ((Flags & LQUF_IGNALLHASH) || ((Flags & LQUF_IGNHASH) && p == (*Linep)))) { while ((c = getc(f)) != EOF && c != '\n') { /*NULLBODY*/ ; } if (c == EOF) { break; } else { (void) ungetc(c, f); continue; } } else if ( p == (*Linep) && (Flags & LQUF_IGNSPACES) && (isascii(c) && isspace(c)) ) { continue; /* ignore leading blanks */ } /* Now ensure that we have allocated enpough space: */ if (!p) { /* the -1 is to leave room for a trailing \0 */ if (LineLength == 0) { LineLength = TotalSoFar / (LinesRead ? LinesRead : 1); if (LineLength < LQT_READLINE_SLOP) { LineLength = LQT_READLINE_SLOP; LinesRead = 0; } p = LQTpReadLineBuffer = *Linep = emalloc("fReadLine Buffer", LineLength + 1); } else { p = (*Linep) = LQTpReadLineBuffer; } } else if (p - (*Linep) >= LineLength - 1) { /* save our position... */ int WhereWeWere = p - (*Linep); /* increase linelength */ if (LineLength < 20) { LineLength += 10; } else { LineLength += (LineLength / 2); } *Linep = LQTpReadLineBuffer = erealloc(LQTpReadLineBuffer, LineLength + 1); if (!*Linep) return WhereWeWere + 1; p = (*Linep); p = &p[WhereWeWere]; } *p++ = c; *p = '\0'; /* p a r a n o i a */ if (c == '\n') { /* delete trailing newline if asked to do so */ if (Flags & LQUF_IGNNEWLINE) { *--p = '\0'; } break; } } if (!p) { if (c == EOF) { return -1; } else if (Flags & LQUF_IGNBLANKS) { *Linep = (char *) 0; goto TryAgain; } else { /* empty line */ *Linep = (char *) 0; return 0; } } *p = '\0'; /* Discard a trailing \r, as these are usually the result of * file transfers and are unwanted. (is this a bogus mis-feature? * I don't think so, but then, I wrote it... Another flag?) */ if ((Flags & (LQUF_IGNNEWLINE|LQUF_IGNSPACES)) && p > *Linep && p[-1] == '\r' ) { *--p = '\0'; } if (Flags & LQUF_IGNSPACES) { /* delete trailing spaces, being careful to keep "\ " if requested */ while ( p > *Linep && (isascii(p[-1]) && isspace(p[-1])) ) { if (&p[-1] > *Linep && p[-2] == '\\' && (Flags && LQUF_ESCAPEOK)) { break; /* This is the case where we must keep "\ " */ } *--p = '\0'; } } TotalSoFar += p - *Linep; ++LinesRead; /* Note: we include blank lines in the average */ if (p == *Linep) { if (c == EOF) { return -1; } else if (Flags & LQUF_IGNBLANKS) { *Linep = (char *) 0; /* p a r a n o i a */ goto TryAgain; } } /* ASSERT: p > *Linep */ if (p - *Linep < LineLength + 1 + LQT_READLINE_SLOP) { /* The +1 in the test above is one for a \0; LQT_READLINE_SLOP is to * reduce the number of relloc() calls, as realloc almost always * copies its argument on most systems, and is slow. */ LineLength = p - *Linep; /* ASSERT: LineLength != 0 */ *Linep = LQTpReadLineBuffer = erealloc(*Linep, LineLength + 1); if (LQTpReadLineBuffer == (char *) 0) { return -1; /* is this right?? no error? */ } p = (*Linep) + LineLength; } return p - *Linep; } /* * LQU_StealReadLineBuffer * Utilities/Files * *

Returns the internal line buffer used by LQU_fReadLine, and also * causes LQU_fReadLine to allocate a new buffer the next time it is * called. In this way, you can read lines with LQU_fReadLine, and * save any that you are interested in keeping by calling * LQU_StealReadLineBuffer, without having to copy the data.

*

The buffer returned may be longer than necessary to contain * the line that was last stored there by LQU_fReadLine by up to * LQT_READLINE_SLOP bytes; use erealloc to shrink it if desired. * The LQT_READLINE_SLOP constant is defined in freadln.c as 30 bytes.

* * a pointer to the buffer, or NULL if there isn't one yet. *
*/ API char * LQU_StealReadLineBuffer() { char *Result; Result = LQTpReadLineBuffer; LQTpReadLineBuffer = 0; LineLength = 0; return Result; }