162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Lightweight buffered reading library. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2019 Google LLC. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#ifndef __API_IO__ 862306a36Sopenharmony_ci#define __API_IO__ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <errno.h> 1162306a36Sopenharmony_ci#include <poll.h> 1262306a36Sopenharmony_ci#include <stdlib.h> 1362306a36Sopenharmony_ci#include <string.h> 1462306a36Sopenharmony_ci#include <unistd.h> 1562306a36Sopenharmony_ci#include <linux/types.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct io { 1862306a36Sopenharmony_ci /* File descriptor being read/ */ 1962306a36Sopenharmony_ci int fd; 2062306a36Sopenharmony_ci /* Size of the read buffer. */ 2162306a36Sopenharmony_ci unsigned int buf_len; 2262306a36Sopenharmony_ci /* Pointer to storage for buffering read. */ 2362306a36Sopenharmony_ci char *buf; 2462306a36Sopenharmony_ci /* End of the storage. */ 2562306a36Sopenharmony_ci char *end; 2662306a36Sopenharmony_ci /* Currently accessed data pointer. */ 2762306a36Sopenharmony_ci char *data; 2862306a36Sopenharmony_ci /* Read timeout, 0 implies no timeout. */ 2962306a36Sopenharmony_ci int timeout_ms; 3062306a36Sopenharmony_ci /* Set true on when the end of file on read error. */ 3162306a36Sopenharmony_ci bool eof; 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic inline void io__init(struct io *io, int fd, 3562306a36Sopenharmony_ci char *buf, unsigned int buf_len) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci io->fd = fd; 3862306a36Sopenharmony_ci io->buf_len = buf_len; 3962306a36Sopenharmony_ci io->buf = buf; 4062306a36Sopenharmony_ci io->end = buf; 4162306a36Sopenharmony_ci io->data = buf; 4262306a36Sopenharmony_ci io->timeout_ms = 0; 4362306a36Sopenharmony_ci io->eof = false; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Reads one character from the "io" file with similar semantics to fgetc. */ 4762306a36Sopenharmony_cistatic inline int io__get_char(struct io *io) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci char *ptr = io->data; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (io->eof) 5262306a36Sopenharmony_ci return -1; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (ptr == io->end) { 5562306a36Sopenharmony_ci ssize_t n; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (io->timeout_ms != 0) { 5862306a36Sopenharmony_ci struct pollfd pfds[] = { 5962306a36Sopenharmony_ci { 6062306a36Sopenharmony_ci .fd = io->fd, 6162306a36Sopenharmony_ci .events = POLLIN, 6262306a36Sopenharmony_ci }, 6362306a36Sopenharmony_ci }; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci n = poll(pfds, 1, io->timeout_ms); 6662306a36Sopenharmony_ci if (n == 0) 6762306a36Sopenharmony_ci errno = ETIMEDOUT; 6862306a36Sopenharmony_ci if (n > 0 && !(pfds[0].revents & POLLIN)) { 6962306a36Sopenharmony_ci errno = EIO; 7062306a36Sopenharmony_ci n = -1; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci if (n <= 0) { 7362306a36Sopenharmony_ci io->eof = true; 7462306a36Sopenharmony_ci return -1; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci n = read(io->fd, io->buf, io->buf_len); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (n <= 0) { 8062306a36Sopenharmony_ci io->eof = true; 8162306a36Sopenharmony_ci return -1; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci ptr = &io->buf[0]; 8462306a36Sopenharmony_ci io->end = &io->buf[n]; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci io->data = ptr + 1; 8762306a36Sopenharmony_ci return *ptr; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* Read a hexadecimal value with no 0x prefix into the out argument hex. If the 9162306a36Sopenharmony_ci * first character isn't hexadecimal returns -2, io->eof returns -1, otherwise 9262306a36Sopenharmony_ci * returns the character after the hexadecimal value which may be -1 for eof. 9362306a36Sopenharmony_ci * If the read value is larger than a u64 the high-order bits will be dropped. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_cistatic inline int io__get_hex(struct io *io, __u64 *hex) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci bool first_read = true; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci *hex = 0; 10062306a36Sopenharmony_ci while (true) { 10162306a36Sopenharmony_ci int ch = io__get_char(io); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (ch < 0) 10462306a36Sopenharmony_ci return ch; 10562306a36Sopenharmony_ci if (ch >= '0' && ch <= '9') 10662306a36Sopenharmony_ci *hex = (*hex << 4) | (ch - '0'); 10762306a36Sopenharmony_ci else if (ch >= 'a' && ch <= 'f') 10862306a36Sopenharmony_ci *hex = (*hex << 4) | (ch - 'a' + 10); 10962306a36Sopenharmony_ci else if (ch >= 'A' && ch <= 'F') 11062306a36Sopenharmony_ci *hex = (*hex << 4) | (ch - 'A' + 10); 11162306a36Sopenharmony_ci else if (first_read) 11262306a36Sopenharmony_ci return -2; 11362306a36Sopenharmony_ci else 11462306a36Sopenharmony_ci return ch; 11562306a36Sopenharmony_ci first_read = false; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/* Read a positive decimal value with out argument dec. If the first character 12062306a36Sopenharmony_ci * isn't a decimal returns -2, io->eof returns -1, otherwise returns the 12162306a36Sopenharmony_ci * character after the decimal value which may be -1 for eof. If the read value 12262306a36Sopenharmony_ci * is larger than a u64 the high-order bits will be dropped. 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_cistatic inline int io__get_dec(struct io *io, __u64 *dec) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci bool first_read = true; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci *dec = 0; 12962306a36Sopenharmony_ci while (true) { 13062306a36Sopenharmony_ci int ch = io__get_char(io); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (ch < 0) 13362306a36Sopenharmony_ci return ch; 13462306a36Sopenharmony_ci if (ch >= '0' && ch <= '9') 13562306a36Sopenharmony_ci *dec = (*dec * 10) + ch - '0'; 13662306a36Sopenharmony_ci else if (first_read) 13762306a36Sopenharmony_ci return -2; 13862306a36Sopenharmony_ci else 13962306a36Sopenharmony_ci return ch; 14062306a36Sopenharmony_ci first_read = false; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* Read up to and including the first newline following the pattern of getline. */ 14562306a36Sopenharmony_cistatic inline ssize_t io__getline(struct io *io, char **line_out, size_t *line_len_out) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci char buf[128]; 14862306a36Sopenharmony_ci int buf_pos = 0; 14962306a36Sopenharmony_ci char *line = NULL, *temp; 15062306a36Sopenharmony_ci size_t line_len = 0; 15162306a36Sopenharmony_ci int ch = 0; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* TODO: reuse previously allocated memory. */ 15462306a36Sopenharmony_ci free(*line_out); 15562306a36Sopenharmony_ci while (ch != '\n') { 15662306a36Sopenharmony_ci ch = io__get_char(io); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (ch < 0) 15962306a36Sopenharmony_ci break; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (buf_pos == sizeof(buf)) { 16262306a36Sopenharmony_ci temp = realloc(line, line_len + sizeof(buf)); 16362306a36Sopenharmony_ci if (!temp) 16462306a36Sopenharmony_ci goto err_out; 16562306a36Sopenharmony_ci line = temp; 16662306a36Sopenharmony_ci memcpy(&line[line_len], buf, sizeof(buf)); 16762306a36Sopenharmony_ci line_len += sizeof(buf); 16862306a36Sopenharmony_ci buf_pos = 0; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci buf[buf_pos++] = (char)ch; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci temp = realloc(line, line_len + buf_pos + 1); 17362306a36Sopenharmony_ci if (!temp) 17462306a36Sopenharmony_ci goto err_out; 17562306a36Sopenharmony_ci line = temp; 17662306a36Sopenharmony_ci memcpy(&line[line_len], buf, buf_pos); 17762306a36Sopenharmony_ci line[line_len + buf_pos] = '\0'; 17862306a36Sopenharmony_ci line_len += buf_pos; 17962306a36Sopenharmony_ci *line_out = line; 18062306a36Sopenharmony_ci *line_len_out = line_len; 18162306a36Sopenharmony_ci return line_len; 18262306a36Sopenharmony_cierr_out: 18362306a36Sopenharmony_ci free(line); 18462306a36Sopenharmony_ci return -ENOMEM; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci#endif /* __API_IO__ */ 188