Merge branch 'refs/heads/t/configtest'
[dss.git] / file.c
1 /*
2  * Copyright (C) 2006-2010 Andre Noll <maan@tuebingen.mpg.de>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <assert.h>
10 #include <string.h>
11 #include <errno.h>
12 #include <sys/types.h>
13 #include <dirent.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16
17 #include "gcc-compat.h"
18 #include "err.h"
19 #include "str.h"
20 #include "file.h"
21
22 /**
23  * Call a function for each subdirectory of the current working directory.
24  *
25  * \param dirname The directory to traverse.
26  * \param func The function to call for each subdirectory.
27  * \param private_data Pointer to an arbitrary data structure.
28  *
29  * For each top-level directory under \a dirname, the supplied function \a func is
30  * called.  The full path of the subdirectory and the \a private_data pointer
31  * are passed to \a func.
32  *
33  * \return This function returns immediately if \a func returned a negative
34  * value. In this case \a func must set error_txt and this negative value is
35  * returned to the caller. Otherwise the function returns when all
36  * subdirectories have been passed to \a func.
37  */
38
39 int for_each_subdir(int (*func)(const char *, void *), void *private_data)
40 {
41         struct dirent *entry;
42         int ret;
43         DIR *dir = opendir(".");
44
45         if (!dir)
46                 return -ERRNO_TO_DSS_ERROR(errno);
47         while ((entry = readdir(dir))) {
48                 mode_t m;
49                 struct stat s;
50
51                 if (!strcmp(entry->d_name, "."))
52                         continue;
53                 if (!strcmp(entry->d_name, ".."))
54                         continue;
55                 ret = lstat(entry->d_name, &s) == -1;
56                 if (ret == -1) {
57                         ret = -ERRNO_TO_DSS_ERROR(errno);
58                         goto out;
59                 }
60                 m = s.st_mode;
61                 if (!S_ISDIR(m))
62                         continue;
63                 ret = func(entry->d_name, private_data);
64                 if (ret < 0)
65                         goto out;
66         }
67         ret = 1;
68 out:
69         closedir(dir);
70         return ret;
71 }
72
73 /**
74  * Set a file descriptor to non-blocking mode.
75  *
76  * \param fd The file descriptor.
77  *
78  * \return Standard.
79  */
80 __must_check int mark_fd_nonblocking(int fd)
81 {
82         int flags = fcntl(fd, F_GETFL);
83         if (flags < 0)
84                 return -ERRNO_TO_DSS_ERROR(errno);
85         flags = fcntl(fd, F_SETFL, ((long)flags) | O_NONBLOCK);
86         if (flags < 0)
87                 return -ERRNO_TO_DSS_ERROR(errno);
88         return 1;
89 }
90
91 /**
92  * dss' wrapper for select(2).
93  *
94  * It calls select(2) (with no exceptfds) and starts over if select() was
95  * interrupted by a signal.
96  *
97  * \param n The highest-numbered descriptor in any of the two sets, plus 1.
98  * \param readfds fds that should be checked for readability.
99  * \param writefds fds that should be checked for writablility.
100  * \param timeout_tv upper bound on the amount of time elapsed before select()
101  * returns.
102  *
103  * \return The return value of the underlying select() call on success, the
104  * negative system error code on errors.
105  *
106  * All arguments are passed verbatim to select(2).
107  * \sa select(2) select_tut(2).
108  */
109 int dss_select(int n, fd_set *readfds, fd_set *writefds,
110                 struct timeval *timeout_tv)
111 {
112         int ret, err;
113         do {
114                 ret = select(n, readfds, writefds, NULL, timeout_tv);
115                 err = errno;
116         } while (ret < 0 && err == EINTR);
117         if (ret < 0)
118                 return -ERRNO_TO_DSS_ERROR(errno);
119         return ret;
120 }