Commit | Line | Data |
---|---|---|
bf0d695d | 1 | /* $OpenBSD: fvwrite.c,v 1.16 2009/10/22 01:23:16 guenther Exp $ */ |
c0c0989a MJ |
2 | /* |
3 | * SPDX-License-Identifier: BSD-3-Clause | |
4 | * | |
5 | * Copyright (C) 1990, 1993 | |
bf0d695d PMF |
6 | * The Regents of the University of California. All rights reserved. |
7 | * | |
8 | * This code is derived from software contributed to Berkeley by | |
9 | * Chris Torek. | |
bf0d695d PMF |
10 | */ |
11 | ||
12 | #include <stdio.h> | |
13 | #include <stdlib.h> | |
14 | #include <string.h> | |
15 | #include <errno.h> | |
16 | #include "local.h" | |
17 | #include "fvwrite.h" | |
18 | #include "various.h" | |
19 | ||
20 | /* | |
21 | * Write some memory regions. Return zero on success, EOF on error. | |
22 | * | |
23 | * This routine is large and unsightly, but most of the ugliness due | |
24 | * to the three different kinds of output buffering is handled here. | |
25 | */ | |
26 | int | |
002e1fde | 27 | __sfvwrite(LTTNG_UST_LFILE *fp, struct __lttng_ust_suio *uio) |
bf0d695d PMF |
28 | { |
29 | size_t len; | |
30 | char *p; | |
002e1fde | 31 | struct __lttng_ust_siov *iov; |
bf0d695d PMF |
32 | int w, s; |
33 | char *nl; | |
34 | int nlknown, nldist; | |
35 | ||
36 | if ((len = uio->uio_resid) == 0) | |
37 | return (0); | |
38 | /* make sure we can write */ | |
39 | if (cantwrite(fp)) { | |
40 | errno = EBADF; | |
41 | return (EOF); | |
42 | } | |
43 | ||
44 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) | |
45 | #define COPY(n) (void)memcpy((void *)fp->_p, (void *)p, (size_t)(n)) | |
46 | ||
47 | iov = uio->uio_iov; | |
48 | p = iov->iov_base; | |
49 | len = iov->iov_len; | |
50 | iov++; | |
51 | #define GETIOV(extra_work) \ | |
52 | while (len == 0) { \ | |
53 | extra_work; \ | |
54 | p = iov->iov_base; \ | |
55 | len = iov->iov_len; \ | |
56 | iov++; \ | |
57 | } | |
58 | if (fp->_flags & __SNBF) { | |
59 | /* | |
60 | * Unbuffered: write up to BUFSIZ bytes at a time. | |
61 | */ | |
62 | do { | |
63 | GETIOV(;); | |
64 | w = (*fp->_write)(fp->_cookie, p, MIN(len, BUFSIZ)); | |
65 | if (w <= 0) | |
66 | goto err; | |
67 | p += w; | |
68 | len -= w; | |
69 | } while ((uio->uio_resid -= w) != 0); | |
70 | } else if ((fp->_flags & __SLBF) == 0) { | |
71 | /* | |
72 | * Fully buffered: fill partially full buffer, if any, | |
73 | * and then flush. If there is no partial buffer, write | |
74 | * one _bf._size byte chunk directly (without copying). | |
75 | * | |
76 | * String output is a special case: write as many bytes | |
77 | * as fit, but pretend we wrote everything. This makes | |
78 | * snprintf() return the number of bytes needed, rather | |
79 | * than the number used, and avoids its write function | |
80 | * (so that the write function can be invalid). | |
81 | */ | |
82 | do { | |
83 | GETIOV(;); | |
84 | if ((fp->_flags & (__SALC | __SSTR)) == | |
85 | (__SALC | __SSTR) && fp->_w < len) { | |
86 | size_t blen = fp->_p - fp->_bf._base; | |
87 | unsigned char *_base; | |
88 | int _size; | |
89 | ||
90 | /* Allocate space exponentially. */ | |
91 | _size = fp->_bf._size; | |
92 | do { | |
93 | _size = (_size << 1) + 1; | |
94 | } while (_size < blen + len); | |
95 | _base = realloc(fp->_bf._base, _size + 1); | |
96 | if (_base == NULL) | |
97 | goto err; | |
98 | fp->_w += _size - fp->_bf._size; | |
99 | fp->_bf._base = _base; | |
100 | fp->_bf._size = _size; | |
101 | fp->_p = _base + blen; | |
102 | } | |
103 | w = fp->_w; | |
104 | if (fp->_flags & __SSTR) { | |
105 | if (len < w) | |
106 | w = len; | |
107 | COPY(w); /* copy MIN(fp->_w,len), */ | |
108 | fp->_w -= w; | |
109 | fp->_p += w; | |
110 | w = len; /* but pretend copied all */ | |
111 | } else if (fp->_p > fp->_bf._base && len > w) { | |
112 | /* fill and flush */ | |
113 | COPY(w); | |
114 | /* fp->_w -= w; */ /* unneeded */ | |
115 | fp->_p += w; | |
116 | if (ust_safe_fflush(fp)) | |
117 | goto err; | |
118 | } else if (len >= (w = fp->_bf._size)) { | |
119 | /* write directly */ | |
120 | w = (*fp->_write)(fp->_cookie, p, w); | |
121 | if (w <= 0) | |
122 | goto err; | |
123 | } else { | |
124 | /* fill and done */ | |
125 | w = len; | |
126 | COPY(w); | |
127 | fp->_w -= w; | |
128 | fp->_p += w; | |
129 | } | |
130 | p += w; | |
131 | len -= w; | |
132 | } while ((uio->uio_resid -= w) != 0); | |
133 | } else { | |
134 | /* | |
135 | * Line buffered: like fully buffered, but we | |
136 | * must check for newlines. Compute the distance | |
137 | * to the first newline (including the newline), | |
138 | * or `infinity' if there is none, then pretend | |
139 | * that the amount to write is MIN(len,nldist). | |
140 | */ | |
141 | nlknown = 0; | |
142 | nldist = 0; /* XXX just to keep gcc happy */ | |
143 | do { | |
144 | GETIOV(nlknown = 0); | |
145 | if (!nlknown) { | |
146 | nl = memchr((void *)p, '\n', len); | |
147 | nldist = nl ? nl + 1 - p : len + 1; | |
148 | nlknown = 1; | |
149 | } | |
150 | s = MIN(len, nldist); | |
151 | w = fp->_w + fp->_bf._size; | |
152 | if (fp->_p > fp->_bf._base && s > w) { | |
153 | COPY(w); | |
154 | /* fp->_w -= w; */ | |
155 | fp->_p += w; | |
156 | if (ust_safe_fflush(fp)) | |
157 | goto err; | |
158 | } else if (s >= (w = fp->_bf._size)) { | |
159 | w = (*fp->_write)(fp->_cookie, p, w); | |
160 | if (w <= 0) | |
161 | goto err; | |
162 | } else { | |
163 | w = s; | |
164 | COPY(w); | |
165 | fp->_w -= w; | |
166 | fp->_p += w; | |
167 | } | |
168 | if ((nldist -= w) == 0) { | |
169 | /* copied the newline: flush and forget */ | |
170 | if (ust_safe_fflush(fp)) | |
171 | goto err; | |
172 | nlknown = 0; | |
173 | } | |
174 | p += w; | |
175 | len -= w; | |
176 | } while ((uio->uio_resid -= w) != 0); | |
177 | } | |
178 | return (0); | |
179 | ||
180 | err: | |
181 | fp->_flags |= __SERR; | |
182 | return (EOF); | |
183 | } |