]>
Commit | Line | Data |
---|---|---|
8148217b | 1 | /* |
b295bdb1 BS |
2 | * Simple functions to access files. Paths can be globally prefixed to read |
3 | * data from an alternative source (e.g. a /proc dump for regression tests). | |
8148217b | 4 | * |
fcc4517c KZ |
5 | * The paths is possible to format by printf-like way for functions with "f" |
6 | * postfix in the name (e.g. readf, openf, ... ul_path_readf_u64()). | |
7 | * | |
8 | * The ul_path_read_* API is possible to use without path_cxt handler. In this | |
9 | * case is not possible to use global prefix and printf-like formatting. | |
10 | * | |
1ed21c80 KZ |
11 | * No copyright is claimed. This code is in the public domain; do with |
12 | * it what you wish. | |
fcc4517c | 13 | * |
1ed21c80 KZ |
14 | * Written by Karel Zak <kzak@redhat.com> [February 2018] |
15 | */ | |
8148217b HC |
16 | #include <stdarg.h> |
17 | #include <string.h> | |
18 | #include <unistd.h> | |
19 | #include <stdio.h> | |
37a5c7ee | 20 | #include <inttypes.h> |
8148217b HC |
21 | #include <errno.h> |
22 | ||
1ed21c80 KZ |
23 | #include "c.h" |
24 | #include "fileutils.h" | |
e12c9866 | 25 | #include "all-io.h" |
8148217b | 26 | #include "path.h" |
bcf445fd | 27 | #include "debug.h" |
e2569bcf | 28 | #include "strutils.h" |
bcf445fd KZ |
29 | |
30 | /* | |
31 | * Debug stuff (based on include/debug.h) | |
32 | */ | |
33 | static UL_DEBUG_DEFINE_MASK(ulpath); | |
34 | UL_DEBUG_DEFINE_MASKNAMES(ulpath) = UL_DEBUG_EMPTY_MASKNAMES; | |
35 | ||
36 | #define ULPATH_DEBUG_INIT (1 << 1) | |
37 | #define ULPATH_DEBUG_CXT (1 << 2) | |
38 | ||
39 | #define DBG(m, x) __UL_DBG(ulpath, ULPATH_DEBUG_, m, x) | |
40 | #define ON_DBG(m, x) __UL_DBG_CALL(ulpath, ULPATH_DEBUG_, m, x) | |
41 | ||
42 | #define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(ulpath) | |
43 | #include "debugobj.h" | |
44 | ||
45 | void ul_path_init_debug(void) | |
46 | { | |
47 | if (ulpath_debug_mask) | |
48 | return; | |
49 | __UL_INIT_DEBUG_FROM_ENV(ulpath, ULPATH_DEBUG_, 0, ULPATH_DEBUG); | |
50 | } | |
8148217b | 51 | |
83bbeb77 | 52 | struct path_cxt *ul_new_path(const char *dir, ...) |
1ed21c80 KZ |
53 | { |
54 | struct path_cxt *pc = calloc(1, sizeof(*pc)); | |
55 | ||
56 | if (!pc) | |
57 | return NULL; | |
58 | ||
bcf445fd KZ |
59 | DBG(CXT, ul_debugobj(pc, "alloc")); |
60 | ||
1ed21c80 KZ |
61 | pc->refcount = 1; |
62 | pc->dir_fd = -1; | |
63 | ||
64 | if (dir) { | |
83bbeb77 KZ |
65 | int rc; |
66 | va_list ap; | |
67 | ||
68 | va_start(ap, dir); | |
69 | rc = vasprintf(&pc->dir_path, dir, ap); | |
70 | va_end(ap); | |
71 | ||
72 | if (rc < 0 || !pc->dir_path) | |
1ed21c80 KZ |
73 | goto fail; |
74 | } | |
75 | return pc; | |
76 | fail: | |
77 | ul_unref_path(pc); | |
78 | return NULL; | |
79 | } | |
80 | ||
81 | void ul_ref_path(struct path_cxt *pc) | |
82 | { | |
83 | if (pc) | |
84 | pc->refcount++; | |
85 | } | |
8148217b | 86 | |
1ed21c80 | 87 | void ul_unref_path(struct path_cxt *pc) |
d182565b | 88 | { |
1ed21c80 KZ |
89 | if (!pc) |
90 | return; | |
d182565b | 91 | |
1ed21c80 KZ |
92 | pc->refcount--; |
93 | ||
94 | if (pc->refcount <= 0) { | |
bcf445fd | 95 | DBG(CXT, ul_debugobj(pc, "dealloc")); |
1ed21c80 KZ |
96 | if (pc->dialect) |
97 | pc->free_dialect(pc); | |
c9ea91d4 | 98 | ul_path_close_dirfd(pc); |
1ed21c80 KZ |
99 | free(pc->dir_path); |
100 | free(pc->prefix); | |
101 | free(pc); | |
d182565b | 102 | } |
1ed21c80 KZ |
103 | } |
104 | ||
105 | int ul_path_set_prefix(struct path_cxt *pc, const char *prefix) | |
106 | { | |
107 | char *p = NULL; | |
108 | ||
109 | assert(pc->dir_fd < 0); | |
110 | ||
111 | if (prefix) { | |
112 | p = strdup(prefix); | |
113 | if (!p) | |
114 | return -ENOMEM; | |
115 | } | |
116 | ||
117 | free(pc->prefix); | |
118 | pc->prefix = p; | |
bcf445fd | 119 | DBG(CXT, ul_debugobj(pc, "new prefix: '%s'", p)); |
d182565b KZ |
120 | return 0; |
121 | } | |
122 | ||
1ed21c80 | 123 | const char *ul_path_get_prefix(struct path_cxt *pc) |
8148217b | 124 | { |
1ed21c80 KZ |
125 | return pc ? pc->prefix : NULL; |
126 | } | |
127 | ||
128 | int ul_path_set_dir(struct path_cxt *pc, const char *dir) | |
129 | { | |
130 | char *p = NULL; | |
131 | ||
132 | if (dir) { | |
133 | p = strdup(dir); | |
134 | if (!p) | |
135 | return -ENOMEM; | |
136 | } | |
f567220b | 137 | |
1ed21c80 KZ |
138 | if (pc->dir_fd >= 0) { |
139 | close(pc->dir_fd); | |
140 | pc->dir_fd = -1; | |
141 | } | |
142 | ||
143 | free(pc->dir_path); | |
144 | pc->dir_path = p; | |
bcf445fd | 145 | DBG(CXT, ul_debugobj(pc, "new dir: '%s'", p)); |
1ed21c80 KZ |
146 | return 0; |
147 | } | |
148 | ||
149 | const char *ul_path_get_dir(struct path_cxt *pc) | |
150 | { | |
151 | return pc ? pc->dir_path : NULL; | |
152 | } | |
153 | ||
154 | int ul_path_set_dialect(struct path_cxt *pc, void *data, void free_data(struct path_cxt *)) | |
155 | { | |
156 | pc->dialect = data; | |
157 | pc->free_dialect = free_data; | |
866ac74f | 158 | DBG(CXT, ul_debugobj(pc, "(re)set dialect")); |
1ed21c80 KZ |
159 | return 0; |
160 | } | |
161 | ||
162 | void *ul_path_get_dialect(struct path_cxt *pc) | |
163 | { | |
164 | return pc ? pc->dialect : NULL; | |
165 | } | |
166 | ||
167 | int ul_path_set_enoent_redirect(struct path_cxt *pc, int (*func)(struct path_cxt *, const char *, int *)) | |
168 | { | |
169 | pc->redirect_on_enoent = func; | |
170 | return 0; | |
171 | } | |
172 | ||
173 | static const char *get_absdir(struct path_cxt *pc) | |
174 | { | |
175 | int rc; | |
c87b53ca | 176 | const char *dirpath; |
1ed21c80 KZ |
177 | |
178 | if (!pc->prefix) | |
179 | return pc->dir_path; | |
180 | ||
c87b53ca KZ |
181 | dirpath = pc->dir_path; |
182 | if (!dirpath) | |
183 | return pc->prefix; | |
184 | if (*dirpath == '/') | |
185 | dirpath++; | |
186 | ||
187 | rc = snprintf(pc->path_buffer, sizeof(pc->path_buffer), "%s/%s", pc->prefix, dirpath); | |
f567220b RM |
188 | if (rc < 0) |
189 | return NULL; | |
1ed21c80 | 190 | if ((size_t)rc >= sizeof(pc->path_buffer)) { |
f567220b RM |
191 | errno = ENAMETOOLONG; |
192 | return NULL; | |
193 | } | |
1ed21c80 KZ |
194 | |
195 | return pc->path_buffer; | |
196 | } | |
197 | ||
1ed21c80 KZ |
198 | int ul_path_get_dirfd(struct path_cxt *pc) |
199 | { | |
200 | assert(pc); | |
201 | assert(pc->dir_path); | |
202 | ||
203 | if (pc->dir_fd < 0) { | |
204 | const char *path = get_absdir(pc); | |
205 | if (!path) | |
206 | return -errno; | |
bcf445fd KZ |
207 | |
208 | DBG(CXT, ul_debugobj(pc, "opening dir: '%s'", path)); | |
1ed21c80 KZ |
209 | pc->dir_fd = open(path, O_RDONLY|O_CLOEXEC); |
210 | } | |
211 | ||
212 | return pc->dir_fd; | |
213 | } | |
214 | ||
c9ea91d4 KZ |
215 | /* Note that next ul_path_get_dirfd() will reopen the directory */ |
216 | void ul_path_close_dirfd(struct path_cxt *pc) | |
217 | { | |
218 | assert(pc); | |
219 | ||
220 | if (pc->dir_fd >= 0) { | |
221 | DBG(CXT, ul_debugobj(pc, "closing dir: '%s'", pc->dir_path)); | |
222 | close(pc->dir_fd); | |
223 | pc->dir_fd = -1; | |
224 | } | |
225 | } | |
226 | ||
227 | int ul_path_isopen_dirfd(struct path_cxt *pc) | |
228 | { | |
229 | return pc && pc->dir_fd >= 0; | |
230 | } | |
231 | ||
1ed21c80 KZ |
232 | static const char *ul_path_mkpath(struct path_cxt *pc, const char *path, va_list ap) |
233 | { | |
2c417af1 | 234 | int rc; |
1ed21c80 | 235 | |
2c417af1 KZ |
236 | errno = 0; |
237 | ||
238 | rc = vsnprintf(pc->path_buffer, sizeof(pc->path_buffer), path, ap); | |
239 | if (rc < 0) { | |
240 | if (!errno) | |
241 | errno = EINVAL; | |
1ed21c80 | 242 | return NULL; |
2c417af1 | 243 | } |
1ed21c80 KZ |
244 | |
245 | if ((size_t)rc >= sizeof(pc->path_buffer)) { | |
246 | errno = ENAMETOOLONG; | |
247 | return NULL; | |
248 | } | |
249 | ||
250 | return pc->path_buffer; | |
251 | } | |
252 | ||
e74e5401 KZ |
253 | char *ul_path_get_abspath(struct path_cxt *pc, char *buf, size_t bufsz, const char *path, ...) |
254 | { | |
255 | if (path) { | |
256 | int rc; | |
257 | va_list ap; | |
c87b53ca | 258 | const char *tail = NULL, *dirpath = pc->dir_path; |
e74e5401 KZ |
259 | |
260 | va_start(ap, path); | |
261 | tail = ul_path_mkpath(pc, path, ap); | |
262 | va_end(ap); | |
263 | ||
c87b53ca KZ |
264 | if (dirpath && *dirpath == '/') |
265 | dirpath++; | |
266 | if (tail && *tail == '/') | |
267 | tail++; | |
268 | ||
e74e5401 KZ |
269 | rc = snprintf(buf, bufsz, "%s/%s/%s", |
270 | pc->prefix ? pc->prefix : "", | |
c87b53ca KZ |
271 | dirpath ? dirpath : "", |
272 | tail ? tail : ""); | |
e74e5401 KZ |
273 | |
274 | if ((size_t)rc >= bufsz) { | |
275 | errno = ENAMETOOLONG; | |
276 | return NULL; | |
277 | } | |
278 | } else { | |
279 | const char *tmp = get_absdir(pc); | |
280 | ||
281 | if (!tmp) | |
282 | return NULL; | |
e2569bcf | 283 | xstrncpy(buf, tmp, bufsz); |
e74e5401 KZ |
284 | } |
285 | ||
286 | return buf; | |
287 | } | |
288 | ||
289 | ||
1ed21c80 KZ |
290 | int ul_path_access(struct path_cxt *pc, int mode, const char *path) |
291 | { | |
292 | int dir, rc; | |
293 | ||
294 | dir = ul_path_get_dirfd(pc); | |
295 | if (dir < 0) | |
296 | return dir; | |
297 | ||
c7df0f42 | 298 | DBG(CXT, ul_debugobj(pc, "access: '%s'", path)); |
1ed21c80 KZ |
299 | rc = faccessat(dir, path, mode, 0); |
300 | ||
301 | if (rc && errno == ENOENT | |
302 | && pc->redirect_on_enoent | |
303 | && pc->redirect_on_enoent(pc, path, &dir) == 0) | |
304 | rc = faccessat(dir, path, mode, 0); | |
1ed21c80 | 305 | return rc; |
8148217b HC |
306 | } |
307 | ||
1ed21c80 | 308 | int ul_path_accessf(struct path_cxt *pc, int mode, const char *path, ...) |
dd3bc51a | 309 | { |
1ed21c80 | 310 | va_list ap; |
dd3bc51a | 311 | const char *p; |
1ed21c80 KZ |
312 | |
313 | va_start(ap, path); | |
314 | p = ul_path_mkpath(pc, path, ap); | |
315 | va_end(ap); | |
316 | ||
2c417af1 | 317 | return !p ? -errno : ul_path_access(pc, mode, p); |
1ed21c80 KZ |
318 | } |
319 | ||
320 | int ul_path_open(struct path_cxt *pc, int flags, const char *path) | |
321 | { | |
fcc4517c | 322 | int fd; |
1ed21c80 | 323 | |
fcc4517c KZ |
324 | if (!pc) { |
325 | fd = open(path, flags); | |
c9ea91d4 | 326 | DBG(CXT, ul_debug("opening '%s' [no context]", path)); |
fcc4517c | 327 | } else { |
eb4a75af | 328 | int fdx; |
fcc4517c KZ |
329 | int dir = ul_path_get_dirfd(pc); |
330 | if (dir < 0) | |
331 | return dir; | |
1ed21c80 | 332 | |
eb4a75af | 333 | fdx = fd = openat(dir, path, flags); |
1ed21c80 | 334 | |
fcc4517c KZ |
335 | if (fd < 0 && errno == ENOENT |
336 | && pc->redirect_on_enoent | |
337 | && pc->redirect_on_enoent(pc, path, &dir) == 0) | |
338 | fd = openat(dir, path, flags); | |
339 | ||
eb4a75af | 340 | DBG(CXT, ul_debugobj(pc, "opening '%s'%s", path, fdx != fd ? " [redirected]" : "")); |
fcc4517c | 341 | } |
1ed21c80 KZ |
342 | return fd; |
343 | } | |
344 | ||
345 | int ul_path_vopenf(struct path_cxt *pc, int flags, const char *path, va_list ap) | |
346 | { | |
2c417af1 | 347 | const char *p = ul_path_mkpath(pc, path, ap); |
1ed21c80 | 348 | |
2c417af1 | 349 | return !p ? -errno : ul_path_open(pc, flags, p); |
1ed21c80 KZ |
350 | } |
351 | ||
352 | int ul_path_openf(struct path_cxt *pc, int flags, const char *path, ...) | |
353 | { | |
dd3bc51a | 354 | va_list ap; |
1ed21c80 | 355 | int rc; |
dd3bc51a KZ |
356 | |
357 | va_start(ap, path); | |
1ed21c80 | 358 | rc = ul_path_vopenf(pc, flags, path, ap); |
dd3bc51a KZ |
359 | va_end(ap); |
360 | ||
1ed21c80 | 361 | return rc; |
dd3bc51a KZ |
362 | } |
363 | ||
1ed21c80 KZ |
364 | /* |
365 | * Maybe stupid, but good enough ;-) | |
366 | */ | |
367 | static int mode2flags(const char *mode) | |
8148217b | 368 | { |
1ed21c80 KZ |
369 | int flags = 0; |
370 | const char *p; | |
371 | ||
372 | for (p = mode; p && *p; p++) { | |
373 | if (*p == 'r' && *(p + 1) == '+') | |
374 | flags |= O_RDWR; | |
375 | else if (*p == 'r') | |
376 | flags |= O_RDONLY; | |
377 | ||
378 | else if (*p == 'w' && *(p + 1) == '+') | |
379 | flags |= O_RDWR | O_TRUNC; | |
380 | else if (*p == 'w') | |
381 | flags |= O_WRONLY | O_TRUNC; | |
382 | ||
383 | else if (*p == 'a' && *(p + 1) == '+') | |
384 | flags |= O_RDWR | O_APPEND; | |
385 | else if (*p == 'a') | |
386 | flags |= O_WRONLY | O_APPEND; | |
387 | #ifdef O_CLOEXEC | |
388 | else if (*p == *UL_CLOEXECSTR) | |
389 | flags |= O_CLOEXEC; | |
390 | #endif | |
391 | } | |
392 | ||
393 | return flags; | |
394 | } | |
395 | ||
396 | FILE *ul_path_fopen(struct path_cxt *pc, const char *mode, const char *path) | |
397 | { | |
398 | int flags = mode2flags(mode); | |
399 | int fd = ul_path_open(pc, flags, path); | |
400 | ||
401 | if (fd < 0) | |
402 | return NULL; | |
403 | ||
404 | return fdopen(fd, mode); | |
405 | } | |
406 | ||
407 | ||
408 | FILE *ul_path_vfopenf(struct path_cxt *pc, const char *mode, const char *path, va_list ap) | |
409 | { | |
2c417af1 | 410 | const char *p = ul_path_mkpath(pc, path, ap); |
1ed21c80 | 411 | |
2c417af1 | 412 | return !p ? NULL : ul_path_fopen(pc, mode, p); |
1ed21c80 KZ |
413 | } |
414 | ||
415 | FILE *ul_path_fopenf(struct path_cxt *pc, const char *mode, const char *path, ...) | |
416 | { | |
417 | FILE *f; | |
418 | va_list ap; | |
419 | ||
420 | va_start(ap, path); | |
421 | f = ul_path_vfopenf(pc, mode, path, ap); | |
422 | va_end(ap); | |
f567220b | 423 | |
8148217b HC |
424 | return f; |
425 | } | |
426 | ||
1ed21c80 KZ |
427 | /* |
428 | * Open directory @path in read-onl mode. If the path is NULL then duplicate FD | |
429 | * to the directory addressed by @pc. | |
430 | */ | |
431 | DIR *ul_path_opendir(struct path_cxt *pc, const char *path) | |
9bc2b4b1 | 432 | { |
1ed21c80 KZ |
433 | DIR *dir; |
434 | int fd = -1; | |
435 | ||
436 | if (path) | |
437 | fd = ul_path_open(pc, O_RDONLY|O_CLOEXEC, path); | |
bcf445fd | 438 | else if (pc->dir_path) { |
8e281513 KZ |
439 | int dirfd; |
440 | ||
bcf445fd | 441 | DBG(CXT, ul_debugobj(pc, "duplicate dir path")); |
8e281513 KZ |
442 | dirfd = ul_path_get_dirfd(pc); |
443 | if (dirfd >= 0) | |
444 | fd = dup_fd_cloexec(dirfd, STDERR_FILENO + 1); | |
bcf445fd | 445 | } |
1ed21c80 KZ |
446 | |
447 | if (fd < 0) | |
448 | return NULL; | |
449 | ||
450 | dir = fdopendir(fd); | |
451 | if (!dir) { | |
452 | close(fd); | |
453 | return NULL; | |
454 | } | |
455 | if (!path) | |
456 | rewinddir(dir); | |
457 | return dir; | |
458 | } | |
459 | ||
460 | ||
461 | /* | |
462 | * Open directory @path in read-onl mode. If the path is NULL then duplicate FD | |
463 | * to the directory addressed by @pc. | |
464 | */ | |
465 | DIR *ul_path_vopendirf(struct path_cxt *pc, const char *path, va_list ap) | |
466 | { | |
2c417af1 | 467 | const char *p = ul_path_mkpath(pc, path, ap); |
1ed21c80 | 468 | |
2c417af1 | 469 | return !p ? NULL : ul_path_opendir(pc, p); |
1ed21c80 KZ |
470 | } |
471 | ||
472 | /* | |
473 | * Open directory @path in read-onl mode. If the path is NULL then duplicate FD | |
474 | * to the directory addressed by @pc. | |
475 | */ | |
476 | DIR *ul_path_opendirf(struct path_cxt *pc, const char *path, ...) | |
477 | { | |
478 | va_list ap; | |
479 | DIR *dir; | |
480 | ||
481 | va_start(ap, path); | |
482 | dir = ul_path_vopendirf(pc, path, ap); | |
483 | va_end(ap); | |
484 | ||
485 | return dir; | |
486 | } | |
487 | ||
488 | /* | |
489 | * If @path is NULL then readlink is called on @pc directory. | |
490 | */ | |
491 | ssize_t ul_path_readlink(struct path_cxt *pc, char *buf, size_t bufsiz, const char *path) | |
492 | { | |
493 | int dirfd; | |
494 | ||
495 | if (!path) { | |
496 | const char *p = get_absdir(pc); | |
497 | if (!p) | |
498 | return -errno; | |
499 | return readlink(p, buf, bufsiz); | |
500 | } | |
501 | ||
502 | dirfd = ul_path_get_dirfd(pc); | |
503 | if (dirfd < 0) | |
504 | return dirfd; | |
505 | ||
506 | return readlinkat(dirfd, path, buf, bufsiz); | |
507 | } | |
508 | ||
509 | /* | |
510 | * If @path is NULL then readlink is called on @pc directory. | |
511 | */ | |
512 | ssize_t ul_path_readlinkf(struct path_cxt *pc, char *buf, size_t bufsiz, const char *path, ...) | |
513 | { | |
514 | const char *p; | |
515 | va_list ap; | |
516 | ||
517 | va_start(ap, path); | |
518 | p = ul_path_mkpath(pc, path, ap); | |
519 | va_end(ap); | |
520 | ||
2c417af1 | 521 | return !p ? -errno : ul_path_readlink(pc, buf, bufsiz, p); |
1ed21c80 KZ |
522 | } |
523 | ||
524 | int ul_path_read(struct path_cxt *pc, char *buf, size_t len, const char *path) | |
525 | { | |
526 | int rc, errsv; | |
9bc2b4b1 | 527 | int fd; |
1ed21c80 KZ |
528 | |
529 | fd = ul_path_open(pc, O_RDONLY|O_CLOEXEC, path); | |
530 | if (fd < 0) | |
531 | return -errno; | |
532 | ||
c7df0f42 | 533 | DBG(CXT, ul_debug(" reading '%s'", path)); |
1ed21c80 KZ |
534 | rc = read_all(fd, buf, len); |
535 | ||
536 | errsv = errno; | |
537 | close(fd); | |
538 | errno = errsv; | |
539 | return rc; | |
540 | } | |
541 | ||
542 | int ul_path_vreadf(struct path_cxt *pc, char *buf, size_t len, const char *path, va_list ap) | |
543 | { | |
2c417af1 | 544 | const char *p = ul_path_mkpath(pc, path, ap); |
9bc2b4b1 | 545 | |
2c417af1 | 546 | return !p ? -errno : ul_path_read(pc, buf, len, p); |
1ed21c80 | 547 | } |
f567220b | 548 | |
1ed21c80 KZ |
549 | int ul_path_readf(struct path_cxt *pc, char *buf, size_t len, const char *path, ...) |
550 | { | |
551 | va_list ap; | |
552 | int rc; | |
553 | ||
554 | va_start(ap, path); | |
555 | rc = ul_path_vreadf(pc, buf, len, path, ap); | |
556 | va_end(ap); | |
557 | ||
558 | return rc; | |
559 | } | |
560 | ||
561 | ||
562 | /* | |
563 | * Returns newly allocated buffer with data from file. Maximal size is BUFSIZ | |
564 | * (send patch if you need something bigger;-) | |
565 | * | |
3aa4775c | 566 | * Returns size of the string! |
1ed21c80 KZ |
567 | */ |
568 | int ul_path_read_string(struct path_cxt *pc, char **str, const char *path) | |
569 | { | |
570 | char buf[BUFSIZ]; | |
571 | int rc; | |
572 | ||
80838625 KZ |
573 | if (!str) |
574 | return -EINVAL; | |
1ed21c80 | 575 | |
80838625 | 576 | *str = NULL; |
1ed21c80 | 577 | rc = ul_path_read(pc, buf, sizeof(buf) - 1, path); |
80838625 | 578 | if (rc < 0) |
357dd756 | 579 | return rc; |
1ed21c80 | 580 | |
11026083 | 581 | /* Remove tailing newline (usual in sysfs) */ |
d5878cab KZ |
582 | if (rc > 0 && *(buf + rc - 1) == '\n') |
583 | --rc; | |
584 | ||
1ed21c80 KZ |
585 | buf[rc] = '\0'; |
586 | *str = strdup(buf); | |
587 | if (!*str) | |
588 | rc = -ENOMEM; | |
589 | ||
590 | return rc; | |
9bc2b4b1 HC |
591 | } |
592 | ||
1ed21c80 | 593 | int ul_path_readf_string(struct path_cxt *pc, char **str, const char *path, ...) |
8148217b | 594 | { |
1ed21c80 | 595 | const char *p; |
8148217b HC |
596 | va_list ap; |
597 | ||
598 | va_start(ap, path); | |
1ed21c80 | 599 | p = ul_path_mkpath(pc, path, ap); |
8148217b HC |
600 | va_end(ap); |
601 | ||
2c417af1 | 602 | return !p ? -errno : ul_path_read_string(pc, str, p); |
8148217b HC |
603 | } |
604 | ||
7eb8e47b KZ |
605 | int ul_path_read_buffer(struct path_cxt *pc, char *buf, size_t bufsz, const char *path) |
606 | { | |
607 | int rc = ul_path_read(pc, buf, bufsz - 1, path); | |
608 | if (rc < 0) | |
357dd756 | 609 | return rc; |
7eb8e47b | 610 | |
11026083 | 611 | /* Remove tailing newline (usual in sysfs) */ |
7eb8e47b KZ |
612 | if (rc > 0 && *(buf + rc - 1) == '\n') |
613 | --rc; | |
614 | ||
615 | buf[rc] = '\0'; | |
616 | return rc; | |
617 | } | |
618 | ||
619 | int ul_path_readf_buffer(struct path_cxt *pc, char *buf, size_t bufsz, const char *path, ...) | |
620 | { | |
621 | const char *p; | |
622 | va_list ap; | |
623 | ||
624 | va_start(ap, path); | |
625 | p = ul_path_mkpath(pc, path, ap); | |
626 | va_end(ap); | |
627 | ||
2c417af1 | 628 | return !p ? -errno : ul_path_read_buffer(pc, buf, bufsz, p); |
7eb8e47b KZ |
629 | } |
630 | ||
1ed21c80 | 631 | int ul_path_scanf(struct path_cxt *pc, const char *path, const char *fmt, ...) |
8148217b | 632 | { |
1ed21c80 KZ |
633 | FILE *f; |
634 | va_list fmt_ap; | |
635 | int rc; | |
636 | ||
637 | f = ul_path_fopen(pc, "r" UL_CLOEXECSTR, path); | |
638 | if (!f) | |
639 | return -EINVAL; | |
640 | ||
c7df0f42 KZ |
641 | DBG(CXT, ul_debug(" fscanf [%s] '%s'", fmt, path)); |
642 | ||
1ed21c80 KZ |
643 | va_start(fmt_ap, fmt); |
644 | rc = vfscanf(f, fmt, fmt_ap); | |
645 | va_end(fmt_ap); | |
646 | ||
647 | fclose(f); | |
648 | return rc; | |
649 | } | |
650 | ||
651 | int ul_path_scanff(struct path_cxt *pc, const char *path, va_list ap, const char *fmt, ...) | |
652 | { | |
653 | FILE *f; | |
654 | va_list fmt_ap; | |
655 | int rc; | |
656 | ||
657 | f = ul_path_vfopenf(pc, "r" UL_CLOEXECSTR, path, ap); | |
658 | if (!f) | |
659 | return -EINVAL; | |
660 | ||
661 | va_start(fmt_ap, fmt); | |
662 | rc = vfscanf(f, fmt, fmt_ap); | |
663 | va_end(fmt_ap); | |
664 | ||
665 | fclose(f); | |
666 | return rc; | |
667 | } | |
668 | ||
669 | ||
670 | int ul_path_read_s64(struct path_cxt *pc, int64_t *res, const char *path) | |
671 | { | |
672 | int64_t x = 0; | |
673 | int rc; | |
674 | ||
675 | rc = ul_path_scanf(pc, path, "%"SCNd64, &x); | |
676 | if (rc != 1) | |
677 | return -1; | |
678 | if (res) | |
679 | *res = x; | |
680 | return 0; | |
681 | } | |
682 | ||
683 | int ul_path_readf_s64(struct path_cxt *pc, int64_t *res, const char *path, ...) | |
684 | { | |
685 | const char *p; | |
8148217b HC |
686 | va_list ap; |
687 | ||
688 | va_start(ap, path); | |
1ed21c80 | 689 | p = ul_path_mkpath(pc, path, ap); |
8148217b HC |
690 | va_end(ap); |
691 | ||
2c417af1 | 692 | return !p ? -errno : ul_path_read_s64(pc, res, p); |
1ed21c80 KZ |
693 | } |
694 | ||
695 | int ul_path_read_u64(struct path_cxt *pc, uint64_t *res, const char *path) | |
696 | { | |
697 | uint64_t x = 0; | |
698 | int rc; | |
8148217b | 699 | |
1ed21c80 KZ |
700 | rc = ul_path_scanf(pc, path, "%"SCNu64, &x); |
701 | if (rc != 1) | |
702 | return -1; | |
703 | if (res) | |
704 | *res = x; | |
705 | return 0; | |
8148217b HC |
706 | } |
707 | ||
1ed21c80 | 708 | int ul_path_readf_u64(struct path_cxt *pc, uint64_t *res, const char *path, ...) |
8148217b | 709 | { |
1ed21c80 | 710 | const char *p; |
8148217b | 711 | va_list ap; |
8148217b HC |
712 | |
713 | va_start(ap, path); | |
1ed21c80 | 714 | p = ul_path_mkpath(pc, path, ap); |
8148217b HC |
715 | va_end(ap); |
716 | ||
2c417af1 | 717 | return !p ? -errno : ul_path_read_u64(pc, res, p); |
8148217b HC |
718 | } |
719 | ||
1ed21c80 | 720 | int ul_path_read_s32(struct path_cxt *pc, int *res, const char *path) |
37a5c7ee | 721 | { |
1ed21c80 KZ |
722 | int rc, x = 0; |
723 | ||
724 | rc = ul_path_scanf(pc, path, "%d", &x); | |
725 | if (rc != 1) | |
726 | return -1; | |
727 | if (res) | |
728 | *res = x; | |
729 | return 0; | |
730 | } | |
731 | ||
732 | int ul_path_readf_s32(struct path_cxt *pc, int *res, const char *path, ...) | |
733 | { | |
734 | const char *p; | |
37a5c7ee | 735 | va_list ap; |
37a5c7ee KZ |
736 | |
737 | va_start(ap, path); | |
1ed21c80 | 738 | p = ul_path_mkpath(pc, path, ap); |
37a5c7ee KZ |
739 | va_end(ap); |
740 | ||
2c417af1 | 741 | return !p ? -errno : ul_path_read_s32(pc, res, p); |
1ed21c80 KZ |
742 | } |
743 | ||
744 | int ul_path_read_u32(struct path_cxt *pc, unsigned int *res, const char *path) | |
745 | { | |
746 | int rc; | |
747 | unsigned int x; | |
748 | ||
749 | rc = ul_path_scanf(pc, path, "%u", &x); | |
750 | if (rc != 1) | |
751 | return -1; | |
752 | if (res) | |
753 | *res = x; | |
754 | return 0; | |
755 | } | |
756 | ||
757 | int ul_path_readf_u32(struct path_cxt *pc, unsigned int *res, const char *path, ...) | |
758 | { | |
759 | const char *p; | |
760 | va_list ap; | |
761 | ||
762 | va_start(ap, path); | |
763 | p = ul_path_mkpath(pc, path, ap); | |
764 | va_end(ap); | |
765 | ||
2c417af1 | 766 | return !p ? -errno : ul_path_read_u32(pc, res, p); |
37a5c7ee KZ |
767 | } |
768 | ||
1ed21c80 | 769 | int ul_path_read_majmin(struct path_cxt *pc, dev_t *res, const char *path) |
9bc2b4b1 | 770 | { |
1ed21c80 KZ |
771 | int rc, maj, min; |
772 | ||
773 | rc = ul_path_scanf(pc, path, "%d:%d", &maj, &min); | |
774 | if (rc != 2) | |
775 | return -1; | |
776 | if (res) | |
777 | *res = makedev(maj, min); | |
778 | return 0; | |
779 | } | |
780 | ||
781 | int ul_path_readf_majmin(struct path_cxt *pc, dev_t *res, const char *path, ...) | |
782 | { | |
783 | const char *p; | |
9bc2b4b1 HC |
784 | va_list ap; |
785 | ||
786 | va_start(ap, path); | |
1ed21c80 | 787 | p = ul_path_mkpath(pc, path, ap); |
9bc2b4b1 | 788 | va_end(ap); |
1ed21c80 | 789 | |
2c417af1 | 790 | return !p ? -errno : ul_path_read_majmin(pc, res, p); |
1ed21c80 KZ |
791 | } |
792 | ||
793 | int ul_path_write_string(struct path_cxt *pc, const char *str, const char *path) | |
794 | { | |
795 | int rc, errsv; | |
796 | int fd; | |
797 | ||
798 | fd = ul_path_open(pc, O_WRONLY|O_CLOEXEC, path); | |
799 | if (fd < 0) | |
800 | return -errno; | |
801 | ||
802 | rc = write_all(fd, str, strlen(str)); | |
803 | ||
804 | errsv = errno; | |
9bc2b4b1 | 805 | close(fd); |
1ed21c80 KZ |
806 | errno = errsv; |
807 | return rc; | |
9bc2b4b1 HC |
808 | } |
809 | ||
1ed21c80 | 810 | int ul_path_writef_string(struct path_cxt *pc, const char *str, const char *path, ...) |
8148217b | 811 | { |
1ed21c80 | 812 | const char *p; |
8148217b | 813 | va_list ap; |
1ed21c80 KZ |
814 | |
815 | va_start(ap, path); | |
816 | p = ul_path_mkpath(pc, path, ap); | |
817 | va_end(ap); | |
818 | ||
2c417af1 | 819 | return !p ? -errno : ul_path_write_string(pc, str, p); |
1ed21c80 KZ |
820 | } |
821 | ||
c455cdb3 KZ |
822 | int ul_path_write_s64(struct path_cxt *pc, int64_t num, const char *path) |
823 | { | |
824 | char buf[sizeof(stringify_value(LLONG_MAX))]; | |
825 | int rc, errsv; | |
826 | int fd, len; | |
827 | ||
828 | fd = ul_path_open(pc, O_WRONLY|O_CLOEXEC, path); | |
829 | if (fd < 0) | |
830 | return -errno; | |
831 | ||
832 | len = snprintf(buf, sizeof(buf), "%" PRId64, num); | |
833 | if (len < 0 || (size_t) len >= sizeof(buf)) | |
834 | rc = len < 0 ? -errno : -E2BIG; | |
835 | else | |
836 | rc = write_all(fd, buf, len); | |
837 | ||
838 | errsv = errno; | |
839 | close(fd); | |
840 | errno = errsv; | |
841 | return rc; | |
842 | } | |
843 | ||
1ed21c80 KZ |
844 | int ul_path_write_u64(struct path_cxt *pc, uint64_t num, const char *path) |
845 | { | |
846 | char buf[sizeof(stringify_value(ULLONG_MAX))]; | |
847 | int rc, errsv; | |
848 | int fd, len; | |
849 | ||
850 | fd = ul_path_open(pc, O_WRONLY|O_CLOEXEC, path); | |
851 | if (fd < 0) | |
852 | return -errno; | |
853 | ||
854 | len = snprintf(buf, sizeof(buf), "%" PRIu64, num); | |
855 | if (len < 0 || (size_t) len >= sizeof(buf)) | |
856 | rc = len < 0 ? -errno : -E2BIG; | |
857 | else | |
858 | rc = write_all(fd, buf, len); | |
859 | ||
860 | errsv = errno; | |
861 | close(fd); | |
862 | errno = errsv; | |
863 | return rc; | |
864 | } | |
865 | ||
866 | int ul_path_writef_u64(struct path_cxt *pc, uint64_t num, const char *path, ...) | |
867 | { | |
8148217b | 868 | const char *p; |
1ed21c80 | 869 | va_list ap; |
8148217b HC |
870 | |
871 | va_start(ap, path); | |
1ed21c80 | 872 | p = ul_path_mkpath(pc, path, ap); |
8148217b HC |
873 | va_end(ap); |
874 | ||
2c417af1 | 875 | return !p ? -errno : ul_path_write_u64(pc, num, p); |
1ed21c80 | 876 | |
8148217b HC |
877 | } |
878 | ||
1ed21c80 | 879 | int ul_path_count_dirents(struct path_cxt *pc, const char *path) |
8148217b | 880 | { |
1ed21c80 KZ |
881 | DIR *dir; |
882 | int r = 0; | |
883 | ||
884 | dir = ul_path_opendir(pc, path); | |
885 | if (!dir) | |
886 | return 0; | |
887 | ||
888 | while (xreaddir(dir)) r++; | |
889 | ||
890 | closedir(dir); | |
891 | return r; | |
892 | } | |
893 | ||
894 | int ul_path_countf_dirents(struct path_cxt *pc, const char *path, ...) | |
895 | { | |
896 | const char *p; | |
897 | va_list ap; | |
898 | ||
899 | va_start(ap, path); | |
900 | p = ul_path_mkpath(pc, path, ap); | |
901 | va_end(ap); | |
902 | ||
2c417af1 | 903 | return !p ? -errno : ul_path_count_dirents(pc, p); |
1ed21c80 KZ |
904 | } |
905 | ||
9023b201 KZ |
906 | /* |
907 | * Like fopen() but, @path is always prefixed by @prefix. This function is | |
908 | * useful in case when ul_path_* API is overkill. | |
909 | */ | |
910 | FILE *ul_prefix_fopen(const char *prefix, const char *path, const char *mode) | |
911 | { | |
912 | char buf[PATH_MAX]; | |
913 | ||
914 | if (!path) | |
915 | return NULL; | |
916 | if (!prefix) | |
917 | return fopen(path, mode); | |
918 | if (*path == '/') | |
919 | path++; | |
920 | ||
921 | snprintf(buf, sizeof(buf), "%s/%s", prefix, path); | |
922 | return fopen(buf, mode); | |
923 | } | |
924 | ||
1ed21c80 KZ |
925 | #ifdef HAVE_CPU_SET_T |
926 | static int ul_path_cpuparse(struct path_cxt *pc, cpu_set_t **set, int maxcpus, int islist, const char *path, va_list ap) | |
927 | { | |
928 | FILE *f; | |
8148217b HC |
929 | size_t setsize, len = maxcpus * 7; |
930 | char buf[len]; | |
91bf5817 | 931 | int rc; |
8148217b | 932 | |
9023b201 KZ |
933 | *set = NULL; |
934 | ||
1ed21c80 KZ |
935 | f = ul_path_vfopenf(pc, "r" UL_CLOEXECSTR, path, ap); |
936 | if (!f) | |
937 | return -errno; | |
8148217b | 938 | |
91bf5817 | 939 | rc = fgets(buf, len, f) == NULL ? -errno : 0; |
1ed21c80 | 940 | fclose(f); |
8148217b | 941 | |
91bf5817 KZ |
942 | if (rc) |
943 | return rc; | |
944 | ||
8148217b HC |
945 | len = strlen(buf); |
946 | if (buf[len - 1] == '\n') | |
947 | buf[len - 1] = '\0'; | |
948 | ||
1ed21c80 KZ |
949 | *set = cpuset_alloc(maxcpus, &setsize, NULL); |
950 | if (!*set) | |
951 | return -ENOMEM; | |
8148217b HC |
952 | |
953 | if (islist) { | |
9023b201 KZ |
954 | if (cpulist_parse(buf, *set, setsize, 0)) { |
955 | cpuset_free(*set); | |
1ed21c80 | 956 | return -EINVAL; |
9023b201 | 957 | } |
8148217b | 958 | } else { |
9023b201 KZ |
959 | if (cpumask_parse(buf, *set, setsize)) { |
960 | cpuset_free(*set); | |
1ed21c80 | 961 | return -EINVAL; |
9023b201 | 962 | } |
8148217b | 963 | } |
1ed21c80 | 964 | return 0; |
8148217b HC |
965 | } |
966 | ||
1ed21c80 | 967 | int ul_path_readf_cpuset(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...) |
8148217b HC |
968 | { |
969 | va_list ap; | |
1ed21c80 | 970 | int rc = 0; |
8148217b HC |
971 | |
972 | va_start(ap, path); | |
1ed21c80 | 973 | rc = ul_path_cpuparse(pc, set, maxcpus, 0, path, ap); |
8148217b HC |
974 | va_end(ap); |
975 | ||
1ed21c80 | 976 | return rc; |
8148217b HC |
977 | } |
978 | ||
1ed21c80 | 979 | int ul_path_readf_cpulist(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...) |
8148217b HC |
980 | { |
981 | va_list ap; | |
1ed21c80 | 982 | int rc = 0; |
8148217b HC |
983 | |
984 | va_start(ap, path); | |
1ed21c80 | 985 | rc = ul_path_cpuparse(pc, set, maxcpus, 1, path, ap); |
8148217b HC |
986 | va_end(ap); |
987 | ||
1ed21c80 | 988 | return rc; |
8148217b | 989 | } |
1ed21c80 | 990 | |
4a04d7f3 | 991 | #endif /* HAVE_CPU_SET_T */ |
1ed21c80 KZ |
992 | |
993 | ||
994 | #ifdef TEST_PROGRAM_PATH | |
995 | #include <getopt.h> | |
996 | ||
997 | static void __attribute__((__noreturn__)) usage(void) | |
998 | { | |
999 | fprintf(stdout, " %s [options] <dir> <command>\n\n", program_invocation_short_name); | |
1000 | fputs(" -p, --prefix <dir> redirect hardcoded paths to <dir>\n", stdout); | |
1001 | ||
1002 | fputs(" Commands:\n", stdout); | |
1003 | fputs(" read-u64 <file> read uint64_t from file\n", stdout); | |
1004 | fputs(" read-s64 <file> read int64_t from file\n", stdout); | |
1005 | fputs(" read-u32 <file> read uint32_t from file\n", stdout); | |
1006 | fputs(" read-s32 <file> read int32_t from file\n", stdout); | |
1007 | fputs(" read-string <file> read string from file\n", stdout); | |
1008 | fputs(" read-majmin <file> read devno from file\n", stdout); | |
1009 | fputs(" read-link <file> read symlink\n", stdout); | |
1010 | fputs(" write-string <file> <str> write string from file\n", stdout); | |
1011 | fputs(" write-u64 <file> <str> write uint64_t from file\n", stdout); | |
1012 | ||
1013 | exit(EXIT_SUCCESS); | |
1014 | } | |
1015 | ||
1016 | int main(int argc, char *argv[]) | |
1017 | { | |
1018 | int c; | |
1019 | const char *prefix = NULL, *dir, *file, *command; | |
1020 | struct path_cxt *pc = NULL; | |
1021 | ||
1022 | static const struct option longopts[] = { | |
1023 | { "prefix", 1, NULL, 'p' }, | |
1024 | { "help", 0, NULL, 'h' }, | |
1025 | { NULL, 0, NULL, 0 }, | |
1026 | }; | |
1027 | ||
1028 | while((c = getopt_long(argc, argv, "p:h", longopts, NULL)) != -1) { | |
1029 | switch(c) { | |
1030 | case 'p': | |
24c329bb | 1031 | prefix = optarg; |
1ed21c80 KZ |
1032 | break; |
1033 | case 'h': | |
1034 | usage(); | |
1035 | break; | |
1036 | default: | |
1037 | err(EXIT_FAILURE, "try --help"); | |
1038 | } | |
1039 | } | |
1040 | ||
1041 | if (optind == argc) | |
1042 | errx(EXIT_FAILURE, "<dir> not defined"); | |
1043 | dir = argv[optind++]; | |
1044 | ||
bcf445fd KZ |
1045 | ul_path_init_debug(); |
1046 | ||
1ed21c80 KZ |
1047 | pc = ul_new_path(dir); |
1048 | if (!pc) | |
1049 | err(EXIT_FAILURE, "failed to initialize path context"); | |
1050 | if (prefix) | |
1051 | ul_path_set_prefix(pc, prefix); | |
1052 | ||
1053 | if (optind == argc) | |
1054 | errx(EXIT_FAILURE, "<command> not defined"); | |
1055 | command = argv[optind++]; | |
1056 | ||
1057 | if (strcmp(command, "read-u32") == 0) { | |
1058 | uint32_t res; | |
1059 | ||
1060 | if (optind == argc) | |
1061 | errx(EXIT_FAILURE, "<file> not defined"); | |
1062 | file = argv[optind++]; | |
1063 | ||
1064 | if (ul_path_read_u32(pc, &res, file) != 0) | |
1065 | err(EXIT_FAILURE, "read u64 failed"); | |
1066 | printf("read: %s: %u\n", file, res); | |
1067 | ||
1068 | if (ul_path_readf_u32(pc, &res, "%s", file) != 0) | |
1069 | err(EXIT_FAILURE, "readf u64 failed"); | |
1070 | printf("readf: %s: %u\n", file, res); | |
1071 | ||
1072 | } else if (strcmp(command, "read-s32") == 0) { | |
1073 | int32_t res; | |
1074 | ||
1075 | if (optind == argc) | |
1076 | errx(EXIT_FAILURE, "<file> not defined"); | |
1077 | file = argv[optind++]; | |
1078 | ||
1079 | if (ul_path_read_s32(pc, &res, file) != 0) | |
1080 | err(EXIT_FAILURE, "read u64 failed"); | |
1081 | printf("read: %s: %d\n", file, res); | |
1082 | ||
1083 | if (ul_path_readf_s32(pc, &res, "%s", file) != 0) | |
1084 | err(EXIT_FAILURE, "readf u64 failed"); | |
1085 | printf("readf: %s: %d\n", file, res); | |
1086 | ||
1087 | } else if (strcmp(command, "read-u64") == 0) { | |
1088 | uint64_t res; | |
1089 | ||
1090 | if (optind == argc) | |
1091 | errx(EXIT_FAILURE, "<file> not defined"); | |
1092 | file = argv[optind++]; | |
1093 | ||
1094 | if (ul_path_read_u64(pc, &res, file) != 0) | |
1095 | err(EXIT_FAILURE, "read u64 failed"); | |
1096 | printf("read: %s: %" PRIu64 "\n", file, res); | |
1097 | ||
1098 | if (ul_path_readf_u64(pc, &res, "%s", file) != 0) | |
1099 | err(EXIT_FAILURE, "readf u64 failed"); | |
1100 | printf("readf: %s: %" PRIu64 "\n", file, res); | |
1101 | ||
1102 | } else if (strcmp(command, "read-s64") == 0) { | |
1103 | int64_t res; | |
1104 | ||
1105 | if (optind == argc) | |
1106 | errx(EXIT_FAILURE, "<file> not defined"); | |
1107 | file = argv[optind++]; | |
1108 | ||
1109 | if (ul_path_read_s64(pc, &res, file) != 0) | |
1110 | err(EXIT_FAILURE, "read u64 failed"); | |
1111 | printf("read: %s: %" PRIu64 "\n", file, res); | |
1112 | ||
1113 | if (ul_path_readf_s64(pc, &res, "%s", file) != 0) | |
1114 | err(EXIT_FAILURE, "readf u64 failed"); | |
1115 | printf("readf: %s: %" PRIu64 "\n", file, res); | |
1116 | ||
1117 | } else if (strcmp(command, "read-majmin") == 0) { | |
1118 | dev_t res; | |
1119 | ||
1120 | if (optind == argc) | |
1121 | errx(EXIT_FAILURE, "<file> not defined"); | |
1122 | file = argv[optind++]; | |
1123 | ||
1124 | if (ul_path_read_majmin(pc, &res, file) != 0) | |
1125 | err(EXIT_FAILURE, "read maj:min failed"); | |
1126 | printf("read: %s: %d\n", file, (int) res); | |
1127 | ||
1128 | if (ul_path_readf_majmin(pc, &res, "%s", file) != 0) | |
1129 | err(EXIT_FAILURE, "readf maj:min failed"); | |
1130 | printf("readf: %s: %d\n", file, (int) res); | |
1131 | ||
1132 | } else if (strcmp(command, "read-string") == 0) { | |
1133 | char *res; | |
1134 | ||
1135 | if (optind == argc) | |
1136 | errx(EXIT_FAILURE, "<file> not defined"); | |
1137 | file = argv[optind++]; | |
1138 | ||
905bc781 KZ |
1139 | if (ul_path_read_string(pc, &res, file) < 0) |
1140 | err(EXIT_FAILURE, "read string failed"); | |
1ed21c80 KZ |
1141 | printf("read: %s: %s\n", file, res); |
1142 | ||
905bc781 KZ |
1143 | if (ul_path_readf_string(pc, &res, "%s", file) < 0) |
1144 | err(EXIT_FAILURE, "readf string failed"); | |
1ed21c80 KZ |
1145 | printf("readf: %s: %s\n", file, res); |
1146 | ||
1147 | } else if (strcmp(command, "read-link") == 0) { | |
1148 | char res[PATH_MAX]; | |
1149 | ||
1150 | if (optind == argc) | |
1151 | errx(EXIT_FAILURE, "<file> not defined"); | |
1152 | file = argv[optind++]; | |
1153 | ||
1154 | if (ul_path_readlink(pc, res, sizeof(res), file) < 0) | |
1155 | err(EXIT_FAILURE, "read symlink failed"); | |
1156 | printf("read: %s: %s\n", file, res); | |
1157 | ||
1158 | if (ul_path_readlinkf(pc, res, sizeof(res), "%s", file) < 0) | |
1159 | err(EXIT_FAILURE, "readf symlink failed"); | |
1160 | printf("readf: %s: %s\n", file, res); | |
1161 | ||
1162 | } else if (strcmp(command, "write-string") == 0) { | |
1163 | char *str; | |
1164 | ||
1165 | if (optind + 1 == argc) | |
1166 | errx(EXIT_FAILURE, "<file> <string> not defined"); | |
1167 | file = argv[optind++]; | |
1168 | str = argv[optind++]; | |
1169 | ||
1170 | if (ul_path_write_string(pc, str, file) != 0) | |
1171 | err(EXIT_FAILURE, "write string failed"); | |
1172 | if (ul_path_writef_string(pc, str, "%s", file) != 0) | |
1173 | err(EXIT_FAILURE, "writef string failed"); | |
1174 | ||
1175 | } else if (strcmp(command, "write-u64") == 0) { | |
1176 | uint64_t num; | |
1177 | ||
1178 | if (optind + 1 == argc) | |
1179 | errx(EXIT_FAILURE, "<file> <num> not defined"); | |
1180 | file = argv[optind++]; | |
1181 | num = strtoumax(argv[optind++], NULL, 0); | |
1182 | ||
1183 | if (ul_path_write_u64(pc, num, file) != 0) | |
1184 | err(EXIT_FAILURE, "write u64 failed"); | |
1185 | if (ul_path_writef_u64(pc, num, "%s", file) != 0) | |
1186 | err(EXIT_FAILURE, "writef u64 failed"); | |
1187 | } | |
1188 | ||
1189 | ul_unref_path(pc); | |
1190 | return EXIT_SUCCESS; | |
1191 | } | |
1192 | #endif /* TEST_PROGRAM_PATH */ | |
1193 |