• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <stdbool.h>
2 #include <stdint.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <alloca.h>
6 #include <errno.h>
7 
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11 #include <fcntl.h>
12 
13 #if CPUINFO_MOCK
14 	#include <cpuinfo-mock.h>
15 #endif
16 #include <linux/api.h>
17 #include <cpuinfo/log.h>
18 
19 
cpuinfo_linux_parse_multiline_file(const char * filename,size_t buffer_size,cpuinfo_line_callback callback,void * context)20 bool cpuinfo_linux_parse_multiline_file(const char* filename, size_t buffer_size, cpuinfo_line_callback callback, void* context)
21 {
22 	int file = -1;
23 	bool status = false;
24 	char* buffer = (char*) alloca(buffer_size);
25 
26 #if CPUINFO_MOCK
27 	file = cpuinfo_mock_open(filename, O_RDONLY);
28 #else
29 	file = open(filename, O_RDONLY);
30 #endif
31 	if (file == -1) {
32 		cpuinfo_log_info("failed to open %s: %s", filename, strerror(errno));
33 		goto cleanup;
34 	}
35 
36 	/* Only used for error reporting */
37 	size_t position = 0;
38 	uint64_t line_number = 1;
39 	const char* buffer_end = &buffer[buffer_size];
40 	char* data_start = buffer;
41 	ssize_t bytes_read;
42 	do {
43 #if CPUINFO_MOCK
44 		bytes_read = cpuinfo_mock_read(file, data_start, (size_t) (buffer_end - data_start));
45 #else
46 		bytes_read = read(file, data_start, (size_t) (buffer_end - data_start));
47 #endif
48 		if (bytes_read < 0) {
49 			cpuinfo_log_info("failed to read file %s at position %zu: %s",
50 				filename, position, strerror(errno));
51 			goto cleanup;
52 		}
53 
54 		position += (size_t) bytes_read;
55 		const char* data_end = data_start + (size_t) bytes_read;
56 		const char* line_start = buffer;
57 
58 		if (bytes_read == 0) {
59 			/* No more data in the file: process the remaining text in the buffer as a single entry */
60 			const char* line_end = data_end;
61 			if (!callback(line_start, line_end, context, line_number)) {
62 				goto cleanup;
63 			}
64 		} else {
65 			const char* line_end;
66 			do {
67 				/* Find the end of the entry, as indicated by newline character ('\n') */
68 				for (line_end = line_start; line_end != data_end; line_end++) {
69 					if (*line_end == '\n') {
70 						break;
71 					}
72 				}
73 
74 				/*
75 				 * If we located separator at the end of the entry, parse it.
76 				 * Otherwise, there may be more data at the end; read the file once again.
77 				 */
78 				if (line_end != data_end) {
79 					if (!callback(line_start, line_end, context, line_number++)) {
80 						goto cleanup;
81 					}
82 					line_start = line_end + 1;
83 				}
84 			} while (line_end != data_end);
85 
86 			/* Move remaining partial line data at the end to the beginning of the buffer */
87 			const size_t line_length = (size_t) (line_end - line_start);
88 			memmove(buffer, line_start, line_length);
89 			data_start = &buffer[line_length];
90 		}
91 	} while (bytes_read != 0);
92 
93 	/* Commit */
94 	status = true;
95 
96 cleanup:
97 	if (file != -1) {
98 #if CPUINFO_MOCK
99 		cpuinfo_mock_close(file);
100 #else
101 		close(file);
102 #endif
103 		file = -1;
104 	}
105 	return status;
106 }
107