/************************************************************************************************* * Implementation of Curia * Copyright (C) 2000-2003 Mikio Hirabayashi * This file is part of QDBM, Quick Database Manager. * QDBM is free software; you can redistribute it and/or modify it under the terms of the GNU * Lesser General Public License as published by the Free Software Foundation; either version * 2.1 of the License or any later version. QDBM is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * You should have received a copy of the GNU Lesser General Public License along with QDBM; if * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA. *************************************************************************************************/ #include "curia.h" #include "myconf.h" #define CR_NAMEMAX 512 /* max size of a database name */ #define CR_DPMAX 512 /* max number of division of a database */ #define CR_DIRMODE 00755 /* permission of a creating directory */ #define CR_FILEMODE 00644 /* permission of a creating file */ #define CR_PATHBUFSIZ 1024 /* size of a path buffer */ #define CR_DEFDNUM 5 /* default number of division of a database */ #define CR_ATTRBNUM 16 /* bucket number of attrubute database */ #define CR_DPNAME "depot" /* name of each sub database */ #define CR_KEYEND "end" /* key of endian */ #define CR_KEYDNUM "dnum" /* key of division number */ #define CR_KEYLRNUM "lrnum" /* key of the number of large objects */ #define CR_LOBDIR "lob" /* name of the directory of large objects */ #define CR_LOBDDEPTH 2 /* depth of the directories of large objects */ /* private function prototypes */ static int crbigendian(void); static int crbyteswap(int num); static char *crstrdup(const char *str); static int crdpgetnum(DEPOT *depot, const char *kbuf, int ksiz); static char *crgetlobpath(CURIA *curia, const char *kbuf, int ksiz); static int crmklobdir(const char *path); static int crrmlobdir(const char *path); static int crwrite(int fd, const void *buf, int size); static int crread(int fd, void *buf, int size); /************************************************************************************************* * public objects *************************************************************************************************/ /* Get a database handle. */ CURIA *cropen(const char *name, int omode, int bnum, int dnum){ DEPOT *attr, **depots; char path[CR_PATHBUFSIZ], *tname, end; int i, j, dpomode, inode, lrnum; struct stat sbuf; CURIA *curia; assert(name); if(dnum < 1) dnum = CR_DEFDNUM; if(dnum > CR_DPMAX) dnum = CR_DPMAX; if(strlen(name) > CR_NAMEMAX){ dpecode = DP_EMISC; return NULL; } dpomode = DP_OREADER; if(omode & CR_OWRITER){ dpomode = DP_OWRITER; if(omode & CR_OCREAT) dpomode |= DP_OCREAT; if(omode & CR_OTRUNC) dpomode |= DP_OTRUNC; } if(omode & CR_ONOLCK) dpomode |= DP_ONOLCK; attr = NULL; lrnum = 0; if((omode & CR_OWRITER) && (omode & CR_OCREAT)){ if(mkdir(name, CR_DIRMODE) == -1 && errno != EEXIST){ dpecode = DP_EMKDIR; return NULL; } for(i = 0; i < dnum; i++){ sprintf(path, "%s%c%04d", name, MYPATHCHR, i + 1); if(mkdir(path, CR_DIRMODE) == -1 && errno != EEXIST){ dpecode = DP_EMKDIR; return NULL; } } sprintf(path, "%s%c%s", name, MYPATHCHR, CR_DPNAME); if(!(attr = dpopen(path, dpomode, CR_ATTRBNUM))) return NULL; end = crbigendian(); if(!dpput(attr, CR_KEYEND, -1, &end, 1, DP_DOVER) || !dpput(attr, CR_KEYDNUM, -1, (char *)&dnum, sizeof(int), DP_DOVER)){ dpclose(attr); return NULL; } } if(!attr){ sprintf(path, "%s%c%s", name, MYPATHCHR, CR_DPNAME); if(!(attr = dpopen(path, dpomode, 1))) return NULL; if(!(omode & CR_OTRUNC)){ if((dnum = crdpgetnum(attr, CR_KEYDNUM, -1)) < 1 || (lrnum = crdpgetnum(attr, CR_KEYLRNUM, -1)) < 0){ dpclose(attr); dpecode = DP_EBROKEN; return NULL; } } } if(omode & CR_OTRUNC){ for(i = 0; i < CR_DPMAX; i++){ sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, i + 1, MYPATHCHR, CR_DPNAME); if(unlink(path) == -1 && errno != ENOENT){ dpclose(attr); dpecode = DP_EUNLINK; return NULL; } sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, i + 1, MYPATHCHR, CR_LOBDIR); if(!crrmlobdir(path)){ dpclose(attr); return NULL; } if(i >= dnum){ sprintf(path, "%s%c%04d", name, MYPATHCHR, i + 1); if(rmdir(path) == -1 && errno != ENOENT){ dpclose(attr); dpecode = DP_ERMDIR; return NULL; } } } errno = 0; } if(stat(name, &sbuf) == -1){ dpclose(attr); dpecode = DP_ESTAT; return NULL; } inode = sbuf.st_ino; if(!(depots = malloc(dnum * sizeof(DEPOT *)))){ dpclose(attr); dpecode = DP_EALLOC; return NULL; } for(i = 0; i < dnum; i++){ sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, i + 1, MYPATHCHR, CR_DPNAME); if(!(depots[i] = dpopen(path, dpomode, bnum))){ for(j = 0; j < i; j++){ dpclose(depots[j]); } free(depots); dpclose(attr); return NULL; } } curia = malloc(sizeof(CURIA)); tname = crstrdup(name); if(!curia || !tname){ free(curia); free(tname); for(i = 0; i < dnum; i++){ dpclose(depots[i]); } free(depots); dpclose(attr); dpecode = DP_EALLOC; return NULL; } curia->name = tname; curia->wmode = (omode & CR_OWRITER); curia->inode = inode; curia->attr = attr; curia->depots = depots; curia->dnum = dnum; curia->inum = 0; curia->lrnum = lrnum; return curia; } /* Close a database handle. */ int crclose(CURIA *curia){ int i, err; assert(curia); err = FALSE; for(i = 0; i < curia->dnum; i++){ if(!dpclose(curia->depots[i])) err = TRUE; } free(curia->depots); if(curia->wmode){ if(!dpput(curia->attr, CR_KEYLRNUM, -1, (char *)&(curia->lrnum), sizeof(int), DP_DOVER)) err = TRUE; } if(!dpclose(curia->attr)) err = TRUE; free(curia->name); free(curia); return err ? FALSE : TRUE; } /* Store a record. */ int crput(CURIA *curia, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode){ int dpdmode; int tnum; assert(curia && kbuf && vbuf); if(!curia->wmode){ dpecode = DP_EMODE; return FALSE; } switch(dmode){ case CR_DKEEP: dpdmode = DP_DKEEP; break; case CR_DCAT: dpdmode = DP_DCAT; break; default: dpdmode = DP_DOVER; break; } tnum = dpouterhash(kbuf, ksiz) % curia->dnum; return dpput(curia->depots[tnum], kbuf, ksiz, vbuf, vsiz, dpdmode); } /* Delete a record. */ int crout(CURIA *curia, const char *kbuf, int ksiz){ int tnum; assert(curia && kbuf); if(!curia->wmode){ dpecode = DP_EMODE; return FALSE; } tnum = dpouterhash(kbuf, ksiz) % curia->dnum; return dpout(curia->depots[tnum], kbuf, ksiz); } /* Retrieve a record. */ char *crget(CURIA *curia, const char *kbuf, int ksiz, int start, int max, int *sp){ int tnum; assert(curia && kbuf && start >= 0); tnum = dpouterhash(kbuf, ksiz) % curia->dnum; return dpget(curia->depots[tnum], kbuf, ksiz, start, max, sp); } /* Get the size of the value of a record. */ int crvsiz(CURIA *curia, const char *kbuf, int ksiz){ int tnum; assert(curia && kbuf); tnum = dpouterhash(kbuf, ksiz) % curia->dnum; return dpvsiz(curia->depots[tnum], kbuf, ksiz); } /* Initialize the iterator of a database handle. */ int criterinit(CURIA *curia){ int i, err; assert(curia); err = FALSE; for(i = 0; i < curia->dnum; i++){ if(!dpiterinit(curia->depots[i])){ err = TRUE; break; } } curia->inum = 0; return err ? FALSE : TRUE; } /* Get the next key of the iterator. */ char *criternext(CURIA *curia, int *sp){ char *kbuf; assert(curia); kbuf = NULL; while(curia->inum < curia->dnum && !(kbuf = dpiternext(curia->depots[curia->inum], sp))){ if(dpecode != DP_ENOITEM) return NULL; (curia->inum)++; } return kbuf; } /* Set alignment of a database handle. */ int crsetalign(CURIA *curia, int align){ int i, err; assert(curia); if(!curia->wmode){ dpecode = DP_EMODE; return FALSE; } err = FALSE; for(i = 0; i < curia->dnum; i++){ if(!dpsetalign(curia->depots[i], align)){ err = TRUE; break; } } return err ? FALSE : TRUE; } /* Synchronize contents of updating a database with the files and the devices. */ int crsync(CURIA *curia){ int i, err; assert(curia); if(!curia->wmode){ dpecode = DP_EMODE; return FALSE; } err = FALSE; if(!dpput(curia->attr, CR_KEYLRNUM, -1, (char *)&(curia->lrnum), sizeof(int), DP_DOVER) || !dpsync(curia->attr)) err = TRUE; for(i = 0; i < curia->dnum; i++){ if(!dpsync(curia->depots[i])){ err = TRUE; break; } } return err ? FALSE : TRUE; } /* Optimize a database. */ int croptimize(CURIA *curia, int bnum){ int i, err; assert(curia); if(!curia->wmode){ dpecode = DP_EMODE; return FALSE; } err = FALSE; for(i = 0; i < curia->dnum; i++){ if(!dpoptimize(curia->depots[i], bnum)){ err = TRUE; break; } } curia->inum = 0; return err ? FALSE : TRUE; } /* Get the name of a database. */ char *crname(CURIA *curia){ char *name; assert(curia); if(!(name = crstrdup(curia->name))){ dpecode = DP_EALLOC; return NULL; } return name; } /* Get the total size of database files. */ int crfsiz(CURIA *curia){ int i, sum, rv; assert(curia); sum = 0; for(i = 0; i < curia->dnum; i++){ rv = dpfsiz(curia->depots[i]); if(rv == -1) return -1; sum += rv; } return sum; } /* Get the total number of the elements of each bucket array. */ int crbnum(CURIA *curia){ int i, sum, rv; assert(curia); sum = 0; for(i = 0; i < curia->dnum; i++){ rv = dpbnum(curia->depots[i]); if(rv == -1) return -1; sum += rv; } return sum; } /* Get the total number of the used elements of each bucket array. */ int crbusenum(CURIA *curia){ int i, sum, rv; assert(curia); sum = 0; for(i = 0; i < curia->dnum; i++){ rv = dpbusenum(curia->depots[i]); if(rv == -1) return -1; sum += rv; } return sum; } /* Get the number of the records stored in a database. */ int crrnum(CURIA *curia){ int i, sum, rv; assert(curia); sum = 0; for(i = 0; i < curia->dnum; i++){ rv = dprnum(curia->depots[i]); if(rv == -1) return -1; sum += rv; } return sum; } /* Check whether a database handle is a writer or not. */ int crwritable(CURIA *curia){ assert(curia); return curia->wmode; } /* Check whether a database has a fatal error or not. */ int crfatalerror(CURIA *curia){ int i; assert(curia); if(dpfatalerror(curia->attr)) return TRUE; for(i = 0; i < curia->dnum; i++){ if(dpfatalerror(curia->depots[i])) return TRUE; } return FALSE; } /* Get the inode number of a database directory. */ int crinode(CURIA *curia){ assert(curia); return curia->inode; } /* Remove a database directory. */ int crremove(const char *name){ struct stat sbuf; CURIA *curia; char path[CR_PATHBUFSIZ]; assert(name); if(stat(name, &sbuf) == -1){ dpecode = DP_ESTAT; return FALSE; } if((curia = cropen(name, CR_OWRITER | CR_OTRUNC, 1, 1)) != NULL) crclose(curia); sprintf(path, "%s%c0001%c%s", name, MYPATHCHR, MYPATHCHR, CR_DPNAME); dpremove(path); sprintf(path, "%s%c0001", name, MYPATHCHR); if(rmdir(path) == -1){ dpecode = DP_ERMDIR; return FALSE; } sprintf(path, "%s%c%s", name, MYPATHCHR, CR_DPNAME); if(!dpremove(path)) return FALSE; if(rmdir(name) == -1){ dpecode = DP_ERMDIR; return FALSE; } return TRUE; } /* Convert a database directory for another platform with different byte order. */ int creconv(const char *name, int big){ int i, mybig, dnum, lrnum, vsiz, loop; char path[CR_PATHBUFSIZ], fbig, *vbuf; DEPOT *depot; assert(name); mybig = crbigendian(); sprintf(path, "%s%c%s", name, MYPATHCHR, CR_DPNAME); if(!dpeconv(path, mybig)) return FALSE; if(!(depot = dpopen(path, DP_OWRITER, -1))) return FALSE; if(!(vbuf = dpget(depot, CR_KEYEND, strlen(CR_KEYEND), 0, -1, &vsiz)) || vsiz != 1){ free(vbuf); dpclose(depot); return FALSE; } fbig = *vbuf; free(vbuf); if(!(vbuf = dpget(depot, CR_KEYDNUM, strlen(CR_KEYDNUM), 0, -1, &vsiz)) || vsiz != sizeof(int)){ free(vbuf); dpclose(depot); return FALSE; } dnum = *(int *)vbuf; free(vbuf); if(!(vbuf = dpget(depot, CR_KEYLRNUM, strlen(CR_KEYLRNUM), 0, -1, &vsiz)) || vsiz != sizeof(int)){ free(vbuf); dpclose(depot); return FALSE; } lrnum = *(int *)vbuf; free(vbuf); if(mybig ? !fbig : fbig){ dnum = crbyteswap(dnum); lrnum = crbyteswap(lrnum); } loop = dnum; if(mybig ? !big : big){ dnum = crbyteswap(dnum); lrnum = crbyteswap(lrnum); } fbig = big; if(!dpput(depot, CR_KEYEND, strlen(CR_KEYEND), &fbig, 1, DP_DOVER) || !dpput(depot, CR_KEYDNUM, strlen(CR_KEYDNUM), (char *)&dnum, sizeof(int), DP_DOVER) || !dpput(depot, CR_KEYLRNUM, strlen(CR_KEYLRNUM), (char *)&lrnum, sizeof(int), DP_DOVER)){ dpclose(depot); return FALSE; } if(!dpclose(depot)) return FALSE; if(!dpeconv(path, big)) return FALSE; for(i = 1; i <= loop; i++){ sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, i, MYPATHCHR, CR_DPNAME); if(!dpeconv(path, big)) return FALSE; } return TRUE; } /* Store a large object. */ int crputlob(CURIA *curia, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode){ char *path; int mode, fd, err, be; struct stat sbuf; assert(curia && kbuf && vbuf); if(!curia->wmode){ dpecode = DP_EMODE; return FALSE; } if(ksiz < 0) ksiz = strlen(kbuf); if(vsiz < 0) vsiz = strlen(vbuf); if(!(path = crgetlobpath(curia, kbuf, ksiz))) return FALSE; if(!crmklobdir(path)){ free(path); return FALSE; } be = stat(path, &sbuf) != -1 && S_ISREG(sbuf.st_mode); mode = O_RDWR | O_CREAT; if(dmode & CR_DKEEP) mode |= O_EXCL; if(dmode & CR_DCAT){ mode |= O_APPEND; } else { mode |= O_TRUNC; } if((fd = open(path, mode, CR_FILEMODE)) == -1){ free(path); dpecode = DP_EOPEN; if(dmode == CR_DKEEP) dpecode = DP_EKEEP; return FALSE; } free(path); err = FALSE; if(crwrite(fd, vbuf, vsiz) == -1){ err = TRUE; dpecode = DP_EWRITE; } if(close(fd) == -1){ err = TRUE; dpecode = DP_ECLOSE; } if(!err && !be) (curia->lrnum)++; return err ? FALSE : TRUE; } /* Delete a large object. */ int croutlob(CURIA *curia, const char *kbuf, int ksiz){ char *path; int err, be; struct stat sbuf; assert(curia && kbuf); if(!curia->wmode){ dpecode = DP_EMODE; return FALSE; } if(ksiz < 0) ksiz = strlen(kbuf); if(!(path = crgetlobpath(curia, kbuf, ksiz))) return FALSE; be = stat(path, &sbuf) != -1 && S_ISREG(sbuf.st_mode); err = FALSE; if(unlink(path) == -1){ err = TRUE; dpecode = DP_ENOITEM; } free(path); if(!err && be) (curia->lrnum)--; return err ? FALSE : TRUE; } /* Retrieve a large object. */ char *crgetlob(CURIA *curia, const char *kbuf, int ksiz, int start, int max, int *sp){ char *path, *buf; struct stat sbuf; int fd, size; assert(curia && kbuf && start >= 0); if(ksiz < 0) ksiz = strlen(kbuf); if(!(path = crgetlobpath(curia, kbuf, ksiz))) return NULL; if((fd = open(path, O_RDONLY, CR_FILEMODE)) == -1){ free(path); dpecode = DP_ENOITEM; return NULL; } free(path); if(fstat(fd, &sbuf) == -1){ close(fd); dpecode = DP_ESTAT; return NULL; } if(start > sbuf.st_size){ close(fd); dpecode = DP_ENOITEM; return NULL; } if(lseek(fd, start, SEEK_SET) == -1){ close(fd); dpecode = DP_ESEEK; return NULL; } if(max < 0) max = sbuf.st_size; if(!(buf = malloc(max + 1))){ close(fd); dpecode = DP_EALLOC; return NULL; } size = crread(fd, buf, max); close(fd); if(size == -1){ free(buf); dpecode = DP_EREAD; return NULL; } buf[size] = '\0'; if(sp) *sp = size; return buf; } /* Get the size of the value of a large object. */ int crvsizlob(CURIA *curia, const char *kbuf, int ksiz){ char *path; struct stat sbuf; assert(curia && kbuf); if(ksiz < 0) ksiz = strlen(kbuf); if(!(path = crgetlobpath(curia, kbuf, ksiz))) return -1; if(stat(path, &sbuf) == -1){ free(path); dpecode = DP_ENOITEM; return -1; } free(path); return sbuf.st_size; } /* Get the number of the large objects stored in a database. */ int crrnumlob(CURIA *curia){ assert(curia); return curia->lrnum; } /************************************************************************************************* * Functions for Experts *************************************************************************************************/ /* Synchronize updating contents on memory. */ int crmemsync(CURIA *curia){ int i, err; assert(curia); if(!curia->wmode){ dpecode = DP_EMODE; return FALSE; } err = FALSE; if(!dpput(curia->attr, CR_KEYLRNUM, -1, (char *)&(curia->lrnum), sizeof(int), DP_DOVER) || !dpmemsync(curia->attr)) err = TRUE; for(i = 0; i < curia->dnum; i++){ if(!dpmemsync(curia->depots[i])){ err = TRUE; break; } } return err ? FALSE : TRUE; } /************************************************************************************************* * private objects *************************************************************************************************/ /* Check whether the byte order of the platform is big endian or not. The return value is true if bigendian, else, it is false. */ static int crbigendian(void){ char buf[sizeof(int)]; *(int *)buf = 1; return !buf[0]; } /* Get a byte-reversed integer. `num' specifies an original integer. The return value is the swapped integer of the original integer. */ static int crbyteswap(int num){ char buf[sizeof(int)]; int i; for(i = 0; i < sizeof(int); i++){ buf[i] = ((char *)&num)[sizeof(int)-i-1]; } return *(int *)buf; } /* Get a copied string. `str' specifies an original string. The return value is a copied string whose region is allocated by `malloc'. */ static char *crstrdup(const char *str){ int len; char *buf; assert(str); len = strlen(str); if(!(buf = malloc(len + 1))) return NULL; memcpy(buf, str, len + 1); return buf; } /* Get an integer from a database. `depot' specifies an inner database handle. `kbuf' specifies the pointer to the region of a key. `ksiz' specifies the size of the key. The return value is the integer of the corresponding record. */ static int crdpgetnum(DEPOT *depot, const char *kbuf, int ksiz){ char *vbuf; int vsiz, rv; if(!(vbuf = dpget(depot, kbuf, ksiz, 0, -1, &vsiz)) || vsiz != sizeof(int)){ free(vbuf); return INT_MIN; } rv = *(int *)vbuf; free(vbuf); return rv; } /* Get the path of a large object. `curia' specifies a database handle. `kbuf' specifies the pointer to the region of a key. `ksiz' specifies the size of the key. The return value is a path string whose region is allocated by `malloc'. */ static char *crgetlobpath(CURIA *curia, const char *kbuf, int ksiz){ char prefix[CR_PATHBUFSIZ], *wp, *path; int i, hash; assert(curia && kbuf && ksiz >= 0); wp = prefix; wp += sprintf(wp, "%s%c%04d%c%s%c", curia->name, MYPATHCHR, dpouterhash(kbuf, ksiz) % curia->dnum + 1, MYPATHCHR, CR_LOBDIR, MYPATHCHR); hash = dpinnerhash(kbuf, ksiz); for(i = 0; i < CR_LOBDDEPTH; i++){ wp += sprintf(wp, "%02X%c", hash % 0x100, MYPATHCHR); hash /= 0x100; } if(!(path = malloc(strlen(prefix) + ksiz * 2 + 1))){ dpecode = DP_EALLOC; return NULL; } wp = path; wp += sprintf(path, "%s", prefix); for(i = 0; i < ksiz; i++){ wp += sprintf(wp, "%02X", ((unsigned char *)kbuf)[i]); } return path; } /* Create directories included in a path. `path' specifies a path. The return value is true if successful, else, it is false. */ static int crmklobdir(const char *path){ char elem[CR_PATHBUFSIZ], *wp; const char *dp; int err, len; wp = elem; err = FALSE; while(*path != '\0' && (dp = strchr(path, MYPATHCHR)) != NULL){ len = dp - path; if(wp != elem) wp += sprintf(wp, "%c", MYPATHCHR); memcpy(wp, path, len); wp[len] = '\0'; wp += len; if(mkdir(elem, CR_DIRMODE) == -1 && errno != EEXIST) err = TRUE; path = dp + 1; } if(err) dpecode = DP_EMKDIR; return err ? FALSE : TRUE; } /* Remofe file and directories under a directory. `path' specifies a path. The return value is true if successful, else, it is false. */ static int crrmlobdir(const char *path){ char elem[CR_PATHBUFSIZ]; DIR *DD; struct dirent *dp; assert(path); if(unlink(path) != -1){ return TRUE; } else { if(errno == ENOENT) return TRUE; if(!(DD = opendir(path))){ dpecode = DP_EMISC; return FALSE; } while((dp = readdir(DD)) != NULL){ if(!strcmp(dp->d_name, MYCDIRSTR) || !strcmp(dp->d_name, MYPDIRSTR)) continue; sprintf(elem, "%s%c%s", path, MYPATHCHR, dp->d_name); if(!crrmlobdir(elem)){ closedir(DD); return FALSE; } } } if(closedir(DD) == -1){ dpecode = DP_EMISC; return FALSE; } if(rmdir(path) == -1){ dpecode = DP_ERMDIR; return FALSE; } return TRUE; } /* Write into a file. `fd' specifies a file descriptor. `buf' specifies a buffer to write. `size' specifies the size of the buffer. The return value is the size of the written buffer, or, -1 on failure. */ static int crwrite(int fd, const void *buf, int size){ char *lbuf; int rv, wb; assert(fd >= 0 && buf && size >= 0); lbuf = (char *)buf; rv = 0; do { wb = write(fd, lbuf, size); switch(wb){ case -1: if(errno != EINTR) return -1; case 0: break; default: lbuf += wb; size -= wb; rv += wb; break; } } while(size > 0); return rv; } /* Read from a file and store the data into a buffer. `fd' specifies a file descriptor. `buffer' specifies a buffer to store into. `size' specifies the size to read with. The return value is the size read with, or, -1 on failure. */ static int crread(int fd, void *buf, int size){ char *lbuf; int i, bs; assert(fd >= 0 && buf && size >= 0); lbuf = buf; for(i = 0; i < size && (bs = read(fd, lbuf + i, size - i)) != 0; i += bs){ if(bs == -1 && errno != EINTR) return -1; } return i; } /* END OF FILE */