aboutsummaryrefslogtreecommitdiff
path: root/src/linux/multiline.c
blob: 9a07b6e3025a809febeff5cfae7f948fe561b283 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <alloca.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#if CPUINFO_MOCK
	#include <cpuinfo-mock.h>
#endif
#include <linux/api.h>
#include <log.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;
}