1 /*
2 * Copyright (c) 2003 Gunnar Ritter
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty. In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute
10 * it freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 * claim that you wrote the original software. If you use this software
14 * in a product, an acknowledgment in the product documentation would be
15 * appreciated but is not required.
16 *
17 * 2. Altered source versions must be plainly marked as such, and must not be
18 * misrepresented as being the original software.
19 *
20 * 3. This notice may not be removed or altered from any source distribution.
21 */
22 /* Sccsid @(#)oblok.c 1.7 (gritter) 7/16/04 */
23
24 #include <sys/types.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30
31 #include "memalign.h"
32 #include "oblok.h"
33
34 struct list {
35 struct list *l_nxt;
36 struct oblok *l_op;
37 };
38
39 static struct list *bloks;
40 static int exitset;
41
42 int
ob_clear(void)43 ob_clear(void)
44 {
45 struct list *lp;
46 int val = 0;
47
48 for (lp = bloks; lp; lp = lp->l_nxt) {
49 if (ob_flush(lp->l_op) < 0)
50 val = -1;
51 else if (val >= 0)
52 val++;
53 }
54 return val;
55 }
56
57 static void
add(struct oblok * op)58 add(struct oblok *op)
59 {
60 struct list *lp, *lq;
61
62 if ((lp = calloc(1, sizeof *lp)) != NULL) {
63 lp->l_nxt = NULL;
64 lp->l_op = op;
65 if (bloks) {
66 for (lq = bloks; lq->l_nxt; lq = lq->l_nxt);
67 lq->l_nxt = lp;
68 } else
69 bloks = lp;
70 if (exitset == 0) {
71 exitset = 1;
72 atexit((void (*)(void))ob_clear);
73 }
74 }
75 }
76
77 static void
del(struct oblok * op)78 del(struct oblok *op)
79 {
80 struct list *lp, *lq = NULL;
81
82 if (bloks) {
83 for (lp = bloks; lp && lp->l_op != op; lp = lp->l_nxt)
84 lq = lp;
85 if (lp) {
86 if (lq)
87 lq->l_nxt = lp->l_nxt;
88 if (lp == bloks)
89 bloks = bloks->l_nxt;
90 free(lp);
91 }
92 }
93 }
94
95 struct oblok *
ob_alloc(int fd,enum ob_mode bf)96 ob_alloc(int fd, enum ob_mode bf)
97 {
98 static long pagesize;
99 struct oblok *op;
100
101 if (pagesize == 0)
102 if ((pagesize = sysconf(_SC_PAGESIZE)) < 0)
103 pagesize = 4096;
104 if ((op = memalign(pagesize, sizeof *op)) == NULL)
105 return NULL;
106 memset(op, 0, sizeof *op);
107 op->ob_fd = fd;
108 switch (bf) {
109 case OB_EBF:
110 op->ob_bf = isatty(fd) ? OB_LBF : OB_FBF;
111 break;
112 default:
113 op->ob_bf = bf;
114 }
115 add(op);
116 return op;
117 }
118
119 ssize_t
ob_free(struct oblok * op)120 ob_free(struct oblok *op)
121 {
122 ssize_t wrt;
123
124 wrt = ob_flush(op);
125 del(op);
126 free(op);
127 return wrt;
128 }
129
130 static ssize_t
swrite(int fd,const char * data,size_t sz)131 swrite(int fd, const char *data, size_t sz)
132 {
133 ssize_t wo, wt = 0;
134
135 do {
136 if ((wo = write(fd, data + wt, sz - wt)) < 0) {
137 if (errno == EINTR)
138 continue;
139 else
140 return wt;
141 }
142 wt += wo;
143 } while (wt < sz);
144 return sz;
145 }
146
147 ssize_t
ob_write(struct oblok * op,const char * data,size_t sz)148 ob_write(struct oblok *op, const char *data, size_t sz)
149 {
150 ssize_t wrt;
151 size_t di, isz;
152
153 switch (op->ob_bf) {
154 case OB_NBF:
155 wrt = swrite(op->ob_fd, data, sz);
156 op->ob_wrt += wrt;
157 if (wrt != sz) {
158 op->ob_bf = OB_EBF;
159 writerr(op, sz, wrt>0?wrt:0);
160 return -1;
161 }
162 return wrt;
163 case OB_LBF:
164 case OB_FBF:
165 isz = sz;
166 while (op->ob_pos + sz > (OBLOK)) {
167 di = (OBLOK) - op->ob_pos;
168 sz -= di;
169 if (op->ob_pos > 0) {
170 memcpy(&op->ob_blk[op->ob_pos], data, di);
171 wrt = swrite(op->ob_fd, op->ob_blk, (OBLOK));
172 } else
173 wrt = swrite(op->ob_fd, data, (OBLOK));
174 op->ob_wrt += wrt;
175 if (wrt != (OBLOK)) {
176 op->ob_bf = OB_EBF;
177 writerr(op, (OBLOK), wrt>0?wrt:0);
178 return -1;
179 }
180 data += di;
181 op->ob_pos = 0;
182 }
183 if (op->ob_bf == OB_LBF) {
184 const char *cp;
185
186 cp = data;
187 while (cp < &data[sz]) {
188 if (*cp == '\n') {
189 di = cp - data + 1;
190 sz -= di;
191 if (op->ob_pos > 0) {
192 memcpy(&op->ob_blk[op->ob_pos],
193 data, di);
194 wrt = swrite(op->ob_fd,
195 op->ob_blk,
196 op->ob_pos + di);
197 } else
198 wrt = swrite(op->ob_fd,
199 data, di);
200 op->ob_wrt += wrt;
201 if (wrt != op->ob_pos + di) {
202 op->ob_bf = OB_EBF;
203 writerr(op, di, wrt>0?wrt:0);
204 return -1;
205 }
206 op->ob_pos = 0;
207 data += di;
208 cp = data;
209 }
210 cp++;
211 }
212 }
213 if (sz == (OBLOK)) {
214 wrt = swrite(op->ob_fd, data, sz);
215 op->ob_wrt += wrt;
216 if (wrt != sz) {
217 op->ob_bf = OB_EBF;
218 writerr(op, sz, wrt>0?wrt:0);
219 return -1;
220 }
221 } else if (sz) {
222 memcpy(&op->ob_blk[op->ob_pos], data, sz);
223 op->ob_pos += sz;
224 }
225 return isz;
226 case OB_EBF:
227 ;
228 }
229 return -1;
230 }
231
232 ssize_t
ob_flush(struct oblok * op)233 ob_flush(struct oblok *op)
234 {
235 ssize_t wrt = 0;
236
237 if (op->ob_pos) {
238 wrt = swrite(op->ob_fd, op->ob_blk, op->ob_pos);
239 op->ob_wrt += wrt;
240 if (wrt != op->ob_pos) {
241 op->ob_bf = OB_EBF;
242 writerr(op, op->ob_pos, wrt>0?wrt:0);
243 wrt = -1;
244 }
245 op->ob_pos = 0;
246 }
247 return wrt;
248 }
249
250 int
ob_chr(int c,struct oblok * op)251 ob_chr(int c, struct oblok *op)
252 {
253 char b;
254 ssize_t wrt;
255
256 b = (char)c;
257 wrt = ob_write(op, &b, 1);
258 return wrt < 0 ? EOF : c;
259 }
260