|  | /* | 
|  | * This file is part of the UCB release of Plan 9. It is subject to the license | 
|  | * terms in the LICENSE file found in the top-level directory of this | 
|  | * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
|  | * part of the UCB release of Plan 9, including this file, may be copied, | 
|  | * modified, propagated, or distributed except according to the terms contained | 
|  | * in the LICENSE file. | 
|  | */ | 
|  | #include <printf-ext.h> | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <stdio.h> | 
|  | #include <parlib.h> | 
|  | #include <unistd.h> | 
|  | #include <sys/types.h> | 
|  | #include <sys/stat.h> | 
|  | #include <signal.h> | 
|  | #include <pthread.h> | 
|  | #include <fcntl.h> | 
|  | #include <ctype.h> | 
|  | #include <error.h> | 
|  | #include <iplib.h> | 
|  | #include <fcall.h> | 
|  | #include <ndb.h> | 
|  | #include <fcallfmt.h> | 
|  |  | 
|  | static int dumpsome(FILE *, char *, long); | 
|  | static int fdirconv(FILE *, struct dir *); | 
|  | static char *qidtype(char *, uint8_t); | 
|  |  | 
|  | #define	QIDFMT	"(%.16llux %lud %s)" | 
|  |  | 
|  | int printf_fcall(FILE * stream, const struct printf_info *info, | 
|  | const void *const *args) | 
|  | { | 
|  | struct fcall *f = *(void **)args[0]; | 
|  | int fid, type, tag, i, retval = 0; | 
|  | char buf[512], tmp[200]; | 
|  | char *p, *e; | 
|  | struct dir *d; | 
|  | struct qid *q; | 
|  |  | 
|  | e = buf + sizeof(buf); | 
|  | type = f->type; | 
|  | fid = f->fid; | 
|  | tag = f->tag; | 
|  | switch (type) { | 
|  | case Tversion:	/* 100 */ | 
|  | retval += | 
|  | fprintf(stream, "Tversion tag %u msize %u version '%s' ", tag, | 
|  | f->msize, f->version); | 
|  | break; | 
|  | case Rversion: | 
|  | retval += | 
|  | fprintf(stream, "Rversion tag %u msize %u version '%s' ", tag, | 
|  | f->msize, f->version); | 
|  | break; | 
|  | case Tauth:	/* 102 */ | 
|  | retval += | 
|  | fprintf(stream, "Tauth tag %u afid %d uname %s aname %s ", tag, | 
|  | f->afid, f->uname, f->aname); | 
|  | break; | 
|  | case Rauth: | 
|  | retval += fprintf(stream, "Rauth tag %u qid " QIDFMT, tag, | 
|  | f->aqid.path, f->aqid.vers, qidtype(tmp, | 
|  | f->aqid. | 
|  | type)); | 
|  | break; | 
|  | case Tattach:	/* 104 */ | 
|  | retval += | 
|  | fprintf(stream, | 
|  | "Tattach tag %u fid %d afid %d uname %s aname %s ", tag, | 
|  | fid, f->afid, f->uname, f->aname); | 
|  | break; | 
|  | case Rattach: | 
|  | retval += fprintf(stream, "Rattach tag %u qid " QIDFMT, tag, | 
|  | f->qid.path, f->qid.vers, qidtype(tmp, | 
|  | f->qid.type)); | 
|  | break; | 
|  | case Rerror:	/* 107; 106 (Terror) illegal */ | 
|  | retval += fprintf(stream, "Rerror tag %u ename %s ", tag, f->ename); | 
|  | break; | 
|  | case Tflush:	/* 108 */ | 
|  | retval += | 
|  | fprintf(stream, "Tflush tag %u oldtag %u ", tag, f->oldtag); | 
|  | break; | 
|  | case Rflush: | 
|  | retval += fprintf(stream, "Rflush tag %u ", tag); | 
|  | break; | 
|  | case Twalk:	/* 110 */ | 
|  | retval += | 
|  | fprintf(stream, "Twalk tag %u fid %d newfid %d nwname %d  ", | 
|  | tag, fid, f->newfid, f->nwname); | 
|  | if (f->nwname <= MAXWELEM) | 
|  | for (i = 0; i < f->nwname; i++) | 
|  | retval += fprintf(stream, "%d:%s  ", i, f->wname[i]); | 
|  | break; | 
|  | case Rwalk: | 
|  | retval += | 
|  | fprintf(stream, "Rwalk tag %u nwqid %u  ", tag, f->nwqid); | 
|  | if (f->nwqid <= MAXWELEM) | 
|  | for (i = 0; i < f->nwqid; i++) { | 
|  | q = &f->wqid[i]; | 
|  | retval += fprintf(stream, "%d:" QIDFMT "  ", i, | 
|  | q->path, q->vers, qidtype(tmp, q->type)); | 
|  | } | 
|  | break; | 
|  | case Topen:	/* 112 */ | 
|  | retval += | 
|  | fprintf(stream, "Topen tag %u fid %u mode %d ", tag, fid, | 
|  | f->mode); | 
|  | break; | 
|  | case Ropen: | 
|  | retval += | 
|  | fprintf(stream, "Ropen tag %u qid " QIDFMT " iounit %u  ", tag, | 
|  | f->qid.path, f->qid.vers, qidtype(tmp, f->qid.type), | 
|  | f->iounit); | 
|  | break; | 
|  | case Tcreate:	/* 114 */ | 
|  | retval += | 
|  | fprintf(stream, | 
|  | "Tcreate tag %u fid %u name %s perm %d mode %d ", tag, | 
|  | fid, f->name, (uint32_t) f->perm, f->mode); | 
|  | break; | 
|  | case Rcreate: | 
|  | retval += | 
|  | fprintf(stream, "Rcreate tag %u qid " QIDFMT " iounit %u  ", | 
|  | tag, f->qid.path, f->qid.vers, qidtype(tmp, | 
|  | f->qid.type), | 
|  | f->iounit); | 
|  | break; | 
|  | case Tread:	/* 116 */ | 
|  | retval += | 
|  | fprintf(stream, "Tread tag %u fid %d offset %lld count %u ", | 
|  | tag, fid, f->offset, f->count); | 
|  | break; | 
|  | case Rread: | 
|  | retval += | 
|  | fprintf(stream, "Rread tag %u count %u  ", tag, f->count); | 
|  | retval += dumpsome(stream, f->data, f->count); | 
|  | break; | 
|  | case Twrite:	/* 118 */ | 
|  | retval += | 
|  | fprintf(stream, "Twrite tag %u fid %d offset %lld count %u  ", | 
|  | tag, fid, f->offset, f->count); | 
|  | retval += dumpsome(stream, f->data, f->count); | 
|  | break; | 
|  | case Rwrite: | 
|  | retval += | 
|  | fprintf(stream, "Rwrite tag %u count %u ", tag, f->count); | 
|  | break; | 
|  | case Tclunk:	/* 120 */ | 
|  | retval += fprintf(stream, "Tclunk tag %u fid %u ", tag, fid); | 
|  | break; | 
|  | case Rclunk: | 
|  | retval += fprintf(stream, "Rclunk tag %u ", tag); | 
|  | break; | 
|  | case Tremove:	/* 122 */ | 
|  | retval += fprintf(stream, "Tremove tag %u fid %u ", tag, fid); | 
|  | break; | 
|  | case Rremove: | 
|  | retval += fprintf(stream, "Rremove tag %u ", tag); | 
|  | break; | 
|  | case Tstat:	/* 124 */ | 
|  | retval += fprintf(stream, "Tstat tag %u fid %u ", tag, fid); | 
|  | break; | 
|  | case Rstat: | 
|  | retval += fprintf(stream, "Rstat tag %u  ", tag); | 
|  | if (f->nstat > sizeof tmp) | 
|  | retval += fprintf(stream, " stat(%d bytes) ", f->nstat); | 
|  | else { | 
|  | d = (struct dir *)tmp; | 
|  | convM2D(f->stat, f->nstat, d, (char *)(d + 1)); | 
|  | retval += fprintf(stream, " stat "); | 
|  | retval += fdirconv(stream, d); | 
|  | } | 
|  | break; | 
|  | case Twstat:	/* 126 */ | 
|  | retval += fprintf(stream, "Twstat tag %u fid %u ", tag, fid); | 
|  | if (f->nstat > sizeof tmp) | 
|  | retval += fprintf(stream, " stat(%d bytes) ", f->nstat); | 
|  | else { | 
|  | d = (struct dir *)tmp; | 
|  | convM2D(f->stat, f->nstat, d, (char *)(d + 1)); | 
|  | retval += fprintf(stream, " stat "); | 
|  | retval += fdirconv(stream, d); | 
|  | } | 
|  | break; | 
|  | case Rwstat: | 
|  | retval += fprintf(stream, "Rwstat tag %u ", tag); | 
|  | break; | 
|  | default: | 
|  | retval += fprintf(stream, "unknown type %d ", type); | 
|  | } | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | static char *qidtype(char *s, uint8_t t) | 
|  | { | 
|  | char *p; | 
|  | #define QTDIR              0x80	/* type bit for directories */ | 
|  | p = s; | 
|  | if (t & QTDIR) | 
|  | *p++ = 'd'; | 
|  | #if 0 | 
|  | if (t & QTAPPEND) | 
|  | *p++ = 'a'; | 
|  | if (t & QTEXCL) | 
|  | *p++ = 'l'; | 
|  | if (t & QTAUTH) | 
|  | *p++ = 'A'; | 
|  | #endif | 
|  | *p = '\0'; | 
|  | return s; | 
|  | } | 
|  |  | 
|  | int printf_dir(FILE * stream, const struct printf_info *info, | 
|  | const void *const *args) | 
|  | { | 
|  | struct dir *d = *(void **)args[0]; | 
|  | return fdirconv(stream, d); | 
|  | } | 
|  |  | 
|  | static int fdirconv(FILE * stream, struct dir *d) | 
|  | { | 
|  | char tmp[16]; | 
|  |  | 
|  | return fprintf(stream, "'%s' '%s' '%s' '%s' " | 
|  | "q " QIDFMT " m %#luo " | 
|  | "at %ld mt %ld l %lld " | 
|  | "t %d d %d ", | 
|  | d->name, d->uid, d->gid, d->muid, | 
|  | d->qid.path, d->qid.vers, qidtype(tmp, d->qid.type), d->mode, | 
|  | d->atime, d->mtime, d->length, d->type, d->dev); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * dump out count (or DUMPL, if count is bigger) bytes from | 
|  | * buf to stream, as a string if they are all printable, | 
|  | * else as a series of hex bytes | 
|  | */ | 
|  | #define DUMPL 64 | 
|  |  | 
|  | static int dumpsome(FILE * stream, char *buf, long count) | 
|  | { | 
|  | int i, printable, retval = 0; | 
|  |  | 
|  | if (buf == NULL) { | 
|  | return fprintf(stream, "<no data>"); | 
|  | } | 
|  | printable = 1; | 
|  | if (count > DUMPL) | 
|  | count = DUMPL; | 
|  | for (i = 0; i < count && printable; i++) | 
|  | if ((buf[i] < 32 && buf[i] != '\n' && buf[i] != '\t') | 
|  | || (uint8_t) buf[i] > 127) | 
|  | printable = 0; | 
|  | retval += fprintf(stream, "'"); | 
|  | if (printable) { | 
|  | retval += fprintf(stream, "%s ", buf); | 
|  | } else { | 
|  | for (i = 0; i < count; i++) { | 
|  | if (i > 0 && i % 4 == 0) | 
|  | retval += fprintf(stream, " "); | 
|  | retval += fprintf(stream, "%2.2ux ", buf[i]); | 
|  | } | 
|  | } | 
|  | retval += fprintf(stream, "'"); | 
|  | return retval; | 
|  | } | 
|  |  | 
|  | int printf_fcall_info(const struct printf_info *info, size_t n, int *argtypes, | 
|  | int *size) | 
|  | { | 
|  | if (n > 0) { | 
|  | argtypes[0] = PA_POINTER; | 
|  | size[0] = sizeof(uint8_t *); | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | int printf_dir_info(const struct printf_info *info, size_t n, int *argtypes, | 
|  | int *size) | 
|  | { | 
|  | if (n > 0) { | 
|  | argtypes[0] = PA_POINTER; | 
|  | size[0] = sizeof(uint8_t *); | 
|  | } | 
|  | return 1; | 
|  | } |