| #include <alloca.h> | |
| #include <errno.h> | |
| #include <stdbool.h> | |
| #include <stdint.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <fcntl.h> | |
| #include <sys/stat.h> | |
| #include <sys/types.h> | |
| #include <unistd.h> | |
| #if CPUINFO_MOCK | |
| #include <cpuinfo-mock.h> | |
| #endif | |
| #include <cpuinfo/log.h> | |
| #include <linux/api.h> | |
| bool cpuinfo_linux_parse_multiline_file( | |
| const char* filename, | |
| size_t buffer_size, | |
| cpuinfo_line_callback callback, | |
| void* context) { | |
| int file = -1; | |
| bool status = false; | |
| char* buffer = (char*)alloca(buffer_size); | |
| #if CPUINFO_MOCK | |
| file = cpuinfo_mock_open(filename, O_RDONLY); | |
| #else | |
| file = open(filename, O_RDONLY); | |
| #endif | |
| if (file == -1) { | |
| cpuinfo_log_info("failed to open %s: %s", filename, strerror(errno)); | |
| goto cleanup; | |
| } | |
| /* Only used for error reporting */ | |
| size_t position = 0; | |
| uint64_t line_number = 1; | |
| const char* buffer_end = &buffer[buffer_size]; | |
| char* data_start = buffer; | |
| ssize_t bytes_read; | |
| do { | |
| #if CPUINFO_MOCK | |
| bytes_read = cpuinfo_mock_read(file, data_start, (size_t)(buffer_end - data_start)); | |
| #else | |
| bytes_read = read(file, data_start, (size_t)(buffer_end - data_start)); | |
| #endif | |
| if (bytes_read < 0) { | |
| cpuinfo_log_info( | |
| "failed to read file %s at position %zu: %s", filename, position, strerror(errno)); | |
| goto cleanup; | |
| } | |
| position += (size_t)bytes_read; | |
| const char* data_end = data_start + (size_t)bytes_read; | |
| const char* line_start = buffer; | |
| if (bytes_read == 0) { | |
| /* No more data in the file: process the remaining text | |
| * in the buffer as a single entry */ | |
| const char* line_end = data_end; | |
| if (!callback(line_start, line_end, context, line_number)) { | |
| goto cleanup; | |
| } | |
| } else { | |
| const char* line_end; | |
| do { | |
| /* Find the end of the entry, as indicated by | |
| * newline character ('\n') | |
| */ | |
| for (line_end = line_start; line_end != data_end; line_end++) { | |
| if (*line_end == '\n') { | |
| break; | |
| } | |
| } | |
| /* | |
| * If we located separator at the end of the | |
| * entry, parse it. Otherwise, there may be more | |
| * data at the end; read the file once again. | |
| */ | |
| if (line_end != data_end) { | |
| if (!callback(line_start, line_end, context, line_number++)) { | |
| goto cleanup; | |
| } | |
| line_start = line_end + 1; | |
| } | |
| } while (line_end != data_end); | |
| /* Move remaining partial line data at the end to the | |
| * beginning of the buffer */ | |
| const size_t line_length = (size_t)(line_end - line_start); | |
| memmove(buffer, line_start, line_length); | |
| data_start = &buffer[line_length]; | |
| } | |
| } while (bytes_read != 0); | |
| /* Commit */ | |
| status = true; | |
| cleanup: | |
| if (file != -1) { | |
| #if CPUINFO_MOCK | |
| cpuinfo_mock_close(file); | |
| #else | |
| close(file); | |
| #endif | |
| file = -1; | |
| } | |
| return status; | |
| } |