// SPDX-License-Identifier: AGPL-3.0-or-later /* PASST - Plug A Simple Socket Transport * for qemu/UNIX domain socket mode * * PASTA - Pack A Subtle Tap Abstraction * for network namespace/tap device mode * * lineread.c - Allocation free line-by-line buffered file input * * Copyright Red Hat * Author: David Gibson */ #include #include #include #include #include #include #include "lineread.h" /** * lineread_init() - Prepare for line by line file reading without allocation * @lr: Line reader state structure to initialize * @fd: File descriptor to read lines from */ void lineread_init(struct lineread *lr, int fd) { lr->fd = fd; lr->next_line = lr->count = 0; } /** * peek_line() - Find and NULL-terminate next line in buffer * @lr: Line reader state structure * @eof: Caller indicates end-of-file was already found by read() * * Return: length of line in bytes, -1 if no line was found */ static int peek_line(struct lineread *lr, bool eof) { char *nl; /* Sanity checks (which also document invariants) */ assert(lr->count >= 0); assert(lr->next_line >= 0); assert(lr->next_line + lr->count >= lr->next_line); assert(lr->next_line + lr->count <= LINEREAD_BUFFER_SIZE); nl = memchr(lr->buf + lr->next_line, '\n', lr->count); if (nl) { *nl = '\0'; return nl - lr->buf - lr->next_line + 1; } if (eof) { lr->buf[lr->next_line + lr->count] = '\0'; /* No trailing newline, so treat all remaining bytes * as the last line */ return lr->count; } return -1; } /** * lineread_get() - Read a single line from file (no allocation) * @lr: Line reader state structure * @line: Place a pointer to the next line in this variable * * Return: Length of line read on success, 0 on EOF, negative on error */ int lineread_get(struct lineread *lr, char **line) { bool eof = false; int line_len; while ((line_len = peek_line(lr, eof)) < 0) { int rc; if ((lr->next_line + lr->count) == LINEREAD_BUFFER_SIZE) { /* No space at end */ if (lr->next_line == 0) { /* Buffer is full, which means we've * hit a line too long for us to * process. FIXME: report error * better */ return -1; } memmove(lr->buf, lr->buf + lr->next_line, lr->count); lr->next_line = 0; } /* Read more data into the end of buffer */ rc = read(lr->fd, lr->buf + lr->next_line + lr->count, LINEREAD_BUFFER_SIZE - lr->next_line - lr->count); if (rc < 0) { return rc; } else if (rc == 0) { eof = true; } else { lr->count += rc; } } *line = lr->buf + lr->next_line; lr->next_line += line_len; lr->count -= line_len; return line_len; }