1 #ifndef EX_UTILS_H
2 #define EX_UTILS_H 1
3 
4 /* ************************************************************************** */
5 /* headers: Vstr (and all supporting system headers), plus extra ones we need */
6 /* ************************************************************************** */
7 #define _GNU_SOURCE 1 /* for posix_fadvice etc. */
8 #define VSTR_COMPILE_INCLUDE 1 /* make Vstr include it's system headers */
9 #include <vstr.h>
10 
11 #include <errno.h>
12 
13 #include <err.h> /* BSD/Linux header see: man errx */
14 
15 #include <poll.h>
16 
17 #include <sys/types.h> /* stat + open + STDXXX_FILENO */
18 #include <sys/stat.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 
22 #include <string.h> /* strncmp() etc. in GETOPT macros */
23 
24 #ifdef VSTR_AUTOCONF_fstat64
25 # define fstat64 VSTR_AUTOCONF_fstat64
26 #endif
27 #ifdef VSTR_AUTOCONF_stat64
28 /* does "stat" + "struct stat" */
29 # define stat64 VSTR_AUTOCONF_stat64
30 #endif
31 #ifdef VSTR_AUTOCONF_off64_t
32 # define off64_t VSTR_AUTOCONF_off64_t
33 #endif
34 #ifdef VSTR_AUTOCONF_lseek64
35 # define lseek64 VSTR_AUTOCONF_lseek64
36 #endif
37 
38 /* **************************************************************** */
39 /* defines: TRUE/FALSE and assert(), Note that GETOPT is used later */
40 /* **************************************************************** */
41 
42 #ifndef FALSE
43 # define FALSE 0
44 #endif
45 
46 #ifndef TRUE
47 # define TRUE 1
48 #endif
49 
50 /* Simple getopt code... */
51 #define EX_UTILS_GETOPT_NUM(name, var) \
52     else if (!strncmp("--" name "=", argv[count], strlen("--" name "=")) || \
53              !strncmp("--" name, argv[count], strlen(argv[count])))     \
54     {                                                                   \
55       if (strncmp("--" name, argv[count], strlen(argv[count])))         \
56         (var) = strtol(argv[count] + strlen("--" name "="), NULL, 0);   \
57       else                                                              \
58       {                                                                 \
59         (var) = 0;                                                      \
60                                                                         \
61         ++count;                                                        \
62         if (count >= argc)                                              \
63           break;                                                        \
64                                                                         \
65         (var) = strtol(argv[count], NULL, 0);                           \
66       }                                                                 \
67     }                                                                   \
68     else if (0) ASSERT(FALSE)
69 
70 #define EX_UTILS_GETOPT_CSTR(name, var) \
71     else if (!strncmp("--" name "=", argv[count], strlen("--" name "=")) || \
72              !strncmp("--" name, argv[count], strlen(argv[count])))     \
73     {                                                                   \
74       if (strncmp("--" name, argv[count], strlen(argv[count])))         \
75         (var) = argv[count] + strlen("--" name "=");                    \
76       else                                                              \
77       {                                                                 \
78         (var) = NULL;                                                   \
79                                                                         \
80         ++count;                                                        \
81         if (count >= argc)                                              \
82           break;                                                        \
83                                                                         \
84         (var) = argv[count];                                            \
85       }                                                                 \
86     }                                                                   \
87     else if (0) ASSERT(FALSE)
88 
89 
90 #ifndef VSTR_AUTOCONF_NDEBUG
91 # define assert(x) do { if (x) {} else { warnx("assert(%s), FAILED at %s:%u", #x , __FILE__, __LINE__); abort(); } } while (FALSE)
92 # define ASSERT(x) do { if (x) {} else { warnx("ASSERT(%s), FAILED at %s:%u", #x , __FILE__, __LINE__); abort(); } } while (FALSE)
93 # define assert_ret(x, y) do { if (x) {} else { warnx("assert(%s), FAILED at %s:%u", #x , __FILE__, __LINE__); abort(); } } while (FALSE)
94 # define ASSERT_RET(x, y) do { if (x) {} else { warnx("ASSERT(%s), FAILED at %s:%u", #x , __FILE__, __LINE__); abort(); } } while (FALSE)
95 # define ASSERT_NO_SWITCH_DEF() break; default: ASSERT(!"default label")
96 #else
97 # define assert(x) do { } while (FALSE)
98 # define ASSERT(x) do { } while (FALSE)
99 # define assert_ret(x, y) do { if (x) {} else return y; } while (FALSE)
100 # define ASSERT_RET(x, y) do { if (x) {} else return y; } while (FALSE)
101 # define ASSERT_NO_SWITCH_DEF() break
102 #endif
103 #define ASSERT_NOT_REACHED() ASSERT(!"not reached")
104 
105 
106 /* ********************************* */
107 /* generic POSIX IO helper functions */
108 /* ********************************* */
109 
110 /* limits on amount of data we keep in core -- can be overridden */
111 /* Note that EX_UTILS_NO_USE_INPUT should be defined if Input IO isn't needed */
112 #ifndef EX_MAX_R_DATA_INCORE
113 #define EX_MAX_R_DATA_INCORE (8 * 1024)
114 #endif
115 #ifndef EX_MAX_W_DATA_INCORE
116 #define EX_MAX_W_DATA_INCORE (8 * 1024)
117 #endif
118 
119 #ifndef EX_UTILS_RET_FAIL
120 #define EX_UTILS_RET_FAIL FALSE
121 #endif
122 
123 #define IO_OK    0
124 #define IO_BLOCK 1
125 #define IO_EOF   2
126 #define IO_NONE  3
127 #define IO_FAIL  4
128 
129 #if !defined(EX_UTILS_NO_FUNCS) && !defined(EX_UTILS_NO_USE_BLOCK)
130 /* block waiting for IO read, write or both... */
io_block(int io_r_fd,int io_w_fd)131 static void io_block(int io_r_fd, int io_w_fd)
132 {
133   struct pollfd ios_beg[2];
134   struct pollfd *ios = ios_beg;
135   unsigned int num = 0;
136 
137   ios[0].revents = ios[1].revents = 0;
138 
139   if (io_r_fd == io_w_fd)
140   { /* block on both read and write, same fds */
141     num = 1;
142     ios[0].events = POLLIN | POLLOUT;
143     ios[0].fd     = io_w_fd;
144   }
145   else
146   { /* block on read or write or both */
147     if (io_r_fd != -1)
148     {
149       ios->events = POLLIN;
150       ios->fd     = io_r_fd;
151       ++num; ++ios;
152     }
153     if (io_w_fd != -1)
154     {
155       ios->events = POLLOUT;
156       ios->fd     = io_w_fd;
157       ++num; ++ios;
158     }
159   }
160 
161   while (poll(ios_beg, num, -1) == -1) /* can't timeout */
162   {
163     if (errno != EINTR)
164       err(EXIT_FAILURE, "poll");
165   }
166 }
167 #endif
168 
169 /* Try and move some data from Vstr string to fd */
170 #if !defined(EX_UTILS_NO_FUNCS) && !defined(EX_UTILS_NO_USE_PUT)
io_put(Vstr_base * io_w,int fd)171 static int io_put(Vstr_base *io_w, int fd)
172 {
173   if (!io_w->len)
174     return (IO_NONE);
175 
176   if (!vstr_sc_write_fd(io_w, 1, io_w->len, fd, NULL))
177   {
178     if (errno == EAGAIN)
179       return (IO_BLOCK);
180 
181     if (EX_UTILS_RET_FAIL)
182       return (IO_FAIL);
183 
184     err(EXIT_FAILURE, "write");
185   }
186 
187   return (IO_OK);
188 }
189 #endif
190 
191 #if !defined(EX_UTILS_NO_FUNCS) && !defined(EX_UTILS_NO_USE_BLOCK)
192 #ifndef EX_UTILS_NO_USE_PUTALL
193 /* loop outputting data until empty, blocking when needed */
io_put_all(Vstr_base * io_w,int fd)194 static int io_put_all(Vstr_base *io_w, int fd)
195 {
196   int state = IO_NONE;
197 
198   while ((state = io_put(io_w, fd)) != IO_NONE)
199   {
200     if (state == IO_BLOCK)
201       io_block(-1, fd);
202 
203     if (EX_UTILS_RET_FAIL && (state == IO_FAIL))
204       return (IO_FAIL);
205   }
206 
207   return (state);
208 }
209 #endif
210 #endif
211 
212 #if !defined(EX_UTILS_NO_FUNCS) && !defined(EX_UTILS_NO_USE_INPUT)
213 #ifndef EX_UTILS_NO_USE_GET
214 /* Try and move some data from fd to Vstr string */
io_get(Vstr_base * io_r,int fd)215 static int io_get(Vstr_base *io_r, int fd)
216 {
217   if (io_r->len < EX_MAX_R_DATA_INCORE)
218   {
219     unsigned int ern = 0;
220 
221     vstr_sc_read_iov_fd(io_r, io_r->len, fd, 56, 64, &ern);
222 
223     if (ern == VSTR_TYPE_SC_READ_FD_ERR_EOF)
224       return (IO_EOF);
225     else if ((ern == VSTR_TYPE_SC_READ_FD_ERR_READ_ERRNO) && (errno == EAGAIN))
226       return (IO_BLOCK);
227     else if (EX_UTILS_RET_FAIL && ern)
228       return (IO_FAIL);
229     else if (ern)
230       err(EXIT_FAILURE, "read");
231   }
232 
233   return (IO_OK);
234 }
235 #endif
236 
237 #if !defined(EX_UTILS_NO_FUNCS) && !defined(EX_UTILS_NO_USE_LIMIT)
238 /* block read or writting, depending on limits */
io_limit(int io_r_state,int io_r_fd,int io_w_state,int io_w_fd,Vstr_base * s_w)239 static void io_limit(int io_r_state, int io_r_fd,
240                      int io_w_state, int io_w_fd, Vstr_base *s_w)
241 {
242   if (io_w_state == IO_BLOCK) /* maybe allow data to build up */
243   {
244     if (io_r_state == IO_BLOCK) /* block to either get or put some data */
245       io_block(io_r_fd, io_w_fd);
246     else if (s_w->len > EX_MAX_W_DATA_INCORE)
247       io_block(-1, io_w_fd); /* block to put more data */
248   }
249   else if ((io_w_state == IO_NONE) && (io_r_state == IO_BLOCK))
250     io_block(io_r_fd, -1); /* block to get more data */
251 }
252 #endif
253 #endif
254 
255 /* generic POSIX IO functions that _don't_ call Vstr functions */
256 
257 #if !defined(EX_UTILS_NO_FUNCS) && !defined(EX_UTILS_NO_USE_IO_FD)
io_fd_set_o_nonblock(int fd)258 static int io_fd_set_o_nonblock(int fd)
259 {
260   int flags = 0;
261 
262   /* see if the NONBLOCK flag is set... */
263   if ((flags = fcntl(fd, F_GETFL)) == -1)
264     return (FALSE);
265 
266   /* if it isn't try and add it to the current flags */
267   if (!(flags & O_NONBLOCK) &&
268       (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1))
269     return (FALSE);
270 
271   return (TRUE);
272 }
273 #endif
274 
275 
276 #ifdef VSTR_AUTOCONF_HAVE_OPEN64
277 # define EX_UTILS_OPEN open64
278 #else
279 # define EX_UTILS_OPEN open
280 #endif
281 
282 /* This is inherited from open() on Linux, is it always? */
283 #ifdef __linux__
284 # define OS_INHERITS_NONBLOCK_FROM_OPEN 1
285 #else
286 # define OS_INHERITS_NONBLOCK_FROM_OPEN 0
287 #endif
288 
289 #if !defined(EX_UTILS_NO_FUNCS) && !defined(EX_UTILS_NO_USE_OPEN)
290 # ifndef VSTR_AUTOCONF_HAVE_OPEN64
291 #  define open64 open
292 # endif
io__open(const char * filename,int xflags)293 static int io__open(const char *filename, int xflags)
294 { /* do we alway6s want to do this for fifo's ? */
295   int flags = O_RDONLY | O_NOCTTY | xflags;
296   int fd = EX_UTILS_OPEN(filename, flags);
297 
298   if ((fd == -1) && EX_UTILS_RET_FAIL)
299     return (-1);
300 
301   if (fd == -1)
302     err(EXIT_FAILURE, "open(%s)", filename);
303 
304   /* When doing IO, it should always be non-blocking -- doesn't work
305    * for files, but fd object might be a FIFO etc. */
306   if (!OS_INHERITS_NONBLOCK_FROM_OPEN || !(xflags & O_NONBLOCK))
307     io_fd_set_o_nonblock(fd);
308 
309   return (fd);
310 }
311 #if !defined(EX_UTILS_NO_USE_BLOCK) && !defined(EX_UTILS_NO_USE_BLOCKING_OPEN)
io_open(const char * filename)312 static int io_open(const char *filename)
313 {
314   return (io__open(filename, 0));
315 }
316 #endif
317 #ifdef EX_UTILS_USE_NONBLOCKING_OPEN
io_open_nonblock(const char * filename)318 static int io_open_nonblock(const char *filename)
319 {
320   return (io__open(filename, O_NONBLOCK));
321 }
322 #endif
323 #endif
324 
325 /* ************************ */
326 /* generic helper functions */
327 /* ************************ */
328 
329 #if !defined(EX_UTILS_NO_FUNCS) && !defined(EX_UTILS_NO_USE_INIT)
330 /* Example init function */
ex_init(Vstr_base ** s2)331 static Vstr_base *ex_init(Vstr_base **s2)
332 {
333   Vstr_base *s1 = NULL;
334   struct stat64 stat_buf;
335 
336   if (!vstr_init()) /* init the library */
337     errno = ENOMEM, err(EXIT_FAILURE, "init");
338 
339 
340   /* alter the node buffer size to be whatever the stdout block size is */
341   if (fstat64(1, &stat_buf) == -1)
342   {
343     warn("fstat(STDOUT)");
344     stat_buf.st_blksize = 0;
345   }
346 
347   if (!stat_buf.st_blksize) /* this is allowed to be Zero -- *BSD proc */
348     stat_buf.st_blksize = 4096;
349 
350   if (!vstr_cntl_conf(NULL, VSTR_CNTL_CONF_SET_NUM_BUF_SZ,
351                       stat_buf.st_blksize / 32))
352     warnx("Couldn't alter node size to match block size");
353 
354   /* create strings... */
355   if (!(s1 = vstr_make_base(NULL)) ||
356       (s2 && !(*s2 = vstr_make_base(NULL))))
357     errno = ENOMEM, err(EXIT_FAILURE, "Create string");
358 
359   /* create some data storage for _both_ of the above strings */
360   vstr_make_spare_nodes(NULL, VSTR_TYPE_NODE_BUF, 64);
361 
362   /* Try and make stdout non-blocking, if it is a file this won't do anything */
363   io_fd_set_o_nonblock(STDOUT_FILENO);
364 
365   return (s1);
366 }
367 #endif
368 
369 #if !defined(EX_UTILS_NO_FUNCS) && !defined(EX_UTILS_NO_USE_EXIT)
370 /* Example exit function */
ex_exit(Vstr_base * s1,Vstr_base * s2)371 static int ex_exit(Vstr_base *s1, Vstr_base *s2)
372 {
373   /* These next calls are only really needed for debugging,
374    * in that when they are done any memory leaks can be seen in debugging mode.
375    */
376 
377   /* As with the system free() both of these are ok if passed NULL */
378 
379   /* free s1, our String object */
380   vstr_free_base(s1);
381   /* free s2, our String object */
382   vstr_free_base(s2);
383 
384   /* "exit" Vstr, this free's all internal data and no library calls apart from
385    * vstr_init() should be called after this.
386    */
387   vstr_exit();
388 
389   return (EXIT_SUCCESS);
390 }
391 #endif
392 
393 #endif
394