/* * copyright (c) 1997-2005 Motoyuki Kasahara * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #ifdef WIN32 #include #else #include #endif #include "linebuf.h" /* * Initialize `linebuffer'. */ void initialize_line_buffer(Line_Buffer *line_buffer) { line_buffer->buffer[0] = '\0'; line_buffer->file = -1; line_buffer->cache_length = 0; line_buffer->timeout = 0; } /* * Finalize `line_buffer'. */ void finalize_line_buffer(Line_Buffer *line_buffer) { line_buffer->buffer[0] = '\0'; line_buffer->file = -1; line_buffer->cache_length = 0; line_buffer->timeout = 0; } /* * Set timeout seconds. */ void set_line_buffer_timeout(Line_Buffer *line_buffer, int timeout) { line_buffer->timeout = timeout; } /* * Bind `file' to `line_buffer'. */ void bind_file_to_line_buffer(Line_Buffer *line_buffer, int file) { if (line_buffer->file < 0) initialize_line_buffer(line_buffer); line_buffer->file = file; } /* * Return file descriptor bound to `line_buffer'. * Return -1 when no file is bound. */ int file_bound_to_line_buffer(Line_Buffer *line_buffer) { return line_buffer->file; } /* * Discard cache data in `line_buffer'. */ void discard_cache_in_line_buffer(Line_Buffer *line_buffer) { line_buffer->cache_length = 0; } /* * Get the length of cache data in `line_buffer'. */ size_t cache_length_in_line_buffer(Line_Buffer *line_buffer) { return line_buffer->cache_length; } /* * Read a line from the file bound to `line_buffer', and copy the read * line to `line'. It reads at most `max_line_length' bytes. * * The function recognizes both "\n" and "\r\n" as end of line. * "\n" or "\r\n" is not copied to `buffer', but "\0" is added, instead. * * The function returns the number of characters in the line, upon * successful. It doesn't count "\n" or "\r\n" in the tail of the line, * so that 0 is returned for an empty line, and the line length doesn't * exceed one less than `max_line_length'. * * If EOF is received or an error occurs, -1 is returned. If the * line is too long, `max_line_length' is returned. */ ssize_t read_line_buffer(Line_Buffer *line_buffer, char *line, size_t max_line_length) { char *line_p; char *newline; size_t search_length; size_t additional_length; size_t line_length; fd_set fdset; struct timeval timeval; int select_result; ssize_t read_result; /* * Return -1 if no file is bound, or if `max_line_length' is 0. */ if (line_buffer->file < 0) return -1; if (max_line_length == 0) return -1; /* * Read a file until newline is appeared. */ line_length = 0; line_p = line; for (;;) { if (0 < line_buffer->cache_length) { /* * Find a newline in the cache data. */ if (max_line_length - line_length < line_buffer->cache_length) search_length = max_line_length - line_length; else search_length = line_buffer->cache_length; newline = (char *)memchr(line_buffer->buffer, '\n', search_length); /* * Append cache data in front of the newline to `line'. */ if (newline != NULL) additional_length = newline - line_buffer->buffer + 1; else additional_length = search_length; memcpy(line_p, line_buffer->buffer, additional_length); line_p += additional_length; line_length += additional_length; line_buffer->cache_length -= additional_length; /* * If cache data not copied to `line' are remained in the * buffer, we move them to the beginning of the buffer. */ memmove(line_buffer->buffer, line_buffer->buffer + additional_length, line_buffer->cache_length); if (newline != NULL) break; } /* * Check for the length of the current line. Return if the * line is too long. * * Note that the following conditional expression can be * substituted to (line_buffer->cache_length != 0), because * remained cache data mean that the line is too long. */ if (max_line_length <= line_length) return line_length; /* * Call select(). */ errno = 0; FD_ZERO(&fdset); FD_SET(line_buffer->file, &fdset); if (line_buffer->timeout == 0) { select_result = select(line_buffer->file + 1, &fdset, NULL, NULL, NULL); } else { timeval.tv_sec = line_buffer->timeout; timeval.tv_usec = 0; select_result = select(line_buffer->file + 1, &fdset, NULL, NULL, &timeval); } if (select_result < 0) { if (errno == EINTR) continue; return -1; } else if (select_result == 0) { return -1; } /* * Read from a file. (No cache data are remaind.) */ errno = 0; read_result = recv(line_buffer->file, line_buffer->buffer, LINEBUF_BUFFER_SIZE, 0); if (read_result < 0) { if (errno == EINTR) continue; return -1; } else if (read_result == 0) { if (line_length == 0) { return -1; } return line_length; } line_buffer->cache_length += read_result; } /* * Overwrite `\n' with `\0'. */ line_p--; *line_p = '\0'; line_length--; /* * If the line is end with `\r\n', remove not only `\n' but `\r'. */ if (0 < line_length && *(line_p - 1) == '\r') { line_p--; *line_p = '\0'; line_length--; } return line_length; } /* * Read just `stream_length' bytes from the file bound to `line_buffer', * and copy the read bytes to `stream'. * * Unlike read_line_buffer(), it doesn't append `\0' to the read data, * nor remove newline character in the read data. * * If it succeeds, the number of bytes actually read is returned. * If EOF is received or an error occurs, -1 is returned. */ ssize_t binary_read_line_buffer(Line_Buffer *line_buffer, char *stream, size_t stream_length) { char *stream_p; size_t done_length; fd_set fdset; struct timeval timeval; int select_result; ssize_t read_result; /* * Return -1 if no file is bound. */ if (line_buffer->file < 0) return -1; /* * Return 0 if `stream_length' is 0. */ if (stream_length == 0) return 0; /* * Test whether cache data are left in `line_buffer->buffer'. * If they are, copy them to `stream'. */ stream_p = stream; done_length = 0; if (0 < line_buffer->cache_length) { if (stream_length <= line_buffer->cache_length) done_length = stream_length; else done_length = line_buffer->cache_length; memcpy(stream_p, line_buffer->buffer, done_length); stream_p += done_length; line_buffer->cache_length -= done_length; memmove(line_buffer->buffer, line_buffer->buffer + done_length, line_buffer->cache_length); } /* * Read the file until the number of read bytes (`done_length') is * reached to `stream_length'. */ while (done_length < stream_length) { /* * Call select(). */ errno = 0; FD_ZERO(&fdset); FD_SET(line_buffer->file, &fdset); if (line_buffer->timeout == 0) { select_result = select(line_buffer->file + 1, NULL, &fdset, NULL, NULL); } else { timeval.tv_sec = line_buffer->timeout; timeval.tv_usec = 0; select_result = select(line_buffer->file + 1, NULL, &fdset, NULL, &timeval); } if (select_result < 0) { if (errno == EINTR) continue; return -1; } else if (select_result == 0) { return -1; } /* * Read from a file. */ errno = 0; read_result = recv(line_buffer->file, stream_p, stream_length - done_length, 0); if (read_result < 0) { if (errno == EINTR) continue; return read_result; } else if (read_result == 0) { if (done_length == 0) { return -1; } return done_length; } stream_p += read_result; done_length += read_result; } return stream_length; } /* * Skip too long line read by read_line_buffer(). * * If a line read by read_line_buffer() doesn't contain a newline * character, the line is too long. This function reads and discards * the rest of the line. * * If EOF is received or an error occurs, -1 is returned. * Otherwise, 0 is returned. */ int skip_line_buffer(Line_Buffer *line_buffer) { ssize_t line_length; /* * Read data until the end of the line is found. */ for (;;) { line_length = read_line_buffer(line_buffer, line_buffer->buffer, LINEBUF_BUFFER_SIZE); if (line_length < 0) return -1; if (line_length < LINEBUF_BUFFER_SIZE) break; } return 0; }