1 /* SPDX-License-Identifier: GPL-2.0 */ 2 /* 3 * Lightweight buffered reading library. 4 * 5 * Copyright 2019 Google LLC. 6 */ 7 #ifndef __API_IO__ 8 #define __API_IO__ 9 10 #include <errno.h> 11 #include <poll.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <unistd.h> 15 16 struct io { 17 /* File descriptor being read/ */ 18 int fd; 19 /* Size of the read buffer. */ 20 unsigned int buf_len; 21 /* Pointer to storage for buffering read. */ 22 char *buf; 23 /* End of the storage. */ 24 char *end; 25 /* Currently accessed data pointer. */ 26 char *data; 27 /* Read timeout, 0 implies no timeout. */ 28 int timeout_ms; 29 /* Set true on when the end of file on read error. */ 30 bool eof; 31 }; 32 33 static inline void io__init(struct io *io, int fd, 34 char *buf, unsigned int buf_len) 35 { 36 io->fd = fd; 37 io->buf_len = buf_len; 38 io->buf = buf; 39 io->end = buf; 40 io->data = buf; 41 io->timeout_ms = 0; 42 io->eof = false; 43 } 44 45 /* Reads one character from the "io" file with similar semantics to fgetc. */ 46 static inline int io__get_char(struct io *io) 47 { 48 char *ptr = io->data; 49 50 if (io->eof) 51 return -1; 52 53 if (ptr == io->end) { 54 ssize_t n; 55 56 if (io->timeout_ms != 0) { 57 struct pollfd pfds[] = { 58 { 59 .fd = io->fd, 60 .events = POLLIN, 61 }, 62 }; 63 64 n = poll(pfds, 1, io->timeout_ms); 65 if (n == 0) 66 errno = ETIMEDOUT; 67 if (n > 0 && !(pfds[0].revents & POLLIN)) { 68 errno = EIO; 69 n = -1; 70 } 71 if (n <= 0) { 72 io->eof = true; 73 return -1; 74 } 75 } 76 n = read(io->fd, io->buf, io->buf_len); 77 78 if (n <= 0) { 79 io->eof = true; 80 return -1; 81 } 82 ptr = &io->buf[0]; 83 io->end = &io->buf[n]; 84 } 85 io->data = ptr + 1; 86 return *ptr; 87 } 88 89 /* Read a hexadecimal value with no 0x prefix into the out argument hex. If the 90 * first character isn't hexadecimal returns -2, io->eof returns -1, otherwise 91 * returns the character after the hexadecimal value which may be -1 for eof. 92 * If the read value is larger than a u64 the high-order bits will be dropped. 93 */ 94 static inline int io__get_hex(struct io *io, __u64 *hex) 95 { 96 bool first_read = true; 97 98 *hex = 0; 99 while (true) { 100 int ch = io__get_char(io); 101 102 if (ch < 0) 103 return ch; 104 if (ch >= '0' && ch <= '9') 105 *hex = (*hex << 4) | (ch - '0'); 106 else if (ch >= 'a' && ch <= 'f') 107 *hex = (*hex << 4) | (ch - 'a' + 10); 108 else if (ch >= 'A' && ch <= 'F') 109 *hex = (*hex << 4) | (ch - 'A' + 10); 110 else if (first_read) 111 return -2; 112 else 113 return ch; 114 first_read = false; 115 } 116 } 117 118 /* Read a positive decimal value with out argument dec. If the first character 119 * isn't a decimal returns -2, io->eof returns -1, otherwise returns the 120 * character after the decimal value which may be -1 for eof. If the read value 121 * is larger than a u64 the high-order bits will be dropped. 122 */ 123 static inline int io__get_dec(struct io *io, __u64 *dec) 124 { 125 bool first_read = true; 126 127 *dec = 0; 128 while (true) { 129 int ch = io__get_char(io); 130 131 if (ch < 0) 132 return ch; 133 if (ch >= '0' && ch <= '9') 134 *dec = (*dec * 10) + ch - '0'; 135 else if (first_read) 136 return -2; 137 else 138 return ch; 139 first_read = false; 140 } 141 } 142 143 /* Read up to and including the first newline following the pattern of getline. */ 144 static inline ssize_t io__getline(struct io *io, char **line_out, size_t *line_len_out) 145 { 146 char buf[128]; 147 int buf_pos = 0; 148 char *line = NULL, *temp; 149 size_t line_len = 0; 150 int ch = 0; 151 152 /* TODO: reuse previously allocated memory. */ 153 free(*line_out); 154 while (ch != '\n') { 155 ch = io__get_char(io); 156 157 if (ch < 0) 158 break; 159 160 if (buf_pos == sizeof(buf)) { 161 temp = realloc(line, line_len + sizeof(buf)); 162 if (!temp) 163 goto err_out; 164 line = temp; 165 memcpy(&line[line_len], buf, sizeof(buf)); 166 line_len += sizeof(buf); 167 buf_pos = 0; 168 } 169 buf[buf_pos++] = (char)ch; 170 } 171 temp = realloc(line, line_len + buf_pos + 1); 172 if (!temp) 173 goto err_out; 174 line = temp; 175 memcpy(&line[line_len], buf, buf_pos); 176 line[line_len + buf_pos] = '\0'; 177 line_len += buf_pos; 178 *line_out = line; 179 *line_len_out = line_len; 180 return line_len; 181 err_out: 182 free(line); 183 *line_out = NULL; 184 return -ENOMEM; 185 } 186 187 #endif /* __API_IO__ */ 188