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