ref: b548687a8ed1d0a159c9d3f3f921d93bbb56908e
dir: /os/port/devbin.c/
#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #define WHICHFILE(q) ((q).path) #define SOURCEFD(q) ((q).vers) int debug = 0; /* An fd created here might be used by different processes at the same time if the processes are sharing the Fgrp. Hence, the need for locks. TODO add a ctl file to add searchers at run time (format: name <end byte>\n) */ /* len is the space available in the reader's buffer For now, assuming that the delimiter is 1 char. */ typedef struct Searcher Searcher; struct Searcher { u32 whichfile; char name[KNAMELEN]; /* function pointer to identify the content to send */ s32 (*contentfn)(u8 *readp, u8 *writep, u8 **startp, s32 maxn, u8 **nextreadp); }; QLock lexlock; typedef struct Bin Bin; struct Bin { QLock; Ref; u32 sourcefd; Chan *sourcechan; /* for reading more data, instead of using fd2chan() for each read() */ u8 *buf, *readp, *writep; s8 eof; u32 bufsize; u64 off; }; typedef struct Binreader Binreader; struct Binreader { Bin *bin; Searcher *searcher; }; enum { Qtopdir = 0, Qfddir, Qbufferlength, /* show the remaining bytes in the buffer */ Qbuffer, Qword, Qline, Qdoublequote, Qcloseparen, }; static s32 wordfn(u8 *readp, u8 *writep, u8 **startp, s32 maxn, u8 **nextreadp); static s32 linefn(u8 *readp, u8 *writep, u8 **startp, s32 maxn, u8 **nextreadp); static s32 doublequotefn(u8 *readp, u8 *writep, u8 **startp, s32 maxn, u8 **nextreadp); static s32 closeparenfn(u8 *readp, u8 *writep, u8 **startp, s32 maxn, u8 **nextreadp); static s32 readmax(u8 *readp, u8 *writep, u8 **startp, s32 maxn, u8 **nextreadp); u16 bintype = 0; Searcher searchers[] = { Qbufferlength, "bufferlength", readmax, /* irrelevant function, not used */ Qbuffer, "buffer", readmax, /* to read contents without a delimiter, such as the last non-delimited content */ Qword, "word", wordfn, Qline, "line", linefn, Qdoublequote, "doublequote", doublequotefn, Qcloseparen, "closeparen", closeparenfn, }; static Chan* binattach(char *spec) { Chan *c; c = devattach('n', spec); mkqid(&c->qid, Qtopdir, 0, QTDIR); return c; } static int bingen(Chan *c, char*, Dirtab *, int, int i, Dir *dp) { Qid q; u32 nsourcefds, nfd, ns; char fdname[5]; Searcher *l; /* * if I do .. from #n or #n/0 */ if(i == DEVDOTDOT){ devdir(c, c->qid, "#n", 0, eve, 0555, dp); return 1; } if(WHICHFILE(c->qid) == Qtopdir){ if(i > up->fgrp->maxfd) return -1; nsourcefds = 0; for(nfd = 0; nfd <= up->fgrp->maxfd; nfd++){ if(up->fgrp->fd[nfd] != nil && up->fgrp->fd[nfd]->type != bintype && (up->fgrp->fd[nfd]->mode == OREAD || up->fgrp->fd[nfd]->mode == ORDWR)){ if(nsourcefds == i){ snprint(fdname,5,"%d",nfd); mkqid(&q, Qfddir, nfd, QTDIR); /* source fd in qid.vers */ devdir(c, q, fdname, 0, eve, 0555, dp); return 1; } nsourcefds++; } } return -1; } /* within an fd's dir, show the list of searchers as files */ ns = 0; for(l = searchers; l != nil; l++, ns++){ if(i == ns){ mkqid(&q, l->whichfile, c->qid.vers, QTFILE); devdir(c, q, l->name, 0, eve, 0400, dp); return 1; } } return -1; } static Walkqid* binwalk(Chan *c, Chan *nc, char **name, int nname) { Walkqid *wq; wq = devwalk(c, nc, name, nname, nil, 0, bingen); return wq; } static s32 binstat(Chan *c, uchar *db, s32 n) { int i; i = devstat(c, db, n, nil, 0, bingen); return i; } /* * check all the existing fd's for any open to find this sourcefd. * if found, use that for the Bin */ static Chan* binopen(Chan *c, u32 omode) { Bin *bin; u32 sourcefd, nfd; Binreader *br; Searcher *s; u64 whichfile; if(c->qid.type & QTDIR){ if(omode != OREAD) error(Ebadarg); c->mode = omode; c->flag |= COPEN; c->offset = 0; return c; } if(omode != OREAD) error(Ebadarg); if(WHICHFILE(c->qid) <= Qfddir) error(Ebadarg); sourcefd = SOURCEFD(c->qid); if(sourcefd > up->fgrp->maxfd) error(Ebadarg); if(up->fgrp->fd[sourcefd] == nil || up->fgrp->fd[sourcefd]->type == bintype) error(Ebadarg); if(up->fgrp->fd[sourcefd]->mode != OREAD && up->fgrp->fd[sourcefd]->mode != ORDWR) error(Ebadarg); /* find any other buffering file that has opened this source fd, so the Buffer is used by both to keep the pointers in sync */ bin = nil; for(nfd = 0; nfd <= up->fgrp->maxfd; nfd++){ if(up->fgrp->fd[nfd] != nil && up->fgrp->fd[nfd]->type == bintype && up->fgrp->fd[nfd]->aux != nil && ((Binreader*)up->fgrp->fd[nfd]->aux)->bin->sourcefd == sourcefd){ bin = ((Binreader*)up->fgrp->fd[nfd]->aux)->bin; DBG("binopen found bin chanpath(c) %s br %p bin %p\n", chanpath(c), up->fgrp->fd[nfd]->aux, bin); qlock(bin); incref(bin); qunlock(bin); } } br = malloc(sizeof(Binreader)); if(br == nil) panic("exhausted memory"); if(bin == nil){ bin = malloc(sizeof(Bin)); if(bin == nil) panic("exhausted memory"); br->bin = bin; bin->eof=0; bin->sourcefd = sourcefd; incref(bin); DBG("binopen new bin chanpath(c) %s br %p bin %p\n", chanpath(c), br, bin); } else br->bin = bin; DBG("binopen chanpath(c) %s br %p bin %p\n", chanpath(c), br, bin); whichfile = WHICHFILE(c->qid); for(s = searchers; s != nil; s++){ if(whichfile == s->whichfile){ br->searcher = s; break; } } if(br->searcher == nil) error(Ebadarg); qlock(bin); if(bin->buf == nil){ bin->buf = malloc(conf.pipeqsize); bin->readp = bin->writep = bin->buf; bin->sourcechan = up->fgrp->fd[sourcefd]; /* what if the sourcefd is closed in the meanwhile? */ bin->bufsize = conf.pipeqsize; } qunlock(bin); c->aux = br; c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; c->iounit = conf.pipeqsize; return c; } static void binclose(Chan *c) { Bin *bin; Binreader *l; /* print("binclose: chanpath(c) %s\n", chanpath(c)); */ l = c->aux; if(l != nil){ bin = l->bin; if(bin != nil){ qlock(bin); decref(bin); if(bin->ref == 0){ free(bin->buf); free(bin); }else qunlock(bin); } } } s32 refill(Bin *bin) { s32 nr, n; if(bin->eof == 1) return 0; if(bin->writep == nil) bin->writep = bin->buf; if(bin->readp == nil) bin->readp = bin->buf; if(bin->readp == bin->writep){ bin->readp = bin->writep = bin->buf; DBG("refill: same readp 0x%p writep 0x%p\n", bin->readp, bin->writep); }else if(bin->readp > bin->buf){ n = bin->writep-bin->readp; memmove(bin->buf, bin->readp, n); bin->writep = bin->buf+n; bin->readp = bin->buf; DBG("refill: move readp 0x%p writep 0x%p\n", bin->readp, bin->writep); } DBG("refill: read devtab[n].name %s devtab[n].dc %d" " bin->sourcechan->type %d bin->sourcechan->offset %lld\n", devtab[bin->sourcechan->type]->name, devtab[bin->sourcechan->type]->dc, bin->sourcechan->type, bin->sourcechan->offset); nr = devtab[bin->sourcechan->type]->read(bin->sourcechan, bin->writep, bin->bufsize-(bin->writep-bin->readp), bin->sourcechan->offset); DBG("refill: read nr %d bin->sourcechan->offset %lld\n", nr, bin->sourcechan->offset); if(nr == 0) bin->eof = 1; bin->writep += nr; /* check the notes of rread in os/port/sysfile.c for details */ lock(bin->sourcechan); bin->sourcechan->devoffset += nr; bin->sourcechan->offset += nr; unlock(bin->sourcechan); return nr; } /* end of file is not a delimiter */ static s32 binread(Chan *c, void *va, s32 n, s64) { Bin *bin; Searcher *s; u8 *startp, *nextreadp; u32 rv, nremaining; char nlenstr[10]; s32 nsend; if(c->qid.type == QTDIR){ return devdirread(c, va, n, nil, 0, bingen); } DBG("binread: chanpath(c) %s n %d\n", chanpath(c), n); rv = 0; bin = ((Binreader*)c->aux)->bin; s = ((Binreader*)c->aux)->searcher; if(bin->buf == nil) panic("should no be happening"); /* do not allow arbitrarily high read buffer sizes */ if(n > bin->bufsize) error(Ebadarg); DBG("binread: starting chanpath(c) %s %s readp 0x%p writep 0x%p maxn %d\n", chanpath(c), s->name, bin->readp, bin->writep, n); qlock(bin); if(s->whichfile == Qbufferlength){ rv = snprint(nlenstr, 10, "%lld\n", bin->writep-bin->readp); goto Exit; } if(s->whichfile == Qbuffer && bin->eof == 1){ nremaining = bin->writep-bin->readp; if(nremaining == 0) rv = 0; else if(nremaining > 0){ rv = nremaining > n ? n : nremaining; memmove(va, bin->readp, rv); bin->readp += rv; } goto Exit; } if(bin->readp == bin->writep){ if(bin->eof == 1){ /* nothing more to read */ rv = 0; goto Exit; }else refill(bin); } Search: /* doing this while holding a lock, not sure if it is smart */ DBG("binread: chanpath(c) %s %s readp 0x%p writep 0x%p maxn %d\n", chanpath(c), s->name, bin->readp, bin->writep, n); nsend = s->contentfn(bin->readp, bin->writep, &startp, n, &nextreadp); DBG("binread: nsend %d startp 0x%p nextreadp 0x%p\n", nsend, startp, nextreadp); if(nsend == 0 && bin->eof == 0){ refill(bin); goto Search; } memmove(va, startp, nsend); bin->readp = nextreadp; rv = nsend; Exit: DBG("binread: ending chanpath(c) %s %s readp 0x%p writep 0x%p maxn %d\n", chanpath(c), s->name, bin->readp, bin->writep, n); qunlock(bin); return rv; } void bininit(void) { bintype = devno('n', 0); } s32 binwrite(Chan *, void *, s32, s64) { error(Ebadarg); return -1; } Dev bindevtab = { 'n', "bin", devreset, bininit, devshutdown, binattach, binwalk, binstat, binopen, devcreate, binclose, binread, devbread, binwrite, devbwrite, devremove, devwstat, }; /* to read contents without a delimiter, such as the last non-delimited content */ static u8 * readuntil(u8 *, u8 *endp) { return endp; } static s32 readmax(u8 *readp, u8 *writep, u8 **startp, s32 maxn, u8 **nextreadp) { *startp = readp; *nextreadp = writep; /* nothing to send */ if(readp == writep) return 0; if(writep-readp > maxn){ *nextreadp = readp+maxn; return maxn; } return writep-readp; } /* without a blank line at the end, the last word without an ending word delimiter gets skipped. This is how all Plan9 apps are working. Sticking to that policy. Hence, the application should read the buffer for the last word no, leave it alone. not worth the complexity. \<carriage return> will skip the next line */ static s32 wordfn(u8 *readp, u8 *writep, u8 **startp, s32 maxn, u8 **nextreadp) { u8 *p; s32 n; *startp = *nextreadp = readp; /* nothing to send */ if(readp == writep) return 0; /* skip starting spaces */ for(p = readp; p<writep; p++){ if(*p == ' ' || *p == '\t' || *p == '\n') continue; else break; } *startp = *nextreadp = p; /* disregard until p */ /* all content is spaces */ if(p == writep){ return 0; } /* find ending delimiter */ for(n=0; p<writep && n < maxn; p++){ if(*p == ' ' || *p == '\t' || *p == '\n'){ *nextreadp = p+1; /* skip this for the next read */ return n; } n++; } /* no delimiter found in maxn bytes, send maxn */ if(n == maxn){ *nextreadp += maxn; return maxn; } /* no delimiter found before writep */ return 0; } /* read to c. includes c in the read string. skip c for the next read. It is upto the caller to remove the trailing c from the content. We cannot remove the trailing c because if the next character is a c, we woud be returning 0 characters and 0 is considered as an end of file. */ static s32 onto(u8 *readp, u8 *writep, u8 **startp, s32 maxn, u8 **nextreadp, u8 c) { u8 *p; s32 n; *startp = *nextreadp = readp; /* nothing to send */ if(readp == writep) return 0; DBG("onto readp %p writep %p maxn %d c %c\n", readp, writep, maxn, c); n = writep-readp > maxn ? maxn : writep-readp; p = memchr(readp, c, n); if(p == nil){ /* no delimiter found in maxn bytes, send maxn */ if(n == maxn){ *nextreadp += maxn; return maxn; } return 0; /* kicks off a refill */ }else{ DBG("onto found %c 0x%p: %c %d returning nextreadp 0x%p n %d\n", c, p, *p, *p, p+1, p+1-readp); *nextreadp = p+1; return *nextreadp-readp; } } /* find a double quote */ static s32 doublequotefn(u8 *readp, u8 *writep, u8 **startp, s32 maxn, u8 **nextreadp) { /* " = 0x22 = 34 */ DBG("doublequotefn searching for %c readp 0x%p writep 0x%p readp has -%s-\n", 0x22, readp, writep, readp); return onto(readp, writep, startp, maxn, nextreadp, 0x22); } static s32 closeparenfn(u8 *readp, u8 *writep, u8 **startp, s32 maxn, u8 **nextreadp) { /* ) = 0x29 = 41 */ return onto(readp, writep, startp, maxn, nextreadp, 0x29); } static s32 linefn(u8 *readp, u8 *writep, u8 **startp, s32 maxn, u8 **nextreadp) { /* ) = 0xA = 10 */ return onto(readp, writep, startp, maxn, nextreadp, 0xA); }