New make targets: exe, build-arch, man and build-indep.
[tfortune.git] / util.c
1 /* SPDX-License-Identifier: GPL-3.0-only */
2
3 #include "tf.h"
4
5 DEFINE_TF_ERRLIST;
6
7 int atoi64(const char *str, int64_t *value)
8 {
9         char *endptr;
10         long long tmp;
11
12         errno = 0; /* To distinguish success/failure after call */
13         tmp = strtoll(str, &endptr, 10);
14         if (errno == ERANGE && (tmp == LLONG_MAX || tmp == LLONG_MIN))
15                 return -E_ATOI_OVERFLOW;
16         /*
17          * If there were no digits at all, strtoll() stores the original value
18          * of str in *endptr.
19          */
20         if (endptr == str)
21                 return -E_ATOI_NO_DIGITS;
22         /*
23          * The implementation may also set errno and return 0 in case no
24          * conversion was performed.
25          */
26         if (errno != 0 && tmp == 0)
27                 return -E_ATOI_NO_DIGITS;
28         if (*endptr != '\0') /* Further characters after number */
29                 return -E_ATOI_JUNK_AT_END;
30         *value = tmp;
31         return 1;
32 }
33
34 __attribute__ ((warn_unused_result))
35 void *xrealloc(void *p, size_t size)
36 {
37         /*
38          * No need to check for NULL pointers: If p is NULL, the call
39          * to realloc is equivalent to malloc(size)
40          */
41         assert(size);
42         if (!(p = realloc(p, size))) {
43                 EMERG_LOG("realloc failed (size = %zu), aborting\n", size);
44                 exit(EXIT_FAILURE);
45         }
46         return p;
47 }
48
49 __attribute__ ((warn_unused_result))
50 void *xmalloc(size_t size)
51 {
52         return xrealloc(NULL, size);
53 }
54
55 __attribute__ ((warn_unused_result))
56 void *xcalloc(size_t size)
57 {
58         void *p = xmalloc(size);
59         memset(p, 0, size);
60         return p;
61 }
62
63 __attribute__ ((warn_unused_result))
64 char *xstrdup(const char *str)
65 {
66         char *p = strdup(str);
67
68         if (p)
69                 return p;
70         EMERG_LOG("strdup failed, aborting\n");
71         exit(EXIT_FAILURE);
72 }
73
74 /*
75  * Get the home directory of the current user. Returns a dynamically allocated
76  * string that must be freed by the caller.
77  */
78 __attribute__ ((warn_unused_result))
79 char *get_homedir(void)
80 {
81         struct passwd *pw = getpwuid(getuid());
82
83         if (!pw) {
84                 EMERG_LOG("could not get home directory: %s\n",
85                         strerror(errno));
86                 exit(EXIT_FAILURE);
87         }
88         return xstrdup(pw->pw_dir);
89 }
90
91 /*
92  * Print a formated message to a dynamically allocated string.
93  *
94  * This function is similar to vasprintf(), a GNU extension which is not in C
95  * or POSIX. It allocates a string large enough to hold the output including
96  * the terminating null byte. The allocated string is returned via the first
97  * argument and must be freed by the caller. However, unlike vasprintf(), this
98  * function calls exit() if insufficient memory is available, while vasprintf()
99  * returns -1 in this case.
100  *
101  * It returns the number of bytes written, not including the terminating \p
102  * NULL character.
103  */
104 __attribute__ ((format (printf, 2, 0)))
105 unsigned xvasprintf(char **result, const char *fmt, va_list ap)
106 {
107         int ret;
108         size_t size = 150;
109         va_list aq;
110
111         *result = xmalloc(size + 1);
112         va_copy(aq, ap);
113         ret = vsnprintf(*result, size, fmt, aq);
114         va_end(aq);
115         assert(ret >= 0);
116         if ((size_t)ret < size)
117                 return ret;
118         size = ret + 1;
119         *result = xrealloc(*result, size);
120         va_copy(aq, ap);
121         ret = vsnprintf(*result, size, fmt, aq);
122         va_end(aq);
123         assert(ret >= 0 && (size_t)ret < size);
124         return ret;
125 }
126
127 __attribute__ ((format (printf, 2, 3)))
128 /* Print to a dynamically allocated string, variable number of arguments.  */
129 unsigned xasprintf(char **result, const char *fmt, ...)
130 {
131         va_list ap;
132         unsigned ret;
133
134         va_start(ap, fmt);
135         ret = xvasprintf(result, fmt, ap);
136         va_end(ap);
137         return ret;
138 }
139
140 /*
141  * Compile a regular expression.
142  *
143  * This simple wrapper calls regcomp(3) and logs a message on errors.
144  */
145 int xregcomp(regex_t *preg, const char *regex, int cflags)
146 {
147         char *buf;
148         size_t size;
149         int ret = regcomp(preg, regex, cflags);
150
151         if (ret == 0)
152                 return 1;
153         size = regerror(ret, preg, NULL, 0);
154         buf = xmalloc(size);
155         regerror(ret, preg, buf, size);
156         ERROR_LOG("%s\n", buf);
157         free(buf);
158         return -E_REGEX;
159 }
160
161 /* Write input from fd to dynamically allocated buffer. */
162 int fd2buf(int fd, struct iovec *result)
163 {
164         const size_t max_size = 32 * 1024;
165         size_t loaded = 0;
166         int ret;
167         struct iovec iov;
168
169         iov.iov_len = 100; /* guess that's sufficient */
170         iov.iov_base = xmalloc(iov.iov_len);
171         for (;;) {
172                 ret = read(fd, iov.iov_base + loaded,  iov.iov_len - loaded);
173                 if (ret < 0)
174                         ret = -ERRNO_TO_TF_ERROR(errno);
175                 if (ret <= 0)
176                         break;
177                 loaded += ret;
178                 if (loaded >= iov.iov_len) {
179                         iov.iov_len *= 2;
180                         ret = -ERRNO_TO_TF_ERROR(EOVERFLOW);
181                         if (iov.iov_len >= max_size)
182                                 break;
183                         iov.iov_base = xrealloc(iov.iov_base, iov.iov_len);
184                 }
185         };
186         if (ret < 0) {
187                 free(iov.iov_base);
188                 result->iov_base = NULL;
189                 result->iov_len = 0;
190                 return ret;
191         }
192         iov.iov_len = loaded;
193         iov.iov_base = xrealloc(iov.iov_base, loaded + 1);
194         *((char *)iov.iov_base + loaded) = '\0';
195         *result = iov;
196         return 1;
197 }
198
199 __attribute__ ((format (printf, 2, 3)))
200 void tf_log(int ll, const char* fmt,...)
201 {
202         va_list argp;
203         if (ll < loglevel_arg_val)
204                 return;
205         va_start(argp, fmt);
206         vfprintf(stderr, fmt, argp);
207         va_end(argp);
208 }
209
210 struct regfile_iter {
211         DIR *dir;
212         int dfd;
213         struct dirent *entry;
214         struct stat stat;
215 };
216
217 void regfile_iter_next(struct regfile_iter *iter)
218 {
219         for (;;) {
220                 iter->entry = readdir(iter->dir);
221                 if (!iter->entry)
222                         return;
223                 if (!strcmp(iter->entry->d_name, "."))
224                         continue;
225                 if (!strcmp(iter->entry->d_name, ".."))
226                         continue;
227                 if (fstatat(iter->dfd, iter->entry->d_name, &iter->stat, 0) == -1)
228                         continue;
229                 if (!S_ISREG(iter->stat.st_mode))
230                         continue;
231                 if (iter->stat.st_size == 0) /* skip over empty files */
232                         continue;
233                 return;
234         }
235 }
236
237 void regfile_iter_new(const char *dirname, struct regfile_iter **result)
238 {
239         struct regfile_iter *iter = xmalloc(sizeof(*iter));
240
241         iter->dir = opendir(dirname);
242         if (!iter->dir) {
243                 EMERG_LOG("opendir %s: %s\n", dirname, strerror(errno));
244                 exit(EXIT_FAILURE);
245         }
246         iter->dfd = dirfd(iter->dir);
247         assert(iter->dfd >= 0);
248         regfile_iter_next(iter);
249         *result = iter;
250 }
251
252 static void *xmmap(size_t sz, int fd, const char *path)
253 {
254         void *result = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, fd, 0);
255
256         if (result == MAP_FAILED) {
257                 EMERG_LOG("mmap %s: %s\n", path, strerror(errno));
258                 exit(EXIT_FAILURE);
259         }
260         return result;
261 }
262
263 void mmap_file(const char *path, struct iovec *iov)
264 {
265         int fd, ret;
266
267         ret = open(path, 0);
268         if (ret < 0) {
269                 EMERG_LOG("could not open %s: %s\n", path, strerror(errno));
270                 exit(EXIT_FAILURE);
271         }
272         fd = ret;
273         iov->iov_len = lseek(fd, 0, SEEK_END);
274         if (iov->iov_len == (size_t)(off_t)-1) {
275                 EMERG_LOG("lseek %s: %s\n", path, strerror(errno));
276                 exit(EXIT_FAILURE);
277         }
278         iov->iov_base = xmmap(iov->iov_len, fd, path);
279         close(fd);
280 }
281
282 bool regfile_iter_map(const struct regfile_iter *iter, struct iovec *result)
283 {
284         int ret, fd;
285         void *map;
286         const char *path;
287
288         if (!iter->entry)
289                 return false;
290         path = iter->entry->d_name;
291         ret = openat(iter->dfd, path, O_RDONLY, 0);
292         if (ret < 0) {
293                 EMERG_LOG("could not open %s: %s\n", path, strerror(errno));
294                 exit(EXIT_FAILURE);
295         }
296         fd = ret;
297         map = xmmap(iter->stat.st_size, fd, path);
298         close(fd);
299         result->iov_len = iter->stat.st_size;
300         result->iov_base = map;
301         return true;
302 }
303
304 const char *regfile_iter_basename(const struct regfile_iter *iter)
305 {
306         if (!iter->entry)
307                 return NULL;
308         return iter->entry->d_name;
309 }
310
311 const struct stat *regfile_iter_stat(const struct regfile_iter *iter)
312 {
313         return &iter->stat;
314 }
315
316 void regfile_iter_free(struct regfile_iter *iter)
317 {
318         closedir(iter->dir);
319         free(iter);
320 }