util/os_file: always use the 'grow' mechanism
[mesa.git] / src / util / os_file.c
1 /*
2 * Copyright 2019 Intel Corporation
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include "os_file.h"
7
8 #include <errno.h>
9 #include <stdlib.h>
10
11 #if defined(__linux__)
12
13 #include <fcntl.h>
14 #include <sys/stat.h>
15 #include <unistd.h>
16
17
18 static ssize_t
19 readN(int fd, char *buf, size_t len)
20 {
21 int err = -ENODATA;
22 size_t total = 0;
23 do {
24 ssize_t ret = read(fd, buf + total, len - total);
25
26 if (ret < 0)
27 ret = -errno;
28
29 if (ret == -EINTR || ret == -EAGAIN)
30 continue;
31
32 if (ret <= 0)
33 break;
34
35 total += ret;
36 } while (total != len);
37
38 return total ? total : err;
39 }
40
41 char *
42 os_read_file(const char *filename)
43 {
44 /* Note that this also serves as a slight margin to avoid a 2x grow when
45 * the file is just a few bytes larger when we read it than when we
46 * fstat'ed it.
47 * The string's NULL terminator is also included in here.
48 */
49 size_t len = 64;
50
51 int fd = open(filename, O_RDONLY);
52 if (fd == -1) {
53 /* errno set by open() */
54 return NULL;
55 }
56
57 /* Pre-allocate a buffer at least the size of the file if we can read
58 * that information.
59 */
60 struct stat stat;
61 if (fstat(fd, &stat) == 0)
62 len += stat.st_size;
63
64 char *buf = malloc(len);
65 if (!buf) {
66 close(fd);
67 errno = -ENOMEM;
68 return NULL;
69 }
70
71 ssize_t read;
72 size_t offset = 0, remaining = len - 1;
73 while ((read = readN(fd, buf + offset, remaining)) == remaining) {
74 char *newbuf = realloc(buf, 2 * len);
75 if (!newbuf) {
76 free(buf);
77 close(fd);
78 errno = -ENOMEM;
79 return NULL;
80 }
81
82 buf = newbuf;
83 len *= 2;
84 offset += read;
85 remaining = len - offset - 1;
86 }
87
88 close(fd);
89
90 if (read > 0)
91 offset += read;
92
93 buf[offset] = '\0';
94
95 return buf;
96 }
97
98 #else
99
100 char *
101 os_read_file(const char *filename)
102 {
103 errno = -ENOSYS;
104 return NULL;
105 }
106
107 #endif