/* * Copyright (c) 2003 Gunnar Ritter * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute * it freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. */ /* Sccsid @(#)oblok.c 1.7 (gritter) 7/16/04 */ #include #include #include #include #include #include #include #include "memalign.h" #include "oblok.h" struct list { struct list *l_nxt; struct oblok *l_op; }; static struct list *bloks; static int exitset; int ob_clear(void) { struct list *lp; int val = 0; for (lp = bloks; lp; lp = lp->l_nxt) { if (ob_flush(lp->l_op) < 0) val = -1; else if (val >= 0) val++; } return val; } static void add(struct oblok *op) { struct list *lp, *lq; if ((lp = calloc(1, sizeof *lp)) != NULL) { lp->l_nxt = NULL; lp->l_op = op; if (bloks) { for (lq = bloks; lq->l_nxt; lq = lq->l_nxt); lq->l_nxt = lp; } else bloks = lp; if (exitset == 0) { exitset = 1; atexit((void (*)(void))ob_clear); } } } static void del(struct oblok *op) { struct list *lp, *lq = NULL; if (bloks) { for (lp = bloks; lp && lp->l_op != op; lp = lp->l_nxt) lq = lp; if (lp) { if (lq) lq->l_nxt = lp->l_nxt; if (lp == bloks) bloks = bloks->l_nxt; free(lp); } } } struct oblok * ob_alloc(int fd, enum ob_mode bf) { static long pagesize; struct oblok *op; if (pagesize == 0) if ((pagesize = sysconf(_SC_PAGESIZE)) < 0) pagesize = 4096; if ((op = memalign(pagesize, sizeof *op)) == NULL) return NULL; memset(op, 0, sizeof *op); op->ob_fd = fd; switch (bf) { case OB_EBF: op->ob_bf = isatty(fd) ? OB_LBF : OB_FBF; break; default: op->ob_bf = bf; } add(op); return op; } ssize_t ob_free(struct oblok *op) { ssize_t wrt; wrt = ob_flush(op); del(op); free(op); return wrt; } static ssize_t swrite(int fd, const char *data, size_t sz) { ssize_t wo, wt = 0; do { if ((wo = write(fd, data + wt, sz - wt)) < 0) { if (errno == EINTR) continue; else return wt; } wt += wo; } while (wt < sz); return sz; } ssize_t ob_write(struct oblok *op, const char *data, size_t sz) { ssize_t wrt; size_t di, isz; switch (op->ob_bf) { case OB_NBF: wrt = swrite(op->ob_fd, data, sz); op->ob_wrt += wrt; if (wrt != sz) { op->ob_bf = OB_EBF; writerr(op, sz, wrt>0?wrt:0); return -1; } return wrt; case OB_LBF: case OB_FBF: isz = sz; while (op->ob_pos + sz > (OBLOK)) { di = (OBLOK) - op->ob_pos; sz -= di; if (op->ob_pos > 0) { memcpy(&op->ob_blk[op->ob_pos], data, di); wrt = swrite(op->ob_fd, op->ob_blk, (OBLOK)); } else wrt = swrite(op->ob_fd, data, (OBLOK)); op->ob_wrt += wrt; if (wrt != (OBLOK)) { op->ob_bf = OB_EBF; writerr(op, (OBLOK), wrt>0?wrt:0); return -1; } data += di; op->ob_pos = 0; } if (op->ob_bf == OB_LBF) { const char *cp; cp = data; while (cp < &data[sz]) { if (*cp == '\n') { di = cp - data + 1; sz -= di; if (op->ob_pos > 0) { memcpy(&op->ob_blk[op->ob_pos], data, di); wrt = swrite(op->ob_fd, op->ob_blk, op->ob_pos + di); } else wrt = swrite(op->ob_fd, data, di); op->ob_wrt += wrt; if (wrt != op->ob_pos + di) { op->ob_bf = OB_EBF; writerr(op, di, wrt>0?wrt:0); return -1; } op->ob_pos = 0; data += di; cp = data; } cp++; } } if (sz == (OBLOK)) { wrt = swrite(op->ob_fd, data, sz); op->ob_wrt += wrt; if (wrt != sz) { op->ob_bf = OB_EBF; writerr(op, sz, wrt>0?wrt:0); return -1; } } else if (sz) { memcpy(&op->ob_blk[op->ob_pos], data, sz); op->ob_pos += sz; } return isz; case OB_EBF: ; } return -1; } ssize_t ob_flush(struct oblok *op) { ssize_t wrt = 0; if (op->ob_pos) { wrt = swrite(op->ob_fd, op->ob_blk, op->ob_pos); op->ob_wrt += wrt; if (wrt != op->ob_pos) { op->ob_bf = OB_EBF; writerr(op, op->ob_pos, wrt>0?wrt:0); wrt = -1; } op->ob_pos = 0; } return wrt; } int ob_chr(int c, struct oblok *op) { char b; ssize_t wrt; b = (char)c; wrt = ob_write(op, &b, 1); return wrt < 0 ? EOF : c; }