From 0e96539ff7cff23233d3f0a64bb285b385a3a1f4 Mon Sep 17 00:00:00 2001 From: "Charles.Forsyth" Date: Sat, 30 Dec 2006 21:29:14 +0000 Subject: 20061219 --- FreeBSD/386/bin/mkext | Bin 0 -> 68041 bytes MacOSX/386/bin/data2c | Bin 0 -> 24560 bytes MacOSX/386/bin/mk | Bin 0 -> 66696 bytes MacOSX/386/bin/mkext | Bin 0 -> 34036 bytes MacOSX/386/bin/yacc | Bin 0 -> 56644 bytes MacOSX/386/include/fpuctl.h | 76 ++ MacOSX/386/include/lib9.h | 507 ++++++++++++ MacOSX/power/bin/mkext | Bin 0 -> 41048 bytes appl/cmd/ar.b | 856 +++++++++++++++++++ appl/cmd/cddb.b | 257 ++++++ appl/cmd/lookman.b | 250 ------ appl/cmd/man2html.b | 1328 ++++++++++++++++++++++++++++++ appl/cmd/shutdown.b | 72 -- appl/lib/csv.b | 86 ++ appl/lib/rfc822.b | 561 +++++++++++++ appl/lib/w3c/uris.b | 320 +++++++ appl/spree/man/styxservers-nametree.man2 | 180 ---- appl/spree/man/styxservers.man2 | 902 -------------------- lib/mimetype | 157 ++++ lib/mk/binds | 2 + lib/mk/mkconfig | 28 + lib/mk/mksubdirs | 13 + lib9/getcallerpc-Linux-arm.S | 1 + lib9/getcallerpc-MacOSX-386.s | 8 + mkfiles/mkfile-Linux-arm | 27 + mkfiles/mkfile-MacOSX-386 | 41 + module/csv.m | 8 + module/ida.m | 24 + module/rfc822.m | 68 ++ module/uris.m | 36 + utils/cvbit/cvbit.c | 86 ++ utils/cvbit/mkfile | 15 + 32 files changed, 4505 insertions(+), 1404 deletions(-) create mode 100644 FreeBSD/386/bin/mkext create mode 100644 MacOSX/386/bin/data2c create mode 100644 MacOSX/386/bin/mk create mode 100644 MacOSX/386/bin/mkext create mode 100644 MacOSX/386/bin/yacc create mode 100644 MacOSX/386/include/fpuctl.h create mode 100644 MacOSX/386/include/lib9.h create mode 100644 MacOSX/power/bin/mkext create mode 100644 appl/cmd/ar.b create mode 100644 appl/cmd/cddb.b delete mode 100644 appl/cmd/lookman.b create mode 100644 appl/cmd/man2html.b delete mode 100644 appl/cmd/shutdown.b create mode 100644 appl/lib/csv.b create mode 100644 appl/lib/rfc822.b create mode 100644 appl/lib/w3c/uris.b delete mode 100644 appl/spree/man/styxservers-nametree.man2 delete mode 100644 appl/spree/man/styxservers.man2 create mode 100644 lib/mimetype create mode 100644 lib/mk/binds create mode 100644 lib/mk/mkconfig create mode 100644 lib/mk/mksubdirs create mode 100644 lib9/getcallerpc-Linux-arm.S create mode 100644 lib9/getcallerpc-MacOSX-386.s create mode 100644 mkfiles/mkfile-Linux-arm create mode 100644 mkfiles/mkfile-MacOSX-386 create mode 100644 module/csv.m create mode 100644 module/ida.m create mode 100644 module/rfc822.m create mode 100644 module/uris.m create mode 100644 utils/cvbit/cvbit.c create mode 100644 utils/cvbit/mkfile diff --git a/FreeBSD/386/bin/mkext b/FreeBSD/386/bin/mkext new file mode 100644 index 00000000..050bf695 Binary files /dev/null and b/FreeBSD/386/bin/mkext differ diff --git a/MacOSX/386/bin/data2c b/MacOSX/386/bin/data2c new file mode 100644 index 00000000..0de8f7b8 Binary files /dev/null and b/MacOSX/386/bin/data2c differ diff --git a/MacOSX/386/bin/mk b/MacOSX/386/bin/mk new file mode 100644 index 00000000..96f7f083 Binary files /dev/null and b/MacOSX/386/bin/mk differ diff --git a/MacOSX/386/bin/mkext b/MacOSX/386/bin/mkext new file mode 100644 index 00000000..8eca76ca Binary files /dev/null and b/MacOSX/386/bin/mkext differ diff --git a/MacOSX/386/bin/yacc b/MacOSX/386/bin/yacc new file mode 100644 index 00000000..a315ba8a Binary files /dev/null and b/MacOSX/386/bin/yacc differ diff --git a/MacOSX/386/include/fpuctl.h b/MacOSX/386/include/fpuctl.h new file mode 100644 index 00000000..8389f6ee --- /dev/null +++ b/MacOSX/386/include/fpuctl.h @@ -0,0 +1,76 @@ +/* + * Linux 386 fpu support + * Mimic Plan9 floating point support + */ + +static void +setfcr(ulong fcr) +{ + __asm__( "xorb $0x3f, %%al\n\t" + "pushw %%ax\n\t" + "fwait\n\t" + "fldcw (%%esp)\n\t" + "popw %%ax\n\t" + : /* no output */ + : "al" (fcr) + ); +} + +static ulong +getfcr(void) +{ + ulong fcr = 0; + + __asm__( "pushl %%eax\n\t" + "fwait\n\t" + "fstcw (%%esp)\n\t" + "popl %%eax\n\t" + "xorb $0x3f, %%al\n\t" + : "=a" (fcr) + : "eax" (fcr) + ); + return fcr; +} + +static ulong +getfsr(void) +{ + ulong fsr = -1; + + __asm__( "fwait\n\t" + "fstsw (%%eax)\n\t" + "movl (%%eax), %%eax\n\t" + "andl $0xffff, %%eax\n\t" + : "=a" (fsr) + : "eax" (&fsr) + ); + return fsr; +} + +static void +setfsr(ulong fsr) +{ + __asm__("fclex\n\t"); +} + +/* FCR */ +#define FPINEX (1<<5) +#define FPUNFL ((1<<4)|(1<<1)) +#define FPOVFL (1<<3) +#define FPZDIV (1<<2) +#define FPINVAL (1<<0) +#define FPRNR (0<<10) +#define FPRZ (3<<10) +#define FPRPINF (2<<10) +#define FPRNINF (1<<10) +#define FPRMASK (3<<10) +#define FPPEXT (3<<8) +#define FPPSGL (0<<8) +#define FPPDBL (2<<8) +#define FPPMASK (3<<8) +/* FSR */ +#define FPAINEX FPINEX +#define FPAOVFL FPOVFL +#define FPAUNFL FPUNFL +#define FPAZDIV FPZDIV +#define FPAINVAL FPINVAL diff --git a/MacOSX/386/include/lib9.h b/MacOSX/386/include/lib9.h new file mode 100644 index 00000000..69bbbb95 --- /dev/null +++ b/MacOSX/386/include/lib9.h @@ -0,0 +1,507 @@ +/* + * Based on FreeBSD lib9.h + * Copyright © 1998, 1999 Lucent Technologies Inc. All rights reserved. + * Revisions Copyright © 1999, 2002 Vita Nuova Limited. All rights reserved. + * Revisions Copyright © 2002, 2003 Corpus Callosum Corporation. All rights reserved. + */ + +/* define _BSD_SOURCE to use ISO C, POSIX, and 4.4BSD things. */ +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif +#define _LARGEFILE64_SOURCE 1 +#define _FILE_OFFSET_BITS 64 + +#include +#include +#include +#include +#include +#include +#include +// #include "math.h" +#include +#include +#include +#include + +#define nil ((void*)0) + +// typedef unsigned short ushort; +typedef unsigned char uchar; +typedef unsigned long ulong; +// typedef unsigned int uint; +typedef signed char schar; +typedef long long vlong; +typedef unsigned long long uvlong; +typedef ushort Rune; +typedef unsigned int u32int; +typedef unsigned int mpdigit; /* for /sys/include/mp.h */ +typedef unsigned short u16int; +typedef unsigned char u8int; +typedef unsigned long uintptr; + +/* handle conflicts with host os libs */ +#define getwd infgetwd +#define scalb infscalb +#define div infdiv +#define panic infpanic +#define rint infrint +#define rcmd infrcmd +#undef isnan +#define pow10 infpow10 + +#ifndef EMU +typedef struct Proc Proc; +#endif + +/* + * math module dtoa + */ +#include +#define __LITTLE_ENDIAN + +#define USED(x) if(x){}else{} +#define SET(x) + +#define nelem(x) (sizeof(x)/sizeof((x)[0])) +#define offsetof(s, m) (ulong)(&(((s*)0)->m)) +#define assert(x) if(x){}else _assert("x") + +/* + * mem and string routines are declared by ANSI/POSIX files above + */ + +extern char* strecpy(char*, char*, char*); +extern char* strdup(const char*); +extern int cistrncmp(char*, char*, int); +extern int cistrcmp(char*, char*); +extern char* cistrstr(char*, char*); +extern int tokenize(char*, char**, int); +extern vlong strtoll(const char*, char**, int); + +enum +{ + UTFmax = 3, /* maximum bytes per rune */ + Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ + Runeself = 0x80, /* rune and UTF sequences are the same (<) */ + Runeerror = 0x80 /* decoding error in UTF */ +}; + +/* + * rune routines + */ +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern int runelen(long); +extern int runenlen(Rune*, int); +extern int fullrune(char*, int); +extern int utflen(char*); +extern int utfnlen(char*, long); +extern char* utfrune(char*, long); +extern char* utfrrune(char*, long); +extern char* utfutf(char*, char*); +extern char* utfecpy(char*, char*, char*); + +extern Rune* runestrcat(Rune*, Rune*); +extern Rune* runestrchr(Rune*, Rune); +extern int runestrcmp(Rune*, Rune*); +extern Rune* runestrcpy(Rune*, Rune*); +extern Rune* runestrncpy(Rune*, Rune*, long); +extern Rune* runestrecpy(Rune*, Rune*, Rune*); +extern Rune* runestrdup(Rune*); +extern Rune* runestrncat(Rune*, Rune*, long); +extern int runestrncmp(Rune*, Rune*, long); +extern Rune* runestrrchr(Rune*, Rune); +extern long runestrlen(Rune*); +extern Rune* runestrstr(Rune*, Rune*); + +extern Rune tolowerrune(Rune); +extern Rune totitlerune(Rune); +extern Rune toupperrune(Rune); +extern int isalpharune(Rune); +extern int islowerrune(Rune); +extern int isspacerune(Rune); +extern int istitlerune(Rune); +extern int isupperrune(Rune); + +/* + * malloc + */ +extern void* malloc(size_t); +extern void* mallocz(ulong, int); +extern void free(void*); +extern ulong msize(void*); +extern void* calloc(size_t, size_t); +extern void* realloc(void*, size_t); +extern void setmalloctag(void*, ulong); +extern void setrealloctag(void*, ulong); +extern ulong getmalloctag(void*); +extern ulong getrealloctag(void*); +extern void* malloctopoolblock(void*); + +/* + * print routines + */ +typedef struct Fmt Fmt; +struct Fmt{ + uchar runes; /* output buffer is runes or chars? */ + void *start; /* of buffer */ + void *to; /* current place in the buffer */ + void *stop; /* end of the buffer; overwritten if flush fails */ + int (*flush)(Fmt *); /* called when to == stop */ + void *farg; /* to make flush a closure */ + int nfmt; /* num chars formatted so far */ + va_list args; /* args passed to dofmt */ + int r; /* % format Rune */ + int width; + int prec; + ulong flags; +}; + +enum{ + FmtWidth = 1, + FmtLeft = FmtWidth << 1, + FmtPrec = FmtLeft << 1, + FmtSharp = FmtPrec << 1, + FmtSpace = FmtSharp << 1, + FmtSign = FmtSpace << 1, + FmtZero = FmtSign << 1, + FmtUnsigned = FmtZero << 1, + FmtShort = FmtUnsigned << 1, + FmtLong = FmtShort << 1, + FmtVLong = FmtLong << 1, + FmtComma = FmtVLong << 1, + FmtByte = FmtComma << 1, + + FmtFlag = FmtByte << 1 +}; + +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int vsnprint(char*, int, char*, va_list); +extern char* smprint(char*, ...); +extern char* vsmprint(char*, va_list); +extern int sprint(char*, char*, ...); +extern int fprint(int, char*, ...); +extern int vfprint(int, char*, va_list); + +extern int runesprint(Rune*, char*, ...); +extern int runesnprint(Rune*, int, char*, ...); +extern int runevsnprint(Rune*, int, char*, va_list); +extern Rune* runeseprint(Rune*, Rune*, char*, ...); +extern Rune* runevseprint(Rune*, Rune*, char*, va_list); +extern Rune* runesmprint(char*, ...); +extern Rune* runevsmprint(char*, va_list); + +extern int fmtfdinit(Fmt*, int, char*, int); +extern int fmtfdflush(Fmt*); +extern int fmtstrinit(Fmt*); +extern char* fmtstrflush(Fmt*); +extern int runefmtstrinit(Fmt*); +extern Rune* runefmtstrflush(Fmt*); + +extern int fmtinstall(int, int (*)(Fmt*)); +extern int dofmt(Fmt*, char*); +extern int dorfmt(Fmt*, Rune*); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtvprint(Fmt*, char*, va_list); +extern int fmtrune(Fmt*, int); +extern int fmtstrcpy(Fmt*, char*); +extern int fmtrunestrcpy(Fmt*, Rune*); +/* + * error string for %r + * supplied on per os basis, not part of fmt library + */ +extern int errfmt(Fmt *f); + +/* + * quoted strings + */ +extern char *unquotestrdup(char*); +extern Rune *unquoterunestrdup(Rune*); +extern char *quotestrdup(char*); +extern Rune *quoterunestrdup(Rune*); +extern int quotestrfmt(Fmt*); +extern int quoterunestrfmt(Fmt*); +extern void quotefmtinstall(void); +extern int (*doquote)(int); + +/* + * random number + */ + +extern int nrand(int); +extern ulong truerand(void); +extern ulong ntruerand(ulong); + +/* + * math + */ +extern int isNaN(double); +extern int isInf(double, int); +extern double pow(double, double); + +/* + * Time-of-day + */ + +typedef struct Tm Tm; +struct Tm { + int sec; + int min; + int hour; + int mday; + int mon; + int year; + int wday; + int yday; + char zone[4]; + int tzoff; +}; +extern vlong osnsec(void); +#define nsec osnsec + +/* + * one-of-a-kind + */ +extern void _assert(char*); +extern double charstod(int(*)(void*), void*); +extern char* cleanname(char*); +extern double frexp(double, int*); +extern ulong getcallerpc(void*); +extern int getfields(char*, char**, int, int, char*); +extern char* getuser(void); +extern char* getwd(char*, int); +extern double ipow10(int); +extern double ldexp(double, int); +extern double modf(double, double*); +extern void perror(const char*); +extern double pow10(int); +extern uvlong strtoull(const char*, char**, int); +extern void sysfatal(char*, ...); +extern int dec64(uchar*, int, char*, int); +extern int enc64(char*, int, uchar*, int); +extern int dec32(uchar*, int, char*, int); +extern int enc32(char*, int, uchar*, int); +extern int dec16(uchar*, int, char*, int); +extern int enc16(char*, int, uchar*, int); +extern int encodefmt(Fmt*); + +/* + * synchronization + */ +typedef +struct Lock { + ulong val; + int pid; +} Lock; + +extern ulong _tas(ulong*); + +extern void lock(Lock*); +extern void unlock(Lock*); +extern int canlock(Lock*); + +typedef struct QLock QLock; +struct QLock +{ + Lock use; /* to access Qlock structure */ + Proc *head; /* next process waiting for object */ + Proc *tail; /* last process waiting for object */ + int locked; /* flag */ +}; + +extern void qlock(QLock*); +extern void qunlock(QLock*); +extern int canqlock(QLock*); +extern void _qlockinit(ulong (*)(ulong, ulong)); /* called only by the thread library */ + +typedef +struct RWLock +{ + Lock l; /* Lock modify lock */ + QLock x; /* Mutual exclusion lock */ + QLock k; /* Lock for waiting writers */ + int readers; /* Count of readers in lock */ +} RWLock; + +extern int canrlock(RWLock*); +extern int canwlock(RWLock*); +extern void rlock(RWLock*); +extern void runlock(RWLock*); +extern void wlock(RWLock*); +extern void wunlock(RWLock*); + +/* + * network dialing + */ +#define NETPATHLEN 40 + +/* + * system calls + * + */ + +#define STATMAX 65535U /* max length of machine-independent stat structure */ +#define DIRMAX (sizeof(Dir)+STATMAX) /* max length of Dir structure */ +#define ERRMAX 128 /* max length of error string */ + +#define MORDER 0x0003 /* mask for bits defining order of mounting */ +#define MREPL 0x0000 /* mount replaces object */ +#define MBEFORE 0x0001 /* mount goes before others in union directory */ +#define MAFTER 0x0002 /* mount goes after others in union directory */ +#define MCREATE 0x0004 /* permit creation in mounted directory */ +#define MCACHE 0x0010 /* cache some data */ +#define MMASK 0x0017 /* all bits on */ + +#define OREAD 0 /* open for read */ +#define OWRITE 1 /* write */ +#define ORDWR 2 /* read and write */ +#define OEXEC 3 /* execute, == read but check execute permission */ +#define OTRUNC 16 /* or'ed in (except for exec), truncate file first */ +#define OCEXEC 32 /* or'ed in, close on exec */ +#define ORCLOSE 64 /* or'ed in, remove on close */ +#define OEXCL 0x1000 /* or'ed in, exclusive use (create only) */ + +#define AEXIST 0 /* accessible: exists */ +#define AEXEC 1 /* execute access */ +#define AWRITE 2 /* write access */ +#define AREAD 4 /* read access */ + +/* bits in Qid.type */ +#define QTDIR 0x80 /* type bit for directories */ +#define QTAPPEND 0x40 /* type bit for append only files */ +#define QTEXCL 0x20 /* type bit for exclusive use files */ +#define QTMOUNT 0x10 /* type bit for mounted channel */ +#define QTAUTH 0x08 /* type bit for authentication file */ +#define QTFILE 0x00 /* plain file */ + +/* bits in Dir.mode */ +#define DMDIR 0x80000000 /* mode bit for directories */ +#define DMAPPEND 0x40000000 /* mode bit for append only files */ +#define DMEXCL 0x20000000 /* mode bit for exclusive use files */ +#define DMMOUNT 0x10000000 /* mode bit for mounted channel */ +#define DMAUTH 0x08000000 /* mode bit for authentication file */ +#define DMREAD 0x4 /* mode bit for read permission */ +#define DMWRITE 0x2 /* mode bit for write permission */ +#define DMEXEC 0x1 /* mode bit for execute permission */ + +typedef +struct Qid +{ + uvlong path; + ulong vers; + uchar type; +} Qid; + +typedef +struct Dir { + /* system-modified data */ + ushort type; /* server type */ + uint dev; /* server subtype */ + /* file data */ + Qid qid; /* unique id from server */ + ulong mode; /* permissions */ + ulong atime; /* last read time */ + ulong mtime; /* last write time */ + vlong length; /* file length */ + char *name; /* last element of path */ + char *uid; /* owner name */ + char *gid; /* group name */ + char *muid; /* last modifier name */ +} Dir; + +extern Dir* dirstat(char*); +extern Dir* dirfstat(int); +extern int dirwstat(char*, Dir*); +extern int dirfwstat(int, Dir*); +extern long dirread(int, Dir**); +extern void nulldir(Dir*); +extern long dirreadall(int, Dir**); + +typedef +struct Waitmsg +{ + int pid; /* of loved one */ + ulong time[3]; /* of loved one & descendants */ + char *msg; +} Waitmsg; + +extern void _exits(char*); + +extern void exits(char*); +extern int create(char*, int, int); +extern int errstr(char*, uint); + +extern void perror(const char*); +extern long readn(int, void*, long); +extern int remove(const char*); +extern void rerrstr(char*, uint); +extern vlong seek(int, vlong, int); +extern int segflush(void*, ulong); +extern void werrstr(char*, ...); + +extern char *argv0; +#define ARGBEGIN for((argv0||(argv0=*argv)),argv++,argc--;\ + argv[0] && argv[0][0]=='-' && argv[0][1];\ + argc--, argv++) {\ + char *_args, *_argt;\ + Rune _argc;\ + _args = &argv[0][1];\ + if(_args[0]=='-' && _args[1]==0){\ + argc--; argv++; break;\ + }\ + _argc = 0;\ + while(*_args && (_args += chartorune(&_argc, _args)))\ + switch(_argc) +#define ARGEND SET(_argt);USED(_argt);USED(_argc); USED(_args);}USED(argv); USED(argc); +#define ARGF() (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): 0)) +#define EARGF(x) (_argt=_args, _args="",\ + (*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0))) + +#define ARGC() _argc + +/* + * Extensions for Inferno to basic libc.h + */ + +#define setbinmode() + +/* + * Extensions for emu kernel emulation + */ +#ifdef EMU + +extern Proc *getup(void); +#define up (getup()) + +/* + * This structure must agree with FPsave and FPrestore asm routines + */ + +// something is at odds between i386/fpu.h and some of the thread headers +#define fp_control inffp_control +#define fp_control_t inffp_control_t +#define fp_status inffp_status +#define fp_status_t inffp_status_t + +#include + +typedef struct FPU FPU; +struct FPU +{ + fp_state_t env; +}; + +#undef fp_control +#undef fp_control_t +#undef fp_status +#undef fp_status_t + +typedef sigjmp_buf osjmpbuf; +#define ossetjmp(buf) sigsetjmp(buf, 1) +#endif diff --git a/MacOSX/power/bin/mkext b/MacOSX/power/bin/mkext new file mode 100644 index 00000000..6a0d475b Binary files /dev/null and b/MacOSX/power/bin/mkext differ diff --git a/appl/cmd/ar.b b/appl/cmd/ar.b new file mode 100644 index 00000000..8ef237b8 --- /dev/null +++ b/appl/cmd/ar.b @@ -0,0 +1,856 @@ +implement Ar; + +# +# ar - portable (ascii) format version +# + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; + +include "daytime.m"; + daytime: Daytime; + +include "string.m"; + str: String; + +Ar: module +{ + init: fn(nil: ref Draw->Context, nil: list of string); +}; + +ARMAG: con "!\n"; +SARMAG: con len ARMAG; +ARFMAG0: con byte '`'; +ARFMAG1: con byte '\n'; +SARNAME: con 16; # ancient limit + +# +# printable archive header +# name[SARNAME] date[12] uid[6] gid[6] mode[8] size[10] fmag[2] +# +Oname: con 0; +Lname: con SARNAME; +Odate: con Oname+Lname; +Ldate: con 12; +Ouid: con Odate+Ldate; +Luid: con 6; +Ogid: con Ouid+Luid; +Lgid: con 6; +Omode: con Ogid+Lgid; +Lmode: con 8; +Osize: con Omode+Lmode; +Lsize: con 10; +Ofmag: con Osize+Lsize; +Lfmag: con 2; +SAR_HDR: con Ofmag+Lfmag; # 60 + +# +# The algorithm uses up to 3 temp files. The "pivot contents" is the +# archive contents specified by an a, b, or i option. The temp files are +# astart - contains existing contentss up to and including the pivot contents. +# amiddle - contains new files moved or inserted behind the pivot. +# aend - contains the existing contentss that follow the pivot contents. +# When all contentss have been processed, function 'install' streams the +# temp files, in order, back into the archive. +# + +Armember: adt { # one per archive contents + name: string; # trimmed + length: int; + date: int; + uid: int; + gid: int; + mode: int; + size: int; + contents: array of byte; + fd: ref Sys->FD; # if contents is nil and fd is not nil, fd has contents + next: cyclic ref Armember; + + new: fn(name: string, fd: ref Sys->FD): ref Armember; + rdhdr: fn(b: ref Iobuf): ref Armember; + read: fn(m: self ref Armember, b: ref Iobuf): int; + wrhdr: fn(m: self ref Armember, fd: ref Sys->FD); + write: fn(m: self ref Armember, fd: ref Sys->FD); + skip: fn(m: self ref Armember, b: ref Iobuf); + replace: fn(m: self ref Armember, name: string, fd: ref Sys->FD); + copyout: fn(m: self ref Armember, b: ref Iobuf, destfd: ref Sys->FD); +}; + +Arfile: adt { # one per tempfile + fd: ref Sys->FD; # paging file descriptor, nil if none allocated + + head: ref Armember; + tail: ref Armember; + + new: fn(): ref Arfile; + copy: fn(ar: self ref Arfile, b: ref Iobuf, mem: ref Armember); + insert: fn(ar: self ref Arfile, mem: ref Armember); + stream: fn(ar: self ref Arfile, fd: ref Sys->FD); + page: fn(ar: self ref Arfile): int; +}; + +File: adt { + name: string; + trimmed: string; + found: int; +}; + +man := "mrxtdpq"; +opt := "uvnbailo"; + +aflag := 0; +bflag := 0; +cflag := 0; +oflag := 0; +uflag := 0; +vflag := 0; + +pivotname: string; +bout: ref Iobuf; +stderr: ref Sys->FD; +parts: array of ref Arfile; + +comfun: ref fn(a: string, f: array of ref File); + +init(nil: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + daytime = load Daytime Daytime->PATH; + str = load String String->PATH; + + stderr = sys->fildes(2); + bout = bufio->fopen(sys->fildes(1), Sys->OWRITE); + if(len args < 3) + usage(); + args = tl args; + s := hd args; args = tl args; + for(i := 0; i < len s; i++){ + case s[i] { + 'a' => aflag = 1; + 'b' => bflag = 1; + 'c' => cflag = 1; + 'd' => setcom(dcmd); + 'i' => bflag = 1; + 'l' => ; # ignored + 'm' => setcom(mcmd); + 'o' => oflag = 1; + 'p' => setcom(pcmd); + 'q' => setcom(qcmd); + 'r' => setcom(rcmd); + 't' => setcom(tcmd); + 'u' => uflag = 1; + 'v' => vflag = 1; + 'x' => setcom(xcmd); + * => + sys->fprint(stderr, "ar: bad option `%c'\n", s[i]); + usage(); + } + } + if(aflag && bflag){ + sys->fprint(stderr, "ar: only one of 'a' and 'b' can be specified\n"); + usage(); + } + if(aflag || bflag){ + pivotname = trim(hd args); args = tl args; + if(len args < 2) + usage(); + } + if(comfun == nil){ + if(uflag == 0){ + sys->fprint(stderr, "ar: one of [%s] must be specified\n", man); + usage(); + } + setcom(rcmd); + } + cp := hd args; args = tl args; + files := array[len args] of ref File; + for(i = 0; args != nil; args = tl args) + files[i++] = ref File(hd args, trim(hd args), 0); + comfun(cp, files); # do the command + for(i = 0; i < len files; i++) + if(!files[i].found){ + sys->fprint(stderr, "ar: %s not found\n", files[i].name); + cp = "error"; + } + bout.flush(); + if(cp != nil) + raise "fail:"+cp; +} + +# +# select a command +# +setcom(fun: ref fn(s: string, f: array of ref File)) +{ + if(comfun != nil){ + sys->fprint(stderr, "ar: only one of [%s] allowed\n", man); + usage(); + } + comfun = fun; +} + +# +# perform the 'r' and 'u' commands +# +rcmd(arname: string, files: array of ref File) +{ + bar := openar(arname, Sys->ORDWR, 1); + parts = array[2] of {Arfile.new(), nil}; + ap := parts[0]; + if(bar != nil){ + while((mem := Armember.rdhdr(bar)) != nil){ + if(bamatch(mem.name, pivotname)) # check for pivot + ap = parts[1] = Arfile.new(); + f := match(files, mem.name); + if(f == nil){ + ap.copy(bar, mem); + continue; + } + f.found = 1; + dfd := sys->open(f.name, Sys->OREAD); + if(dfd == nil){ + if(len files > 0) + sys->fprint(stderr, "ar: cannot open %s: %r\n", f.name); + ap.copy(bar, mem); + continue; + } + if(uflag){ + (ok, d) := sys->fstat(dfd); + if(ok < 0 || d.mtime <= mem.date){ + if(ok < 0) + sys->fprint(stderr, "ar: cannot stat %s: %r\n", f.name); + ap.copy(bar, mem); + continue; + } + } + mem.skip(bar); + mesg('r', f.name); + mem.replace(f.name, dfd); + ap.insert(mem); + dfd = nil; + } + } + # copy in remaining files named on command line + for(i := 0; i < len files; i++){ + f := files[i]; + if(f.found) + continue; + f.found = 1; + dfd := sys->open(f.name, Sys->OREAD); + if(dfd != nil){ + mesg('a', f.name); + parts[0].insert(Armember.new(f.trimmed, dfd)); + }else + sys->fprint(stderr, "ar: cannot open %s: %r\n", f.name); + } + if(bar == nil && !cflag) + install(arname, parts, 1); # issue 'creating' msg + else + install(arname, parts, 0); +} + +dcmd(arname: string, files: array of ref File) +{ + if(len files == 0) + return; + changed := 0; + parts = array[] of {Arfile.new()}; + bar := openar(arname, Sys->ORDWR, 0); + while((mem := Armember.rdhdr(bar)) != nil){ + if(match(files, mem.name) != nil){ + mesg('d', mem.name); + mem.skip(bar); + changed = 1; + }else + parts[0].copy(bar, mem); + mem = nil; # conserves memory + } + if(changed) + install(arname, parts, 0); +} + +xcmd(arname: string, files: array of ref File) +{ + bar := openar(arname, Sys->OREAD, 0); + i := 0; + while((mem := Armember.rdhdr(bar)) != nil){ + if((f := match(files, mem.name)) != nil){ + f.found = 1; + fd := sys->create(f.name, Sys->OWRITE, mem.mode & 8r777); + if(fd == nil){ + sys->fprint(stderr, "ar: cannot create %s: %r\n", f.name); + mem.skip(bar); + }else{ + mesg('x', f.name); + mem.copyout(bar, fd); + if(oflag){ + dx := sys->nulldir; + dx.atime = mem.date; + dx.mtime = mem.date; + if(sys->fwstat(fd, dx) < 0) + sys->fprint(stderr, "ar: can't set times on %s: %r", f.name); + } + fd = nil; + mem = nil; + } + if(len files > 0 && ++i >= len files) + break; + }else + mem.skip(bar); + } +} + +pcmd(arname: string, files: array of ref File) +{ + bar := openar(arname, Sys->OREAD, 0); + i := 0; + while((mem := Armember.rdhdr(bar)) != nil){ + if((f := match(files, mem.name)) != nil){ + if(vflag) + sys->print("\n<%s>\n\n", f.name); + mem.copyout(bar, sys->fildes(1)); + if(len files > 0 && ++i >= len files) + break; + }else + mem.skip(bar); + mem = nil; # we no longer need the contents + } +} + +mcmd(arname: string, files: array of ref File) +{ + if(len files == 0) + return; + parts = array[3] of {Arfile.new(), Arfile.new(), nil}; + bar := openar(arname, Sys->ORDWR, 0); + ap := parts[0]; + while((mem := Armember.rdhdr(bar)) != nil){ + if(bamatch(mem.name, pivotname)) + ap = parts[2] = Arfile.new(); + if((f := match(files, mem.name)) != nil){ + mesg('m', f.name); + parts[1].copy(bar, mem); + }else + ap.copy(bar, mem); + } + if(pivotname != nil && parts[2] == nil) + sys->fprint(stderr, "ar: %s not found - files moved to end\n", pivotname); + install(arname, parts, 0); +} + +tcmd(arname: string, files: array of ref File) +{ + bar := openar(arname, Sys->OREAD, 0); + while((mem := Armember.rdhdr(bar)) != nil){ + if((f := match(files, mem.name)) != nil){ + longls := ""; + if(vflag) + longls = longtext(mem)+" "; + bout.puts(longls+f.trimmed+"\n"); + } + mem.skip(bar); + mem = nil; + } +} + +qcmd(arname: string, files: array of ref File) +{ + if(aflag || bflag){ + sys->fprint(stderr, "ar: abi not allowed with q\n"); + raise "fail:usage"; + } + fd := openrawar(arname, Sys->ORDWR, 1); + if(fd == nil){ + if(!cflag) + sys->fprint(stderr, "ar: creating %s\n", arname); + fd = arcreate(arname); + } + # leave note group behind when writing archive; i.e. sidestep interrupts + sys->seek(fd, big 0, 2); # append + for(i := 0; i < len files; i++){ + f := files[i]; + f.found = 1; + dfd := sys->open(f.name, Sys->OREAD); + if(dfd != nil){ + mesg('q', f.name); + mem := Armember.new(f.trimmed, dfd); + if(mem != nil){ + mem.write(fd); + mem = nil; + } + }else + sys->fprint(stderr, "ar: cannot open %s: %r\n", f.name); + } +} + +# +# open an archive and validate its header +# +openrawar(arname: string, mode: int, errok: int): ref Sys->FD +{ + fd := sys->open(arname, mode); + if(fd == nil){ + if(!errok){ + sys->fprint(stderr, "ar: cannot open %s: %r\n", arname); + raise "fail:error"; + } + return nil; + } + mbuf := array[SARMAG] of byte; + if(sys->read(fd, mbuf, SARMAG) != SARMAG || string mbuf != ARMAG){ + sys->fprint(stderr, "ar: %s not in archive format\n", arname); + raise "fail:error"; + } + return fd; +} + +openar(arname: string, mode: int, errok: int): ref Iobuf +{ + fd := openrawar(arname, mode, errok); + if(fd == nil) + return nil; + bfd := bufio->fopen(fd, mode); + bfd.seek(big SARMAG, 0); + return bfd; +} + +# +# create an archive and set its header +# +arcreate(arname: string): ref Sys->FD +{ + fd := sys->create(arname, Sys->OWRITE, 8r666); + if(fd == nil){ + sys->fprint(stderr, "ar: cannot create %s: %r\n", arname); + raise "fail:create"; + } + a := array of byte ARMAG; + mustwrite(fd, a, len a); + return fd; +} + +# +# error handling +# +wrerr() +{ + sys->fprint(stderr, "ar: write error: %r\n"); + raise "fail:write error"; +} + +rderr() +{ + sys->fprint(stderr, "ar: read error: %r\n"); + raise "fail:read error"; +} + +phaseerr(offset: big) +{ + sys->fprint(stderr, "ar: phase error at offset %bd\n", offset); + raise "fail:phase error"; +} + +usage() +{ + sys->fprint(stderr, "usage: ar [%s][%s] archive files ...\n", opt, man); + raise "fail:usage"; +} + +# +# concatenate the several sequences of members into one archive +# +install(arname: string, seqs: array of ref Arfile, createflag: int) +{ + # leave process group behind when copying back; i.e. sidestep interrupts + sys->pctl(Sys->NEWPGRP, nil); + + if(createflag) + sys->fprint(stderr, "ar: creating %s\n", arname); + fd := arcreate(arname); + for(i := 0; i < len seqs; i++) + if((ap := seqs[i]) != nil) + ap.stream(fd); +} + +# +# return the command line File matching a given name +# +match(files: array of ref File, file: string): ref File +{ + if(len files == 0) + return ref File(file, file, 0); # empty list always matches + for(i := 0; i < len files; i++) + if(!files[i].found && files[i].trimmed == file){ + files[i].found = 1; + return files[i]; + } + return nil; +} + +# +# is `file' the pivot member's name and is the archive positioned +# at the correct point wrt after or before options? return true if so. +# +state := 0; + +bamatch(file: string, pivot: string): int +{ + case state { + 0 => # looking for position file + if(aflag){ + if(file == pivot) + state = 1; + }else if(bflag){ + if(file == pivot){ + state = 2; # found + return 1; + } + } + 1 => # found - after previous file + state = 2; + return 1; + 2 => # already found position file + ; + } + return 0; +} + +# +# output a message, if 'v' option was specified +# +mesg(c: int, file: string) +{ + if(vflag) + bout.puts(sys->sprint("%c - %s\n", c, file)); +} + +# +# return just the file name +# +trim(s: string): string +{ + for(j := len s; j > 0 && s[j-1] == '/';) + j--; + k := 0; + for(i := 0; i < j; i++) + if(s[i] == '/') + k = i+1; + return s[k: j]; +} + +longtext(mem: ref Armember): string +{ + s := modes(mem.mode); + s += sys->sprint(" %3d/%1d", mem.uid, mem.gid); + s += sys->sprint(" %7ud", mem.size); + t := daytime->text(daytime->local(mem.date)); + return s+sys->sprint(" %-12.12s %-4.4s ", t[4:], t[24:]); +} + +mtab := array[] of { + "---", "--x", "-w-", "-wx", + "r--", "r-x", "rw-", "rwx" +}; + +modes(mode: int): string +{ + return mtab[(mode>>6)&7]+mtab[(mode>>3)&7]+mtab[mode&7]; +} + +# +# read the header for the next archive contents +# +Armember.rdhdr(b: ref Iobuf): ref Armember +{ + buf := array[SAR_HDR] of byte; + if((n := b.read(buf, len buf)) != len buf){ + if(n == 0) + return nil; + if(n > 0) + sys->werrstr("unexpected end-of-file"); + rderr(); + } + mem := ref Armember; + for(i := Oname+Lname; i > Oname; i--) + if(buf[i-1] != byte '/' && buf[i-1] != byte ' ') + break; + mem.name = string buf[Oname:i]; + mem.date = intof(buf[Odate: Odate+Ldate], 10); + mem.uid = intof(buf[Ouid: Ouid+Luid], 10); + mem.gid = intof(buf[Ogid: Ogid+Lgid], 10); + mem.mode = intof(buf[Omode: Omode+Lmode], 8); + mem.size = intof(buf[Osize: Osize+Lsize], 10); + if(buf[Ofmag] != ARFMAG0 || buf[Ofmag+1] != ARFMAG1) + phaseerr(b.offset()-big SAR_HDR); + return mem; +} + +intof(a: array of byte, base: int): int +{ + for(i := len a; i > 0; i--) + if(a[i-1] != byte ' '){ + a = a[0:i]; + break; + } + (n, s) := str->toint(string a, base); + if(s != nil){ + sys->fprint(stderr, "ar: invalid integer in archive member's header: %q\n", string a); + raise "fail:error"; + } + return n; +} + +Armember.wrhdr(mem: self ref Armember, fd: ref Sys->FD) +{ + b := array[SAR_HDR] of {* => byte ' '}; + nm := array of byte mem.name; + if(len nm > Lname) + nm = nm[0:Lname]; + b[Oname:] = nm; + b[Odate:] = sys->aprint("%-12ud", mem.date); + b[Ouid:] = sys->aprint("%-6d", 0); + b[Ogid:] = sys->aprint("%-6d", 0); + b[Omode:] = sys->aprint("%-8uo", mem.mode); + b[Osize:] = sys->aprint("%-10ud", mem.size); + b[Ofmag] = ARFMAG0; + b[Ofmag+1] = ARFMAG1; + mustwrite(fd, b, len b); +} + +# +# make a new member from the given file, with the file's contents +# +Armember.new(name: string, fd: ref Sys->FD): ref Armember +{ + mem := ref Armember; + mem.replace(name, fd); + return mem; +} + +# +# replace the contents of an existing member +# +Armember.replace(mem: self ref Armember, name: string, fd: ref Sys->FD) +{ + (ok, d) := sys->fstat(fd); + if(ok < 0){ + sys->fprint(stderr, "ar: cannot stat %s: %r\n", name); + raise "fail:no stat"; + } + mem.name = trim(name); + mem.date = d.mtime; + mem.uid = 0; + mem.gid = 0; + mem.mode = d.mode & 8r777; + mem.size = int d.length; + if(big mem.size != d.length){ + sys->fprint(stderr, "ar: file %s too big\n", name); + raise "fail:error"; + } + mem.fd = fd; + mem.contents = nil; # will be copied across from fd when needed +} + +# +# read the contents of an archive member +# +Armember.read(mem: self ref Armember, b: ref Iobuf): int +{ + if(mem.contents != nil) + return len mem.contents; + mem.contents = buffer(mem.size + (mem.size&1)); + n := b.read(mem.contents, len mem.contents); + if(n != len mem.contents){ + if(n >= 0) + sys->werrstr("unexpected end-of-file"); + rderr(); + } + return n; +} + +mustwrite(fd: ref Sys->FD, buf: array of byte, n: int) +{ + if(sys->write(fd, buf, n) != n) + wrerr(); +} + +# +# write an archive member to ofd, including header +# +Armember.write(mem: self ref Armember, ofd: ref Sys->FD) +{ + mem.wrhdr(ofd); + if(mem.contents != nil){ + mustwrite(ofd, mem.contents, len mem.contents); + return; + } + if(mem.fd == nil) + raise "ar: write nil fd"; + buf := array[Sys->ATOMICIO] of byte; # could be bigger + for(nr := mem.size; nr > 0;){ + n := nr; + if(n > len buf) + n = len buf; + n = sys->read(mem.fd, buf, n); + if(n <= 0){ + if(n == 0) + sys->werrstr("unexpected end-of-file"); + rderr(); + } + mustwrite(ofd, buf, n); + nr -= n; + } + if(mem.size & 1) + mustwrite(ofd, array[] of {byte '\n'}, 1); +} + +# +# seek past the current member's contents in b +# +Armember.skip(mem: self ref Armember, b: ref Iobuf) +{ + b.seek(big(mem.size + (mem.size&1)), 1); +} + +# +# copy a member's contents from memory or directly from an archive to another file +# +Armember.copyout(mem: self ref Armember, b: ref Iobuf, ofd: ref Sys->FD) +{ + if(mem.contents != nil){ + mustwrite(ofd, mem.contents, len mem.contents); + return; + } + buf := array[Sys->ATOMICIO] of byte; # could be bigger + for(nr := mem.size; nr > 0;){ + n := nr; + if(n > len buf) + n = len buf; + n = b.read(buf, n); + if(n <= 0){ + if(n == 0) + sys->werrstr("unexpected end-of-file"); + rderr(); + } + mustwrite(ofd, buf, n); + nr -= n; + } + if(mem.size & 1) + b.getc(); +} + +# +# Temp file I/O subsystem. We attempt to cache all three temp files in +# core. When we run out of memory we spill to disk. +# The I/O model assumes that temp files: +# 1) are only written on the end +# 2) are only read from the beginning +# 3) are only read after all writing is complete. +# The architecture uses one control block per temp file. Each control +# block anchors a chain of buffers, each containing an archive contents. +# +Arfile.new(): ref Arfile +{ + return ref Arfile; +} + +# +# copy the contents of mem at b into the temporary +# +Arfile.copy(ap: self ref Arfile, b: ref Iobuf, mem: ref Armember) +{ + mem.read(b); + ap.insert(mem); +} + +# +# insert a contents buffer into the contents chain +# +Arfile.insert(ap: self ref Arfile, mem: ref Armember) +{ + mem.next = nil; + if(ap.head == nil) + ap.head = mem; + else + ap.tail.next = mem; + ap.tail = mem; +} + +# +# stream the contents in a temp file to the file referenced by 'fd'. +# +Arfile.stream(ap: self ref Arfile, fd: ref Sys->FD) +{ + if(ap.fd != nil){ # copy prefix from disk + buf := array[Sys->ATOMICIO] of byte; + sys->seek(ap.fd, big 0, 0); + while((n := sys->read(ap.fd, buf, len buf)) > 0) + mustwrite(fd, buf, n); + if(n < 0) + rderr(); + ap.fd = nil; + } + # dump the in-core buffers, which always follow the contents in the temp file + for(mem := ap.head; mem != nil; mem = mem.next) + mem.write(fd); +} + +# +# spill a member's contents to disk +# + +totalmem := 0; +warned := 0; +tn := 0; + +Arfile.page(ap: self ref Arfile): int +{ + mem := ap.head; + if(ap.fd == nil && !warned){ + pid := sys->pctl(0, nil); + for(i := 0;; i++){ + name := sys->sprint("/tmp/art%d.%d.%d", pid, tn, i); + ap.fd = sys->create(name, Sys->OEXCL | Sys->ORDWR | Sys->ORCLOSE, 8r600); + if(ap.fd != nil) + break; + if(i >= 20){ + warned =1; + sys->fprint(stderr,"ar: warning: can't create temp file %s: %r\n", name); + return 0; # we'll simply use the memory + } + } + tn++; + } + mem.write(ap.fd); + ap.head = mem.next; + if(ap.tail == mem) + ap.tail = mem.next; + totalmem -= len mem.contents; + return 1; +} + +# +# account for the space taken by a contents's contents, +# pushing earlier contentss to disk to keep the space below a +# reasonable level +# + +buffer(n: int): array of byte +{ +Flush: + while(totalmem + n > 1024*1024){ + for(i := 0; i < len parts; i++) + if(parts[i] != nil && parts[i].page()) + continue Flush; + break; + } + totalmem += n; + return array[n] of byte; +} diff --git a/appl/cmd/cddb.b b/appl/cmd/cddb.b new file mode 100644 index 00000000..6265ba24 --- /dev/null +++ b/appl/cmd/cddb.b @@ -0,0 +1,257 @@ +implement Cddb; + +# this is a near transliteration of Plan 9 source, and subject to the Lucent Public License 1.02 + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; + +include "string.m"; + str: String; + +include "arg.m"; + +Cddb: module +{ + init: fn(nil: ref Draw->Context, nil: list of string); +}; + +server := "freedb.freedb.org"; +debug := 0; +tflag := 0; +Tflag := 0; + +Track: adt { + n: int; + title: string; +}; + +Toc: adt { + diskid: int; + ntrack: int; + title: string; + track: array of Track; +}; + +DPRINT(fd: int, s: string) +{ + if(debug) + sys->fprint(sys->fildes(fd), "%s", s); +} + +dumpcddb(t: ref Toc) +{ + sys->print("title %s\n", t.title); + for(i:=0; iprint("%d\t%s\t%d:%2.2d\n", i+1, t.track[i].title, s/60, s%60); + } + else + sys->print("%d\t%s\n", i+1, t.track[i].title); + } + if(Tflag){ + s := t.track[i].n; + sys->print("Total time: %d:%2.2d\n", s/60, s%60); + } +} + +cddbfilltoc(t: ref Toc): int +{ + (ok, conn) := sys->dial(netmkaddr(server, "tcp", "888"), nil); + if(ok < 0) { + sys->fprint(sys->fildes(2), "cddb: cannot dial %s: %r\n", server); + return -1; + } + bin := bufio->fopen(conn.dfd, Bufio->OREAD); + + if((p:=getline(bin)) == nil || atoi(p)/100 != 2) + return died(p); + + sys->fprint(conn.dfd, "cddb hello gre plan9 9cd 1.0\r\n"); + if((p = getline(bin)) == nil || atoi(p)/100 != 2) + return died(p); + + # + # Protocol level 6 is the same as level 5 except that + # the character set is now UTF-8 instead of ISO-8859-1. + # + sys->fprint(conn.dfd, "proto 6\r\n"); + if((p = getline(bin)) == nil || atoi(p)/100 != 2) + return died(p); + DPRINT(2, sys->sprint("%s\n", p)); + + sys->fprint(conn.dfd, "cddb query %8.8ux %d", t.diskid, t.ntrack); + DPRINT(2, sys->sprint("cddb query %8.8ux %d", t.diskid, t.ntrack)); + for(i:=0; ifprint(conn.dfd, " %d", t.track[i].n); + DPRINT(2, sys->sprint(" %d", t.track[i].n)); + } + sys->fprint(conn.dfd, " %d\r\n", t.track[t.ntrack].n); + DPRINT(2, sys->sprint(" %d\r\n", t.track[t.ntrack].n)); + + if((p = getline(bin)) == nil || atoi(p)/100 != 2) + return died(p); + DPRINT(2, sys->sprint("cddb: %s\n", p)); + (nf, fl) := sys->tokenize(p, " \t\n\r"); + if(nf < 1) + return died(p); + + categ, id: string; + case atoi(hd fl) { + 200 => # exact match + if(nf < 3) + return died(p); + categ = hd tl fl; + id = hd tl tl fl; + 211 => # close matches + if((p = getline(bin)) == nil) + return died(nil); + if(p[0] == '.') # no close matches? + return died(nil); + + # accept first match + (nsf, f) := sys->tokenize(p, " \t\n\r"); + if(nsf < 2) + return died(p); + categ = hd f; + id = hd tl f; + + # snarf rest of buffer + while(p[0] != '.') { + if((p = getline(bin)) == nil) + return died(p); + DPRINT(2, sys->sprint("cddb: %s\n", p)); + } + 202 or # no match + * => + return died(p); + } + + t.title = ""; + for(i=0; ifprint(conn.dfd, "cddb read %s %s\r\n", categ, id); + do { + if((p = getline(bin)) == nil) + return died(nil); +DPRINT(2, sys->sprint("cddb %s\n", p)); + if(len p >= 7 && p[0:7] == "DTITLE=") + t.title += p[7:]; + else if(len p >= 6 && p[0:6] == "TTITLE"&& isdigit(p[6])) { + i = atoi(p[6:]); + if(i < t.ntrack) { + p = p[6:]; + while(p != nil && isdigit(p[0])) + p = p[1:]; + if(p != nil && p[0] == '=') + p = p[1:]; + t.track[i].title += p; + } + } + } while(p[0] != '.'); + + sys->fprint(conn.dfd, "quit\r\n"); + + return 0; +} + +getline(f: ref Iobuf): string +{ + p := f.gets('\n'); + while(p != nil && isspace(p[len p-1])) + p = p[0: len p-1]; + return p; +} + +isdigit(c: int): int +{ + return c>='0' && c <= '9'; +} + +isspace(c: int): int +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; +} + +died(p: string): int +{ + sys->fprint(sys->fildes(2), "cddb: error talking to server\n"); + if(p != nil){ + p = p[0:len p-1]; + sys->fprint(sys->fildes(2), "cddb: server says: %s\n", p); + } + return -1; +} + +init(nil: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + str = load String String->PATH; + + arg := load Arg Arg->PATH; + arg->init(args); + arg->setusage("cddb [-DTt] [-s server] query diskid n ..."); + while((o := arg->opt()) != 0) + case o { + 'D' => debug = 1; + 's' => server = arg->earg(); + 'T' => Tflag = 1; tflag = 1; + 't' => tflag = 1; + * => arg->usage(); + } + args = arg->argv(); + argc := len args; + if(argc < 3 || hd args != "query") + arg->usage(); + arg = nil; + + ntrack := atoi(hd tl tl args); + toc := ref Toc(str->toint(hd tl args, 16).t0, ntrack, nil, array[ntrack+1] of Track); + if(argc != 3+toc.ntrack+1){ + sys->fprint(sys->fildes(2), "cddb: argument count does not match given ntrack"); + raise "fail:error"; + } + args = tl tl tl args; + + for(i:=0; i<=toc.ntrack; i++){ # <=? + toc.track[i].n = atoi(hd args); + args = tl args; + } + + if(cddbfilltoc(toc) < 0) + raise "fail:whoops"; + + dumpcddb(toc); +} + +netmkaddr(addr, net, svc: string): string +{ + if(net == nil) + net = "net"; + (n, nil) := sys->tokenize(addr, "!"); + if(n <= 1){ + if(svc== nil) + return sys->sprint("%s!%s", net, addr); + return sys->sprint("%s!%s!%s", net, addr, svc); + } + if(svc == nil || n > 2) + return addr; + return sys->sprint("%s!%s", addr, svc); +} + +atoi(s: string): int +{ + return int s; +} diff --git a/appl/cmd/lookman.b b/appl/cmd/lookman.b deleted file mode 100644 index 53557c8b..00000000 --- a/appl/cmd/lookman.b +++ /dev/null @@ -1,250 +0,0 @@ -implement Lookman; -include "sys.m"; -include "bufio.m"; -include "draw.m"; - - -Lookman : module { - init : fn (ctxt : ref Draw->Context, argv : list of string); -}; - -sys : Sys; -bufio : Bufio; -Iobuf : import bufio; - -ctype := array [256] of { * => byte 0 }; - -MANINDEX : con "/man/index"; - -init(nil : ref Draw->Context, argv : list of string) -{ - sys = load Sys Sys->PATH; - bufio = load Bufio Bufio->PATH; - - if (bufio == nil) - raise "init:fail"; - - # setup our char conversion table - # map upper-case to lower-case - for (i := 'A'; i <= 'Z'; i++) - ctype[i] = byte ((i - 'A') + 'a'); - - # only allow the following chars - okchars := "abcdefghijklmnopqrstuvwxyz0123456789+.:½ "; - for (i = 0; i < len okchars; i++) { - ch := okchars[i]; - ctype[ch] = byte ch; - } - - stdout := bufio->fopen(sys->fildes(1), Sys->OWRITE); - - argv = tl argv; - paths := lookup(argv); - for (; paths != nil; paths = tl paths) - stdout.puts(sys->sprint("%s\n", hd paths)); - stdout.flush(); -} - -lookup(words : list of string) : list of string -{ - # open the index file - manindex := bufio->open(MANINDEX, Sys->OREAD); - if (manindex == nil) { - sys->print("cannot open %s: %r\n", MANINDEX); - return nil; - } - - # convert to lower-case and discard funny chars - keywords : list of string; - for (; words != nil; words = tl words) { - word := hd words; - kw := ""; - for (i := 0; i < len word; i++) { - ch := word[i]; - if (ch < len ctype && ctype[ch] != byte 0) - kw[len kw] = int ctype[ch]; - } - if (kw != "") - keywords = kw :: keywords; - } - - if (keywords == nil) - return nil; - - keywords = sortuniq(keywords); - matches : list of list of string; - - for (; keywords != nil; keywords = tl keywords) { - kw := hd keywords; - matchlist := look(manindex, '\t', kw); - pathlist : list of string = nil; - for (; matchlist != nil; matchlist = tl matchlist) { - line := hd matchlist; - (n, toks) := sys->tokenize(line, "\t"); - if (n != 2) - continue; - pathlist = hd tl toks :: pathlist; - } - if (pathlist != nil) - matches = pathlist :: matches; - } - - return intersect(matches); -} - -getentry(iob : ref Iobuf) : (string, string) -{ - while ((s := iob.gets('\n')) != nil) { - if (s[len s -1] == '\n') - s = s[0:len s -1]; - if (s == nil) - continue; - (n, toks) := sys->tokenize(s, "\t"); - if (n != 2) - continue; - return (hd toks, hd tl toks); - } - return (nil, nil); -} - -sortuniq(strlist : list of string) : list of string -{ - strs := array [len strlist] of string; - for (i := 0; strlist != nil; (i, strlist) = (i+1, tl strlist)) - strs[i] = hd strlist; - - # simple sort (greatest first) - for (i = 0; i < len strs - 1; i++) { - for (j := i+1; j < len strs; j++) - if (strs[i] < strs[j]) - (strs[i], strs[j]) = (strs[j], strs[i]); - } - - # construct list (result is ascending) - r : list of string; - prev := ""; - for (i = 0; i < len strs; i++) { - if (strs[i] != prev) { - r = strs[i] :: r; - prev = strs[i]; - } - } - return r; -} - -intersect(strlists : list of list of string) : list of string -{ - if (strlists == nil) - return nil; - - okl := hd strlists; - for (strlists = tl strlists; okl != nil && strlists != nil; strlists = tl strlists) { - find := hd strlists; - found : list of string = nil; - for (; okl != nil; okl = tl okl) { - ok := hd okl; - for (scanl := find; scanl != nil; scanl = tl scanl) { - scan := hd scanl; - if (scan == ok) { - found = ok :: found; - break; - } - } - } - okl = found; - } - return sortuniq(okl); -} - -# binary search for key in f. -# based on Plan 9 look.c -# -look(f: ref Iobuf, sep: int, key: string): list of string -{ - bot := mid := 0; - top := int f.seek(big 0, Sys->SEEKEND); - key = canon(key, sep); - - for (;;) { - mid = (top + bot) / 2; - f.seek(big mid, Sys->SEEKSTART); - c: int; - do { - c = f.getb(); - mid++; - } while (c != Bufio->EOF && c != Bufio->ERROR && c != '\n'); - (entry, eof) := getword(f); - if (entry == nil && eof) - break; - entry = canon(entry, sep); - case comparewords(key, entry) { - -2 or -1 or 0 => - if (top <= mid) - break; - top = mid; - continue; - 1 or 2 => - bot = mid; - continue; - } - break; - } - matchlist : list of string; - f.seek(big bot, Sys->SEEKSTART); - for (;;) { - (entry, eof) := getword(f); - if (entry == nil && eof) - return matchlist; - word := canon(entry, sep); - case comparewords(key, word) { - -1 or 0 => - matchlist = entry :: matchlist; - continue; - 1 or 2 => - continue; - } - break; - } - return matchlist; -} - -comparewords(s, t: string): int -{ - if (s == t) - return 0; - i := 0; - for (; i < len s && i < len t && s[i] == t[i]; i++) - ; - if (i >= len s) - return -1; - if (i >= len t) - return 1; - if (s[i] < t[i]) - return -2; - return 2; -} - -getword(f: ref Iobuf): (string, int) -{ - ret := ""; - for (;;) { - c := f.getc(); - if (c == Bufio->EOF || c == Bufio->ERROR) - return (ret, 0); - if (c == '\n') - break; - ret[len ret] = c; - } - return (ret, 1); -} - -canon(s: string, sep: int): string -{ - if (sep < 0) - return s; - i := 0; - for (; i < len s; i++) - if (s[i] == sep) - break; - return s[0:i]; -} diff --git a/appl/cmd/man2html.b b/appl/cmd/man2html.b new file mode 100644 index 00000000..9eda5940 --- /dev/null +++ b/appl/cmd/man2html.b @@ -0,0 +1,1328 @@ +implement Man2html; + +include "sys.m"; + stderr: ref Sys->FD; + sys: Sys; + print, fprint, sprint: import sys; + + +include "bufio.m"; + +include "draw.m"; + +include "daytime.m"; + dt: Daytime; + +include "string.m"; + str: String; + +Man2html: module +{ + init: fn(ctxt: ref Draw->Context, args: list of string); +}; + +Runeself: con 16r80; +false, true: con iota; + +Troffspec: adt { + name: string; + value: string; +}; + +tspec := array [] of { Troffspec + ("ff", "ff"), + ("fi", "fi"), + ("fl", "fl"), + ("Fi", "ffi"), + ("ru", "_"), + ("em", "­"), + ("14", "¼"), + ("12", "½"), + ("co", "©"), + ("de", "°"), + ("dg", "¡"), + ("fm", "´"), + ("rg", "®"), +# ("bu", "*"), + ("bu", "•"), + ("sq", "¤"), + ("hy", "-"), + ("pl", "+"), + ("mi", "-"), + ("mu", "×"), + ("di", "÷"), + ("eq", "="), + ("==", "=="), + (">=", ">="), + ("<=", "<="), + ("!=", "!="), + ("+-", "±"), + ("no", "¬"), + ("sl", "/"), + ("ap", "&"), + ("~=", "~="), + ("pt", "oc"), + ("gr", "GRAD"), + ("->", "->"), + ("<-", "<-"), + ("ua", "^"), + ("da", "v"), + ("is", "Integral"), + ("pd", "DIV"), + ("if", "oo"), + ("sr", "-/"), + ("sb", "(~"), + ("sp", "~)"), + ("cu", "U"), + ("ca", "(^)"), + ("ib", "(="), + ("ip", "=)"), + ("mo", "C"), + ("es", "Ø"), + ("aa", "´"), + ("ga", "`"), + ("ci", "O"), + ("L1", "Lucent"), + ("sc", "§"), + ("dd", "++"), + ("lh", "<="), + ("rh", "=>"), + ("lt", "("), + ("rt", ")"), + ("lc", "|"), + ("rc", "|"), + ("lb", "("), + ("rb", ")"), + ("lf", "|"), + ("rf", "|"), + ("lk", "|"), + ("rk", "|"), + ("bv", "|"), + ("ts", "s"), + ("br", "|"), + ("or", "|"), + ("ul", "_"), + ("rn", " "), + ("*p", "PI"), + ("**", "*"), +}; + + Entity: adt { + name: string; + value: int; + }; + Entities: array of Entity; + +Entities = array[] of { + Entity( "¡", '¡' ), + Entity( "¢", '¢' ), + Entity( "£", '£' ), + Entity( "¤", '¤' ), + Entity( "¥", '¥' ), + Entity( "¦", '¦' ), + Entity( "§", '§' ), + Entity( "¨", '¨' ), + Entity( "©", '©' ), + Entity( "ª", 'ª' ), + Entity( "«", '«' ), + Entity( "¬", '¬' ), + Entity( "­", '­' ), + Entity( "®", '®' ), + Entity( "¯", '¯' ), + Entity( "°", '°' ), + Entity( "±", '±' ), + Entity( "²", '²' ), + Entity( "³", '³' ), + Entity( "´", '´' ), + Entity( "µ", 'µ' ), + Entity( "¶", '¶' ), + Entity( "·", '·' ), + Entity( "¸", '¸' ), + Entity( "¹", '¹' ), + Entity( "º", 'º' ), + Entity( "»", '»' ), + Entity( "¼", '¼' ), + Entity( "½", '½' ), + Entity( "¾", '¾' ), + Entity( "¿", '¿' ), + Entity( "À", 'À' ), + Entity( "Á", 'Á' ), + Entity( "Â", 'Â' ), + Entity( "Ã", 'Ã' ), + Entity( "Ä", 'Ä' ), + Entity( "Å", 'Å' ), + Entity( "Æ", 'Æ' ), + Entity( "Ç", 'Ç' ), + Entity( "È", 'È' ), + Entity( "É", 'É' ), + Entity( "Ê", 'Ê' ), + Entity( "Ë", 'Ë' ), + Entity( "Ì", 'Ì' ), + Entity( "Í", 'Í' ), + Entity( "Î", 'Î' ), + Entity( "Ï", 'Ï' ), + Entity( "Ð", 'Ð' ), + Entity( "Ñ", 'Ñ' ), + Entity( "Ò", 'Ò' ), + Entity( "Ó", 'Ó' ), + Entity( "Ô", 'Ô' ), + Entity( "Õ", 'Õ' ), + Entity( "Ö", 'Ö' ), + Entity( "&215;", '×' ), + Entity( "Ø", 'Ø' ), + Entity( "Ù", 'Ù' ), + Entity( "Ú", 'Ú' ), + Entity( "Û", 'Û' ), + Entity( "Ü", 'Ü' ), + Entity( "Ý", 'Ý' ), + Entity( "Þ", 'Þ' ), + Entity( "ß", 'ß' ), + Entity( "à", 'à' ), + Entity( "á", 'á' ), + Entity( "â", 'â' ), + Entity( "ã", 'ã' ), + Entity( "ä", 'ä' ), + Entity( "å", 'å' ), + Entity( "æ", 'æ' ), + Entity( "ç", 'ç' ), + Entity( "è", 'è' ), + Entity( "é", 'é' ), + Entity( "ê", 'ê' ), + Entity( "ë", 'ë' ), + Entity( "ì", 'ì' ), + Entity( "í", 'í' ), + Entity( "î", 'î' ), + Entity( "ï", 'ï' ), + Entity( "ð", 'ð' ), + Entity( "ñ", 'ñ' ), + Entity( "ò", 'ò' ), + Entity( "ó", 'ó' ), + Entity( "ô", 'ô' ), + Entity( "õ", 'õ' ), + Entity( "ö", 'ö' ), + Entity( "&247;", '÷' ), + Entity( "ø", 'ø' ), + Entity( "ù", 'ù' ), + Entity( "ú", 'ú' ), + Entity( "û", 'û' ), + Entity( "ü", 'ü' ), + Entity( "ý", 'ý' ), + Entity( "þ", 'þ' ), + Entity( "ÿ", 'ÿ' ), # ÿ + + Entity( "&#SPACE;", ' ' ), + Entity( "&#RS;", '\n' ), + Entity( "&#RE;", '\r' ), + Entity( """, '"' ), + Entity( "&", '&' ), + Entity( "<", '<' ), + Entity( ">", '>' ), + + Entity( "CAP-DELTA", 'Δ' ), + Entity( "ALPHA", 'α' ), + Entity( "BETA", 'β' ), + Entity( "DELTA", 'δ' ), + Entity( "EPSILON", 'ε' ), + Entity( "THETA", 'θ' ), + Entity( "MU", 'μ' ), + Entity( "PI", 'π' ), + Entity( "TAU", 'τ' ), + Entity( "CHI", 'χ' ), + + Entity( "<-", '←' ), + Entity( "^", '↑' ), + Entity( "->", '→' ), + Entity( "v", '↓' ), + Entity( "!=", '≠' ), + Entity( "<=", '≤' ), + Entity( nil, 0 ), +}; + + +Hit: adt { + glob: string; + chap: string; + mtype: string; + page: string; +}; + +Lnone, Lordered, Lunordered, Ldef, Lother: con iota; # list types + +Chaps: adt { + name: string; + primary: int; +}; + +Types: adt { + name: string; + desc: string; +}; + + +# having two separate flags here allows for inclusion of old-style formatted pages +# under a new-style three-level tree +Oldstyle: adt { + names: int; # two-level directory tree? + fmt: int; # old internal formats: e.g., "B" font means "L"; name in .TH in all caps +}; + +Href: adt { + title: string; + chap: string; + mtype: string; + man: string; +}; + +# per-thread global data +Global: adt { + bufio: Bufio; + bin: ref Bufio->Iobuf; + bout: ref Bufio->Iobuf; + topname: string; # name of the top level categories in the manual + chaps: array of Chaps; # names of top-level partitions of this manual + types: array of Types; # names of second-level partitions + oldstyle: Oldstyle; + mantitle: string; + mandir: string; + thisone: Hit; # man page we're displaying + mtime: int; # last modification time of thisone + href: Href; # hrefs of components of this man page + hits: array of Hit; + nhits: int; + list_type: int; + pm: string; # proprietary marking + def_goobie: string; # deferred goobie + sop: int; # output at start of paragraph? + sol: int; # input at start of line? + broken: int; # output at a break? + fill: int; # in fill mode? + pre: int; # in PRE block? + example: int; # an example active? + ipd: int; # emit inter-paragraph distance? + indents: int; + hangingdt: int; + curfont: string; # current font + prevfont: string; # previous font + lastc: int; # previous char from input scanner + def_sm: int; # amount of deferred "make smaller" request + + mk_href_chap: fn(g: self ref Global, chap: string); + mk_href_man: fn(g: self ref Global, man: string, oldstyle: int); + mk_href_mtype: fn(g: self ref Global, chap, mtype: string); + dobreak: fn(g: self ref Global); + print: fn(g: self ref Global, s: string); + softbr: fn(g: self ref Global): string; + softp: fn(g: self ref Global): string; +}; + + +usage() +{ + sys->fprint(stderr, "Usage: man2html file [section]\n"); + raise "fail:usage"; +} + + +init(nil: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + stderr = sys->fildes(2); + str = load String String->PATH; + dt = load Daytime Daytime->PATH; + g := Global_init(); + if(args != nil) + args = tl args; + if(args == nil) + usage(); + page := hd args; + args = tl args; + section := "1"; + if(args != nil) + section = hd args; + hit := Hit ("", "man", section, page); + domanpage(g, hit); + g.bufio->g.bout.flush(); +} + +# remove markup from a string +# doesn't handle nested/quoted delimiters +demark(s: string): string +{ + t: string; + clean := true; + for (i := 0; i < len s; i++) { + case s[i] { + '<' => + clean = false; + '>' => + clean = true; + * => + if (clean) + t[len t] = s[i]; + } + } + return t; +} + + +# +# Convert an individual man page to HTML and output. +# +domanpage(g: ref Global, man: Hit) +{ + file := man.page; + g.bin = g.bufio->open(file, Bufio->OREAD); + g.bout = g.bufio->fopen(sys->fildes(1), Bufio->OWRITE); + if (g.bin == nil) { + fprint(stderr, "Cannot open %s: %r\n", file); + return; + } + (err, info) := sys->fstat(g.bin.fd); + if (! err) { + g.mtime = info.mtime; + } + g.thisone = man; + while ((p := getnext(g)) != nil) { + c := p[0]; + if (c == '.' && g.sol) { + if (g.pre) { + g.print(""); + g.pre = false; + } + dogoobie(g, false); + dohangingdt(g); + } else if (g.def_goobie != nil || g.def_sm != 0) { + g.bufio->g.bin.ungetc(); + dogoobie(g, true); + } else if (c == '\n') { + g.print(p); + dohangingdt(g); + } else + g.print(p); + } + if (g.pm != nil) { + g.print("


\n"); + g.print(g.pm); + g.print("
\n"); + } + closeall(g, 0); + rev(g, g.bin); +} + +dogoobie(g: ref Global, deferred: int) +{ + # read line, translate special chars + line: string; + while ((token := getnext(g)) != "\n") { + if (token == nil) + return; + line += token; + } + + # parse into arguments + argl, rargl: list of string; # create reversed version, then invert + while ((line = str->drop(line, " \t")) != nil) + if (line[0] == '"') { + (token, line) = split(line[1:], '"'); + rargl = token :: rargl; + } else { + (token, line) = str->splitl(line, " \t"); + rargl = token :: rargl; + } + + if (rargl == nil && !deferred) + return; + for ( ; rargl != nil; rargl = tl rargl) + argl = hd rargl :: argl; + + def_sm := g.def_sm; + if (deferred && def_sm > 0) { + g.print(sprint("", def_sm)); + if (g.def_goobie == nil) + argl = "dS" :: argl; # dS is our own local creation + } + + subgoobie(g, argl); + + if (deferred && def_sm > 0) { + g.def_sm = 0; + g.print(""); + } +} + +subgoobie(g: ref Global, argl: list of string) +{ + if (g.def_goobie != nil) { + argl = g.def_goobie :: argl; + g.def_goobie = nil; + if (tl argl == nil) + return; + } + + # the command part is at most two characters, but may be concatenated with the first arg + cmd := hd argl; + argl = tl argl; + if (len cmd > 2) { + cmd = cmd[0:2]; + argl = cmd[2:] :: argl; + } + + case cmd { + + "B" or "I" or "L" or "R" => + font(g, cmd, argl); # "R" macro implicitly generated by deferred R* macros + + "BI" or "BL" or "BR" or + "IB" or "IL" or + "LB" or "LI" or + "RB" or "RI" or "RL" => + altfont(g, cmd[0:1], cmd[1:2], argl, true); + + "IR" or "LR" => + anchor(g, cmd[0:1], cmd[1:2], argl); # includes man page refs ("IR" is old style, "LR" is new) + + "dS" => + printargs(g, argl); + g.print("\n"); + + "1C" or "2C" or "DT" or "TF" => # ignore these + return; + + "P" or "PP" or "LP" => + g_PP(g); + + "EE" => g_EE(g); + "EX" => g_EX(g); + "HP" => g_HP_TP(g, 1); + "IP" => g_IP(g, argl); + "PD" => g_PD(g, argl); + "PM" => g_PM(g, argl); + "RE" => g_RE(g); + "RS" => g_RS(g); + "SH" => g_SH(g, argl); + "SM" => g_SM(g, argl); + "SS" => g_SS(g, argl); + "TH" => g_TH(g, argl); + "TP" => g_HP_TP(g, 3); + + "br" => g_br(g); + "sp" => g_sp(g, argl); + "ti" => g_br(g); + "nf" => g_nf(g); + "fi" => g_fi(g); + "ft" => g_ft(g, argl); + + * => return; # ignore unrecognized commands + } + +} + +g_br(g: ref Global) +{ + if (g.hangingdt != 0) { + g.print("
"); + g.hangingdt = 0; + } else if (g.fill && ! g.broken) + g.print("
\n"); + g.broken = true; +} + +g_EE(g: ref Global) +{ + g.print("\n"); + g.fill = true; + g.broken = true; + g.example = false; +} + +g_EX(g: ref Global) +{ + g.print("
");
+	if (! g.broken)
+		g.print("\n");
+	g.sop = true;
+	g.fill = false;
+	g.broken = true;
+	g.example = true;
+}
+
+g_fi(g: ref Global)
+{
+	if (g.fill)
+		return;
+	g.fill = true;
+	g.print("

\n"); + g.broken = true; + g.sop = true; +} + +g_ft(g: ref Global, argl: list of string) +{ + font: string; + arg: string; + + if (argl == nil) + arg = "P"; + else + arg = hd argl; + + if (g.curfont != nil) + g.print(sprint("", g.curfont)); + + case arg { + "2" or "I" => + font = "I"; + "3" or "B" => + font = "B"; + "5" or "L" => + font = "TT"; + "P" => + font = g.prevfont; + * => + font = nil; + } + g.prevfont = g.curfont; + g.curfont = font; + if (g.curfont != nil) + if (g.fill) + g.print(sprint("<%s>", g.curfont)); + else + g.print(sprint("<%s style=\"white-space: pre\">", g.curfont)); +} + +# level == 1 is a .HP; level == 3 is a .TP +g_HP_TP(g: ref Global, level: int) +{ + case g.list_type { + Ldef => + if (g.hangingdt != 0) + g.print("

"); + g.print(g.softbr() + "
"); + * => + closel(g); + g.list_type = Ldef; + g.print("
\n" + g.softbr() + "
"); + } + g.hangingdt = level; + g.broken = true; +} + +g_IP(g: ref Global, argl: list of string) +{ + case g.list_type { + + Lordered or Lunordered or Lother => + ; # continue with an existing list + + * => + # figure out the type of a new list and start it + closel(g); + arg := ""; + if (argl != nil) + arg = hd argl; + case arg { + "1" or "i" or "I" or "a" or "A" => + g.list_type = Lordered; + g.print(sprint("
    \n", arg)); + "*" or "•" or "•" => + g.list_type = Lunordered; + g.print("
      \n"); + "○" or "○"=> + g.list_type = Lunordered; + g.print("
        \n"); + "□" or "□" => + g.list_type = Lunordered; + g.print("
          \n"); + * => + g.list_type = Lother; + g.print("
          \n"); + } + } + + # actually do this list item + case g.list_type { + Lother => + g.print(g.softp()); # make sure there's space before each list item + if (argl != nil) { + g.print("
          "); + printargs(g, argl); + } + g.print("\n
          "); + + Lordered or Lunordered => + g.print(g.softp() + "
        • "); + } + g.broken = true; +} + +g_nf(g: ref Global) +{ + if (! g.fill) + return; + g.fill = false; + g.print("
          \n");
          +	g.broken = true;
          +	g.sop = true;
          +	g.pre = true;
          +}
          +
          +g_PD(g: ref Global, argl: list of string)
          +{
          +	if (len argl == 1 && hd argl == "0")
          +		g.ipd = false;
          +	else
          +		g.ipd = true;
          +}
          +
          +g_PM(g: ref Global, argl: list of string)
          +{
          +	code := "P";
          +	if (argl != nil)
          +		code = hd argl;
          +	case code {
          +	* =>		# includes "1" and "P"
          +		g.pm = "Lucent Technologies - Proprietary\n" +
          +			"
          Use pursuant to Company Instructions.\n"; + "2" or "RS" => + g.pm = "Lucent Technologies - Proprietary (Restricted)\n" + + "
          Solely for authorized persons having a need to know\n" + + "
          pursuant to Company Instructions.\n"; + "3" or "RG" => + g.pm = "Lucent Technologies - Proprietary (Registered)\n" + + "
          Solely for authorized persons having a need to know\n" + + "
          and subject to cover sheet instructions.\n"; + "4" or "CP" => + g.pm = "SEE PROPRIETARY NOTICE ON COVER PAGE\n"; + "5" or "CR" => + g.pm = "Copyright xxxx Lucent Technologies\n" + # should fill in the year from the date register + "
          All Rights Reserved.\n"; + "6" or "UW" => + g.pm = "THIS DOCUMENT CONTAINS PROPRIETARY INFORMATION OF\n" + + "
          LUCENT TECHNOLOGIES INC. AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN\n" + + "
          ACCORDANCE WITH APPLICABLE AGREEMENTS.\n" + + "
          Unpublished & Not for Publication\n"; + } +} + +g_PP(g: ref Global) +{ + closel(g); + reset_font(g); + p := g.softp(); + if (p != nil) + g.print(p); + g.sop = true; + g.broken = true; +} + +g_RE(g: ref Global) +{ + g.print("
        • \n"); + g.indents--; + g.broken = true; +} + +g_RS(g: ref Global) +{ + g.print("
          \n
          "); + g.indents++; + g.broken = true; +} + +g_SH(g: ref Global, argl: list of string) +{ + closeall(g, 1); # .SH is top-level list item + if (g.example) + g_EE(g); + if (g.fill && ! g.sop) + g.print("

          "); + g.print("

          "); + printargs(g, argl); + g.print("

          \n"); + g.print("
          \n"); + g.sop = true; + g.broken = true; +} + +g_SM(g: ref Global, argl: list of string) +{ + g.def_sm++; # can't use def_goobie, lest we collide with a deferred font macro + if (argl == nil) + return; + g.print(sprint("", g.def_sm)); + printargs(g, argl); + g.print("\n"); + g.def_sm = 0; +} + +g_sp(g: ref Global, argl: list of string) +{ + if (g.sop && g.fill) + return; + count := 1; + if (argl != nil) { + rcount := real hd argl; + count = int rcount; # may be 0 (e.g., ".sp .5") + if (count == 0 && rcount > 0.0) + count = 1; # force whitespace for fractional lines + } + g.dobreak(); + for (i := 0; i < count; i++) + g.print(" 
          \n"); + g.broken = true; + g.sop = count > 0; +} + +g_SS(g: ref Global, argl: list of string) +{ + closeall(g, 1); + g.indents++; + g.print(g.softp() + "
          "); + printargs(g, argl); + g.print("\n"); + g.print("
          \n"); + g.sop = true; + g.broken = true; +} + +g_TH(g: ref Global, argl: list of string) +{ + if (g.oldstyle.names && len argl > 2) + argl = hd argl :: hd tl argl :: nil; # ignore extra .TH args on pages in oldstyle trees + case len argl { + 0 => + g.oldstyle.fmt = true; + title(g, sprint("%s", g.href.title), false); + 1 => + g.oldstyle.fmt = true; + title(g, sprint("%s", hd argl), false); # any pages use this form? + 2 => + g.oldstyle.fmt = true; + g.thisone.page = hd argl; + g.thisone.mtype = hd tl argl; + g.mk_href_man(hd argl, true); + g.mk_href_mtype(nil, hd tl argl); + title(g, sprint("%s(%s)", g.href.man, g.href.mtype), false); + * => + g.oldstyle.fmt = false; + chap := hd tl tl argl; + g.mk_href_chap(chap); + g.mk_href_man(hd argl, false); + g.mk_href_mtype(chap, hd tl argl); + title(g, sprint("%s/%s/%s(%s)", g.href.title, g.href.chap, g.href.man, g.href.mtype), false); + } + g.print("[manual index]"); + g.print("[section index]

          "); + g.print("

          \n"); # whole man page is just one big list + g.indents = 1; + g.sop = true; + g.broken = true; +} + +dohangingdt(g: ref Global) +{ + case g.hangingdt { + 3 => + g.hangingdt--; + 2 => + g.print("
          "); + g.hangingdt = 0; + g.broken = true; + } +} + +# close a list, if there's one active +closel(g: ref Global) +{ + case g.list_type { + Lordered => + g.print("
\n"); + g.broken = true; + Lunordered => + g.print("\n"); + g.broken = true; + Lother or Ldef => + g.print("
\n"); + g.broken = true; + } + g.list_type = Lnone; +} + +closeall(g: ref Global, level: int) +{ + closel(g); + reset_font(g); + while (g.indents > level) { + g.indents--; + g.print("\n"); + g.broken = true; + } +} + +# +# Show last revision date for a file. +# +rev(g: ref Global, filebuf: ref Bufio->Iobuf) +{ + if (g.mtime == 0) { + (err, info) := sys->fstat(filebuf.fd); + if (! err) + g.mtime = info.mtime; + } + if (g.mtime != 0) { + g.print("

\n"); + g.print(""); + g.print(sprint("\n"); + g.print(sprint("
")); + g.print(sprint("%s(%s)", g.thisone.page, g.thisone.mtype)); + g.print("Rev:  %s
\n", + dt->text(dt->gmt(g.mtime)))); + } +} + +# +# Some font alternation macros are references to other man pages; +# detect them (second arg contains balanced parens) and make them into hot links. +# +anchor(g: ref Global, f1, f2: string, argl: list of string) +{ + final := ""; + link := false; + if (len argl == 2) { + (s, e) := str->splitl(hd tl argl, ")"); + if (str->prefix("(", s) && e != nil) { + # emit href containing search for target first + # if numeric, do old style + link = true; + file := hd argl; + (chap, man) := split(httpunesc(file), '/'); + if (man == nil) { + # given no explicit chapter prefix, use current chapter + man = chap; + chap = g.thisone.chap; + } + mtype := s[1:]; + if (mtype == nil) + mtype = "-"; + (n, toks) := sys->tokenize(mtype, "."); # Fix section 10 + if (n > 1) mtype = hd toks; + g.print(sprint("", mtype, fixlink(man))); + + # + # now generate the name the user sees, with terminal punctuation + # moved after the closing . + # + if (len e > 1) + final = e[1:]; + argl = hd argl :: s + ")" :: nil; + } + } + altfont(g, f1, f2, argl, false); + if (link) { + g.print(""); + font(g, f2, final :: nil); + } else + g.print("\n"); +} + + +# +# Fix up a link +# + +fixlink(l: string): string +{ + ll := str->tolower(l); + if (ll == "copyright") ll = "1" + ll; + (a, b) := str->splitstrl(ll, "intro"); + if (len b == 5) ll = a + "0" + b; + return ll; +} + + +# +# output argl in font f +# +font(g: ref Global, f: string, argl: list of string) +{ + if (argl == nil) { + g.def_goobie = f; + return; + } + case f { + "L" => f = "TT"; + "R" => f = nil; + } + if (f != nil) # nil == default (typically Roman) + g.print(sprint("<%s>", f)); + printargs(g, argl); + if (f != nil) + g.print(sprint("", f)); + g.print("\n"); + g.prevfont = f; +} + +# +# output concatenated elements of argl, alternating between fonts f1 and f2 +# +altfont(g: ref Global, f1, f2: string, argl: list of string, newline: int) +{ + reset_font(g); + if (argl == nil) { + g.def_goobie = f1; + return; + } + case f1 { + "L" => f1 = "TT"; + "R" => f1 = nil; + } + case f2 { + "L" => f2 = "TT"; + "R" => f2 = nil; + } + f := f1; + for (; argl != nil; argl = tl argl) { + if (f != nil) + g.print(sprint("<%s>%s", f, hd argl, f)); + else + g.print(hd argl); + if (f == f1) + f = f2; + else + f = f1; + } + if (newline) + g.print("\n"); + g.prevfont = f; +} + +# not yet implemented +map_font(nil: ref Global, nil: string) +{ +} + +reset_font(g: ref Global) +{ + if (g.curfont != nil) { + g.print(sprint("", g.curfont)); + g.prevfont = g.curfont; + g.curfont = nil; + } +} + +printargs(g: ref Global, argl: list of string) +{ + for (; argl != nil; argl = tl argl) + if (tl argl != nil) + g.print(hd argl + " "); + else + g.print(hd argl); +} + +# any parameter can be nil +addhit(g: ref Global, chap, mtype, page: string) +{ + # g.print(sprint("Adding %s / %s (%s) . . .", chap, page, mtype)); # debug + # always keep a spare slot at the end + if (g.nhits >= len g.hits - 1) + g.hits = (array[len g.hits + 32] of Hit)[0:] = g.hits; + g.hits[g.nhits].glob = chap + " " + mtype + " " + page; + g.hits[g.nhits].chap = chap; + g.hits[g.nhits].mtype = mtype; + g.hits[g.nhits++].page = page; +} + +Global.dobreak(g: self ref Global) +{ + if (! g.broken) { + g.broken = true; + g.print("
\n"); + } +} + +Global.print(g: self ref Global, s: string) +{ + g.bufio->g.bout.puts(s); + if (g.sop || g.broken) { + # first non-white space, non-HTML we print takes us past the start of the paragraph & line + # (or even white space, if we're in no-fill mode) + for (i := 0; i < len s; i++) { + case s[i] { + '<' => + while (++i < len s && s[i] != '>') + ; + continue; + ' ' or '\t' or '\n' => + if (g.fill) + continue; + } + g.sop = false; + g.broken = false; + break; + } + } +} + +Global.softbr(g: self ref Global): string +{ + if (g.broken) + return nil; + g.broken = true; + return "
"; +} + +# provide a paragraph marker, unless we're already at the start of a section +Global.softp(g: self ref Global): string +{ + if (g.sop) + return nil; + else if (! g.ipd) + return "
"; + if (g.fill) + return "

"; + else + return "

"; +} + +# +# Get next logical character. Expand it with escapes. +# +getnext(g: ref Global): string +{ + iob := g.bufio; + Iobuf: import iob; + + font: string; + token: string; + bin := g.bin; + + g.sol = (g.lastc == '\n'); + + c := bin.getc(); + if (c < 0) + return nil; + g.lastc = c; + if (c >= Runeself) { + for (i := 0; i < len Entities; i++) + if (Entities[i].value == c) + return Entities[i].name; + return sprint("&#%d;", c); + } + case c { + '<' => + return "<"; + '>' => + return ">"; + '\\' => + c = bin.getc(); + if (c < 0) + return nil; + g.lastc = c; + case c { + + # chars to ignore + '|' or '&' or '^' => + return getnext(g); + + # ignore arg + 'k' => + nil = bin.getc(); + return getnext(g); + + # defined strings + '*' => + case bin.getc() { + 'R' => + return "®"; + } + return getnext(g); + + # special chars + '(' => + token[0] = bin.getc(); + token[1] = bin.getc(); + for (i := 0; i < len tspec; i++) + if (token == tspec[i].name) + return tspec[i].value; + return "¿"; + 'c' => + c = bin.getc(); + if (c < 0) + return nil; + else if (c == '\n') { + g.lastc = c; + g.sol = true; + token[0] = bin.getc(); + return token; + } + # DEBUG: should there be a "return xxx" here? + 'e' => + return "\\"; + 'f' => + g.lastc = c = bin.getc(); + if (c < 0) + return nil; + case c { + '2' or 'I' => + font = "I"; + '3' or 'B' => + font = "B"; + '5' or 'L' => + font = "TT"; + 'P' => + font = g.prevfont; + * => # includes '1' and 'R' + font = nil; + } +# There are serious problems with this. We don't know the fonts properly at this stage. +# g.prevfont = g.curfont; +# g.curfont = font; +# if (g.prevfont != nil) +# token = sprint("", g.prevfont); +# if (g.curfont != nil) +# token += sprint("<%s>", g.curfont); + if (token == nil) + return " "; # shouldn't happen - maybe a \fR inside a font macro - just do something! + return token; + 's' => + sign := '+'; + size := 0; + relative := false; + getsize: + for (;;) { + c = bin.getc(); + if (c < 0) + return nil; + case c { + '+' => + relative = true; + '-' => + sign = '-'; + relative = true; + '0' to '9' => + size = size * 10 + (c - '0'); + * => + bin.ungetc(); + break getsize; + } + g.lastc = c; + } + if (size == 0) + token = ""; + else if (relative) + token = sprint("", sign, size); + else + token = sprint("", size); + return token; + } + } + token[0] = c; + return token; +} + +# +# Return strings before and after the left-most instance of separator; +# (s, nil) if no match or separator is last char in s. +# +split(s: string, sep: int): (string, string) +{ + for (i := 0; i < len s; i++) + if (s[i] == sep) + return (s[:i], s[i+1:]); # s[len s:] is a valid slice, with value == nil + return (s, nil); +} + +Global_init(): ref Global +{ + g := ref Global; + g.bufio = load Bufio Bufio->PATH; + g.chaps = array[20] of Chaps; + g.types = array[20] of Types; + g.mantitle = ""; + g.href.title = g.mantitle; # ?? + g.mtime = 0; + g.nhits = 0; + g.oldstyle.names = false; + g.oldstyle.fmt = false; + g.topname = "System"; + g.list_type = Lnone; + g.def_sm = 0; + g.hangingdt = 0; + g.indents = 0; + g.sop = true; + g.broken = true; + g.ipd = true; + g.fill = true; + g.example = false; + g.pre = false; + g.lastc = '\n'; + return g; +} + +Global.mk_href_chap(g: self ref Global, chap: string) +{ + if (chap != nil) + g.href.chap = sprint("%s", g.mandir, chap, chap); +} + +Global.mk_href_man(g: self ref Global, man: string, oldstyle: int) +{ + rman := man; + if (oldstyle) + rman = str->tolower(man); # compensate for tradition of putting titles in all CAPS + g.href.man = sprint("%s", g.mandir, rman, man); +} + +Global.mk_href_mtype(g: self ref Global, chap, mtype: string) +{ + g.href.mtype = sprint("%s", g.mandir, chap, mtype, mtype); +} + +# We assume that anything >= Runeself is already in UTF. +# +httpunesc(s: string): string +{ + t := ""; + for (i := 0; i < len s; i++) { + c := s[i]; + if (c == '&' && i + 1 < len s) { + (char, rem) := str->splitl(s[i+1:], ";"); + if (rem == nil) + break; # require the terminating ';' + if (char == nil) + continue; + if (char[0] == '#' && len char > 1) { + c = int char[1:]; + i += len char; + if (c < 256 && c >= 161) { + t[len t] = Entities[c-161].value; + continue; + } + } else { + for (j := 0; j < len Entities; j++) + if (Entities[j].name == char) + break; + if (j < len Entities) { + i += len char; + t[len t] = Entities[j].value; + continue; + } + } + } + t[len t] = c; + } + return t; +} + + + +title(g: ref Global, t: string, search: int) +{ + if(search) + ; # not yet used + g.print("\n"); + g.print(sprint("Inferno's %s\n", demark(t))); + g.print("\n"); + g.print("\n"); + +} diff --git a/appl/cmd/shutdown.b b/appl/cmd/shutdown.b deleted file mode 100644 index 8eb7a86c..00000000 --- a/appl/cmd/shutdown.b +++ /dev/null @@ -1,72 +0,0 @@ -implement Shutdown; - -include "sys.m"; -sys: Sys; -FD: import Sys; -stderr: ref FD; - -include "draw.m"; -Context: import Draw; - -sysctl: con "/dev/sysctl"; -reboot: con "reboot"; -halt: con "halt"; - -Shutdown: module -{ - init: fn(ctxt: ref Context, argv: list of string); -}; - -rflag: int; -hflag: int; - -init(nil: ref Context, argv: list of string) -{ - sys = load Sys Sys->PATH; - - stderr = sys->fildes(2); - - argv = tl argv; - if(len argv < 1) - usage(); - - while(argv != nil && len hd argv && (arg := hd argv)[0] == '-' && len arg > 1){ - case arg[1] { - 'r' => - rflag = 1; - 'h' => - hflag = 1; - } - argv = tl argv; - } - - if(rflag == 0 && hflag == 0) - usage(); - - if(rflag == 1 && hflag == 1) - usage(); - - fd := sys->open(sysctl, sys->OWRITE); - if(fd == nil) { - sys->fprint(stderr, "shutdown: %r\n"); - exit; - } - - if(rflag == 1) - if (sys->write(fd, array of byte reboot, len reboot) < 0) { - sys->fprint(stderr, "shutdown: write failed: %r\n"); - exit; - } - - if(hflag == 1) - if (sys->write(fd, array of byte halt, len halt) < 0) { - sys->fprint(stderr, "shutdown: write failed: %r\n"); - exit; - } -} - -usage() -{ - sys->fprint(stderr, "usage: shutdown -r | -h\n"); - exit; -} diff --git a/appl/lib/csv.b b/appl/lib/csv.b new file mode 100644 index 00000000..336e2cde --- /dev/null +++ b/appl/lib/csv.b @@ -0,0 +1,86 @@ +implement CSV; + +include "sys.m"; + +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; + +include "csv.m"; + +init(b: Bufio) +{ + bufio = b; +} + +getline(fd: ref Iobuf): list of string +{ + rl: list of string; + for(;;){ + (w, end) := getfield(fd); + if(rl == nil && w == nil && end < 0) + return nil; + rl = w :: rl; + if(end != ',') + break; + } + l: list of string; + for(; rl != nil; rl = tl rl) + l = hd rl :: l; + return l; +} + +getfield(fd: ref Iobuf): (string, int) +{ + w := ""; + if((c := getcr(fd)) == '"'){ # quoted field + while((c = getcr(fd)) >= 0){ + if(c == '"'){ + c = getcr(fd); + if(c != '"') + break; + } + w[len w] = c; + } + } + # unquoted text, possibly following quoted text above + for(; c >= 0 && c != ',' && c != '\n'; c = getcr(fd)) + w[len w] = c; + return (w, c); +} + +getcr(fd: ref Iobuf): int +{ + c := fd.getc(); + if(c == '\r'){ + nc := fd.getc(); + if(nc >= 0 && nc != '\n') + fd.ungetc(); + c = '\n'; + } + return c; +} + +quote(s: string): string +{ + sep := 0; + for(i := 0; i < len s; i++) + if((c := s[i]) == '"') + return innerquote(s); + else if(c == ',' || c == '\n') + sep = 1; + if(sep) + return "\""+s+"\""; + return s; +} + +innerquote(s: string): string +{ + w := "\""; + for(i := j := 0; i < len s; i++) + if(s[i] == '"'){ + w += s[j: i+1]; # including " + j = i; # including " again + } + return w+s[j:i]+"\""; +} diff --git a/appl/lib/rfc822.b b/appl/lib/rfc822.b new file mode 100644 index 00000000..0f15a585 --- /dev/null +++ b/appl/lib/rfc822.b @@ -0,0 +1,561 @@ +implement RFC822; + +include "sys.m"; + sys: Sys; + +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; + +include "rfc822.m"; + +include "string.m"; + str: String; + +include "daytime.m"; + daytime: Daytime; + Tm: import daytime; + +Minrequest: con 512; # more than enough for most requests + +Suffix: adt { + suffix: string; + generic: string; + specific: string; + encoding: string; +}; + +SuffixFile: con "/lib/mimetype"; +mtime := 0; +qid: Sys->Qid; + +suffixes: list of ref Suffix; + +nomod(s: string) +{ + raise sys->sprint("internal: can't load %s: %r", s); +} + +init(b: Bufio) +{ + sys = load Sys Sys->PATH; + bufio = b; + str = load String String->PATH; + if(str == nil) + nomod(String->PATH); + daytime = load Daytime Daytime->PATH; + if(daytime == nil) + nomod(Daytime->PATH); + readsuffixfile(); +} + +readheaders(fd: ref Iobuf, limit: int): array of (string, array of byte) +{ + n := 0; + s := 0; + b := array[Minrequest] of byte; + nline := 0; + lines: list of array of byte; + while((c := fd.getb()) >= 0){ + if(c == '\r'){ + c = fd.getb(); + if(c < 0) + break; + if(c != '\n'){ + fd.ungetb(); + c = '\r'; + } + } + if(n >= len b){ + if(len b >= limit) + return nil; + ab := array[n+512] of byte; + ab[0:] = b; + b = ab; + } + b[n++] = byte c; + if(c == '\n'){ + if(n == 1 || b[n-2] == byte '\n') + break; # empty line + c = fd.getb(); + if(c < 0) + break; + if(c != ' ' && c != '\t'){ # not continued + fd.ungetb(); + lines = b[s: n] :: lines; + nline++; + s = n; + }else + b[n-1] = byte ' '; + } + } + if(n == 0) + return nil; + b = b[0: n]; + if(n != s){ + lines = b[s:n] :: lines; + nline++; + } + a := array[nline] of (string, array of byte); + for(; lines != nil; lines = tl lines){ + b = hd lines; + name := ""; + for(i := 0; i < len b; i++) + if(b[i] == byte ':'){ + name = str->tolower(string b[0:i]); + b = b[i+1:]; + break; + } + a[--nline] = (name, b); + } + return a; +} + +# +# *(";" parameter) used in transfer-extension, media-type and media-range +# parameter = attribute "=" value +# attribute = token +# value = token | quoted-string +# +parseparams(ps: ref Rfclex): list of (string, string) +{ + l: list of (string, string); + do{ + if(ps.lex() != Word) + break; + attr := ps.wordval; + if(ps.lex() != '=' || ps.lex() != Word && ps.tok != QString) + break; + l = (attr, ps.wordval) :: l; + }while(ps.lex() == ';'); + ps.unlex(); + return rev(l); +} + +# +# 1#transfer-coding +# +mimefields(ps: ref Rfclex): list of (string, list of (string, string)) +{ + rf: list of (string, list of (string, string)); + do{ + if(ps.lex() == Word){ + w := ps.wordval; + if(ps.lex() == ';'){ + rf = (w, parseparams(ps)) :: rf; + ps.lex(); + }else + rf = (w, nil) :: rf; + } + }while(ps.tok == ','); + ps.unlex(); + f: list of (string, list of (string, string)); + for(; rf != nil; rf = tl rf) + f = hd rf :: f; + return f; +} + +# #(media-type | (media-range [accept-params])) ; Content-Type and Accept +# +# media-type = type "/" subtype *( ";" parameter ) +# type = token +# subtype = token +# LWS must not be used between type and subtype, nor between attribute and value (in parameter) +# +# media-range = ("*/*" | type "/*" | type "/" subtype ) *(";' parameter) +# accept-params = ";" "q" "=" qvalue *( accept-extension ) +# accept-extension = ";" token [ "=" ( token | quoted-string ) ] +# +# 1#( ( charset | "*" )[ ";" "q" "=" qvalue ] ) ; Accept-Charset +# 1#( codings [ ";" "q" "=" qvalue ] ) ; Accept-Encoding +# 1#( language-range [ ";" "q" "=" qvalue ] ) ; Accept-Language +# +# codings = ( content-coding | "*" ) +# +parsecontent(ps: ref Rfclex, multipart: int, head: list of ref Content): list of ref Content +{ + do{ + if(ps.lex() == Word){ + generic := ps.wordval; + specific := "*"; + if(ps.lex() == '/'){ + if(ps.lex() != Word) + break; + specific = ps.wordval; + if(!multipart && specific != "*") + break; + }else if(multipart) + break; # syntax error + else + ps.unlex(); + params: list of (string, string) = nil; + if(ps.lex() == ';'){ + params = parseparams(ps); + ps.lex(); + } + head = Content.mk(generic, specific, params) :: head; # order reversed, but doesn't matter + } + }while(ps.tok == ','); + ps.unlex(); + return head; +} + +rev(l: list of (string, string)): list of (string, string) +{ + rl: list of (string, string); + for(; l != nil; l = tl l) + rl = hd l :: rl; + return rl; +} + +Rfclex.mk(a: array of byte): ref Rfclex +{ + ps := ref Rfclex; + ps.fd = bufio->aopen(a); + ps.tok = '\n'; + ps.eof = 0; + return ps; +} + +Rfclex.getc(ps: self ref Rfclex): int +{ + c := ps.fd.getb(); + if(c < 0) + ps.eof = 1; + return c; +} + +Rfclex.ungetc(ps: self ref Rfclex) +{ + if(!ps.eof) + ps.fd.ungetb(); +} + +Rfclex.lex(ps: self ref Rfclex): int +{ + if(ps.seen != nil){ + (ps.tok, ps.wordval) = hd ps.seen; + ps.seen = tl ps.seen; + }else + ps.tok = lex1(ps, 0); + return ps.tok; +} + +Rfclex.unlex(ps: self ref Rfclex) +{ + ps.seen = (ps.tok, ps.wordval) :: ps.seen; +} + +Rfclex.skipws(ps: self ref Rfclex): int +{ + return lex1(ps, 1); +} + +# +# rfc 2822/rfc 1521 lexical analyzer +# +lex1(ps: ref Rfclex, skipwhite: int): int +{ + ps.wordval = nil; + while((c := ps.getc()) >= 0){ + case c { + '(' => + level := 1; + while((c = ps.getc()) != Bufio->EOF && c != '\n'){ + if(c == '\\'){ + c = ps.getc(); + if(c == Bufio->EOF) + return '\n'; + continue; + } + if(c == '(') + level++; + else if(c == ')' && --level == 0) + break; + } + ' ' or '\t' or '\r' or 0 => + ; + '\n' => + return '\n'; + ')' or '<' or '>' or '[' or ']' or '@' or '/' or ',' or + ';' or ':' or '?' or '=' => + if(skipwhite){ + ps.ungetc(); + return c; + } + return c; + + '"' => + if(skipwhite){ + ps.ungetc(); + return c; + } + word(ps,"\""); + ps.getc(); # skip the closing quote + return QString; + + * => + ps.ungetc(); + if(skipwhite) + return c; + word(ps,"\"()<>@,;:/[]?={}\r\n \t"); + return Word; + } + } + return '\n'; +} + +# return the rest of an rfc 822 line, not including \r or \n +# do not map to lower case + +Rfclex.line(ps: self ref Rfclex): string +{ + s := ""; + while((c := ps.getc()) != Bufio->EOF && c != '\n' && c != '\r'){ + if(c == '\\'){ + c = ps.getc(); + if(c == Bufio->EOF) + break; + } + s[len s] = c; + } + ps.tok = '\n'; + ps.wordval = s; + return s; +} + +word(ps: ref Rfclex, stop: string) +{ + w := ""; + while((c := ps.getc()) != Bufio->EOF){ + if(c == '\r') + c = ' '; + if(c == '\\'){ + c = ps.getc(); + if(c == Bufio->EOF) + break; + }else if(str->in(c,stop)){ + ps.ungetc(); + break; + } + if(c >= 'A' && c <= 'Z') + c += 'a' - 'A'; + w[len w] = c; + } + ps.wordval = w; +} + +readsuffixfile(): string +{ + iob := bufio->open(SuffixFile, Bufio->OREAD); + if(iob == nil) + return sys->sprint("cannot open %s: %r", SuffixFile); + for(n := 1; (line := iob.gets('\n')) != nil; n++){ + (s, nil) := parsesuffix(line); + if(s != nil) + suffixes = s :: suffixes; + } + return nil; +} + +parsesuffix(line: string): (ref Suffix, string) +{ + (line, nil) = str->splitstrl(line, "#"); + if(line == nil) + return (nil, nil); + (n, slist) := sys->tokenize(line,"\n\t "); + if(n == 0) + return (nil, nil); + if(n < 4) + return (nil, "too few fields"); + s := ref Suffix; + s.suffix = hd slist; + slist = tl slist; + s.generic = hd slist; + if (s.generic == "-") + s.generic = ""; + slist = tl slist; + s.specific = hd slist; + if (s.specific == "-") + s.specific = ""; + slist = tl slist; + s.encoding = hd slist; + if (s.encoding == "-") + s.encoding = ""; + if((s.generic == nil || s.specific == nil) && s.encoding == nil) + return (nil, nil); + return (s, nil); +} + +# +# classify by file suffix +# +suffixclass(name: string): (ref Content, ref Content) +{ + typ, enc: ref Content; + + p := str->splitstrr(name, "/").t1; + if(p != nil) + name = p; + + for(;;){ + (name, p) = suffix(name); # TO DO: match below is case sensitive + if(p == nil) + break; + for(l := suffixes; l != nil; l = tl l){ + s := hd l; + if(p == s.suffix){ + if(s.generic != nil && typ == nil) + typ = Content.mk(s.generic, s.specific, nil); + if(s.encoding != nil && enc == nil) + enc = Content.mk(s.encoding, "", nil); + if(typ != nil && enc != nil) + break; + } + } + } + return (typ, enc); +} + +suffix(s: string): (string, string) +{ + for(n := len s; --n >= 0;) + if(s[n] == '.') + return (s[0: n], s[n:]); + return (s, nil); +} + +# +# classify by initial contents of file +# +dataclass(a: array of byte): (ref Content, ref Content) +{ + utf8 := 0; + for(i := 0; i < len a;){ + c := int a[i]; + if(c < 16r80){ + if(c < 32 && c != '\n' && c != '\r' && c != '\t' && c != '\v' && c != '\f') + return (nil, nil); + i++; + }else{ + utf8 = 1; + (r, l, nil) := sys->byte2char(a, i); + if(r == Sys->UTFerror) + return (nil, nil); + i += l; + } + } + if(utf8) + params := ("charset", "utf-8") :: nil; + return (Content.mk("text", "plain", params), nil); +} + +Content.mk(generic, specific: string, params: list of (string, string)): ref Content +{ + c := ref Content; + c.generic = generic; + c.specific = specific; + c.params = params; + return c; +} + +Content.check(me: self ref Content, oks: list of ref Content): int +{ + if(oks == nil) + return 1; + g := str->tolower(me.generic); + s := str->tolower(me.specific); + for(; oks != nil; oks = tl oks){ + ok := hd oks; + if((ok.generic == g || ok.generic=="*") && + (s == nil || ok.specific == s || ok.specific=="*")) + return 1; + } + return 0; +} + +Content.text(c: self ref Content): string +{ + if((s := c.specific) != nil) + s = c.generic+"/"+s; + else + s = c.generic; + for(l := c.params; l != nil; l = tl l){ + (n, v) := hd l; + s += sys->sprint(";%s=%s", n, quote(v)); + } + return s; +} + +# +# should probably be in a Mime or HTTP module +# + +Quotable: con "()<>@,;:\\\"/[]?={} \t"; + +quotable(s: string): int +{ + for(i := 0; i < len s; i++) + if(str->in(s[i], Quotable)) + return 1; + return 0; +} + +quote(s: string): string +{ + if(!quotable(s)) + return s; + q := "\""; + for(i := 0; i < len s; i++){ + if(str->in(s[i], Quotable)) + q[len q] = '\\'; + q[len q] = s[i]; + } + q[len q] = '"'; + return q; +} + +weekdays := array[] of { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; + +months := array[] of { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +# print dates in the format +# Wkd, DD Mon YYYY HH:MM:SS GMT + +sec2date(t: int): string +{ + tm := daytime->gmt(t); + return sys->sprint("%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", + weekdays[tm.wday], tm.mday, months[tm.mon], tm.year+1900, + tm.hour, tm.min, tm.sec); +} + +# parse dates of formats +# Wkd, DD Mon YYYY HH:MM:SS GMT +# Weekday, DD-Mon-YY HH:MM:SS GMT +# Wkd Mon ( D|DD) HH:MM:SS YYYY +# plus anything similar + +date2sec(date: string): int +{ + tm := daytime->string2tm(date); + if(tm == nil || tm.year < 70 || tm.zone != "GMT") + t := 0; + else + t = daytime->tm2epoch(tm); + return t; +} + +now(): int +{ + return daytime->now(); +} + +time(): string +{ + return sec2date(daytime->now()); +} diff --git a/appl/lib/w3c/uris.b b/appl/lib/w3c/uris.b new file mode 100644 index 00000000..b49c17b8 --- /dev/null +++ b/appl/lib/w3c/uris.b @@ -0,0 +1,320 @@ +implement URIs; + +# +# RFC3986, URI Generic Syntax +# + +include "sys.m"; + sys: Sys; + +include "string.m"; + S: String; + +include "uris.m"; + +Alpha: con "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +Digit: con "0123456789"; + +GenDelims: con ":/?#[]@"; +SubDelims: con "!$&'()*+,;="; +Reserved: con GenDelims + SubDelims; +HexDigit: con Digit+"abcdefABCDEF"; + +Escape: con GenDelims+"%"; # "%" must be encoded as %25 + +Unreserved: con Alpha+Digit+"-._~"; + +F_Esc, F_Scheme: con byte(1<PATH; + S = load String String->PATH; + if(S == nil) + raise sys->sprint("can't load %s: %r", String->PATH); + + ctype = array [256] of { * => byte 0 }; + classify(Escape, F_Esc); + for(i := 0; i <= ' '; i++) + ctype[i] |= F_Esc; + for(i = 16r80; i <= 16rFF; i++) + ctype[i] |= F_Esc; + classify(Alpha+Digit+"+-.", F_Scheme); +} + +# scheme://:@:/?# +# +# ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? +# +# delimiters: :/?# /?# ?# # +# +URI.parse(url: string): ref URI +{ + scheme, userinfo, host, port, path, query, frag: string; + for(i := 0; i < len url; i++){ + c := url[i]; + if(c == ':'){ + scheme = S->tolower(url[0:i]); + url = url[i+1:]; + break; + } + if(c < 0 || c >= len ctype || (ctype[c] & F_Scheme) == byte 0) + break; + } + + if(S->prefix("//", url)){ + authority: string; + (authority, path) = S->splitstrl(url[2:], "/"); + (up, hp) := splitl(authority, "@"); + if(hp == "") + hp = authority; + else + userinfo = up; + if(hp != nil && hp[0] == '['){ # another rfc hack, for IPv6 addresses, which contain : + (host, hp) = S->splitstrr(hp, "]"); + if(hp != nil && hp[0] == ':') + port = hp[1:]; + else + host += hp; # put it back + }else + (host, port) = splitl(hp, ":"); + if(path == nil) + path = "/"; + }else + path = url; + (path, frag) = S->splitstrl(path, "#"); # includes # in frag + (path, query) = S->splitstrl(path, "?"); # includes ? in query + return ref URI(scheme, dec(userinfo), dec(host), port, dec(path), query, dec(frag)); +} + +URI.userpw(u: self ref URI): (string, string) +{ + return splitl(u.userinfo, ":"); +} + +URI.text(u: self ref URI): string +{ + s := ""; + if(u.scheme != nil) + s += u.scheme + ":"; + if(u.hasauthority()) + s += "//" + u.authority(); + return s + enc(u.path, "/@:") + u.query + enc1(u.fragment, "@:/?"); +} + +URI.copy(u: self ref URI): ref URI +{ + return ref *u; +} + +URI.pathonly(u: self ref URI): ref URI +{ + v := ref *u; + v.userinfo = nil; + v.query = nil; + v.fragment = nil; + return v; +} + +URI.addbase(u: self ref URI, b: ref URI): ref URI +{ + # RFC3986 5.2.2, rearranged + r := ref *u; + if(r.scheme == nil && b != nil){ + r.scheme = b.scheme; + if(!r.hasauthority()){ + r.userinfo = b.userinfo; + r.host = b.host; + r.port = b.port; + if(r.path == nil){ + r.path = b.path; + if(r.query == nil) + r.query = b.query; + }else if(r.path[0] != '/'){ + # 5.2.3: merge paths + if(b.path == "" && b.hasauthority()) + p1 := "/"; + else + (p1, nil) = S->splitstrr(b.path, "/"); + r.path = p1 + r.path; + } + } + } + r.path = removedots(r.path); + return r; +} + +URI.nodots(u: self ref URI): ref URI +{ + return u.addbase(nil); +} + +URI.hasauthority(u: self ref URI): int +{ + return u.host != nil || u.userinfo != nil || u.port != nil; +} + +URI.isabsolute(u: self ref URI): int +{ + return u.scheme != nil; +} + +URI.authority(u: self ref URI): string +{ + s := enc(u.userinfo, ":"); + if(s != nil) + s += "@"; + if(u.host != nil){ + s += enc(u.host, "[]:"); # assumes : appears inside []; could enforce it + if(u.port != nil) + s += ":" + enc(u.port,nil); + } + return s; +} + +# +# simplified version of procedure in RFC3986 5.2.4: +# it extracts a complete segment from the input first, then analyses it +# +removedots(s: string): string +{ + if(s == nil) + return ""; + out := ""; + for(p := 0; p < len s;){ + # extract the first segment and any preceding / + q := p; + if(++p < len s){ + while(++p < len s && s[p] != '/') + {} + } + seg := s[q: p]; + if((e := p) < len s) + e++; + case s[q: e] { # includes any following / + "../" or "./" => ; + "/./" or "/." => + if(p >= len s) + s += "/"; + "/../" or "/.." => + if(p >= len s) + s += "/"; + if(out != nil){ + for(q = len out; --q > 0 && out[q] != '/';) + {} # skip + out = out[0: q]; + } + "." or ".." => ; # null effect + * => # including "/" + out += seg; + } + } + return out; +} + +# +# similar to splitstrl but trims the matched character from the result +# +splitl(s, c: string): (string, string) +{ + (a, b) := S->splitstrl(s, c); + if(b != "") + b = b[1:]; + return (a, b); +} + +hex2(s: string): int +{ + n := 0; + for(i := 0; i < 2; i++){ + if(i >= len s) + return -1; + n <<= 4; + case c := s[i] { + '0' to '9' => + n += c-'0'; + 'a' to 'f' => + n += 10+(c-'a'); + 'A' to 'F' => + n += 10+(c-'A'); + * => + return -1; + } + } + return n; +} + +dec(s: string): string +{ + for(i := 0;; i++){ + if(i >= len s) + return s; + if(s[i] == '%' || s[i] == 0) + break; + } + o := s[0:i]; + while(i < len s){ + case c := s[i++] { + '%' => + if((v := hex2(s[i:])) > 0){ + c = v; + i += 2; + } + 0 => + c = ' '; # shouldn't happen + } + o[len o] = c; + } + return o; +} + +enc1(s: string, safe: string): string +{ + if(len s > 1) + return s[0:1] + enc(s[1:], safe); + return s; +} + +# encoding depends on context (eg, &=/: not escaped in `query' string) +enc(s: string, safe: string): string +{ + for(i := 0;; i++){ + if(i >= len s) + return s; # use as-is + c := s[i]; + if(c >= 16r80 || (ctype[c] & F_Esc) != byte 0 && !S->in(c, safe)) + break; + } + t := s[0: i]; + b := array of byte s[i:]; + for(i = 0; i < len b; i++){ + c := int b[i]; + if((ctype[c] & F_Esc) != byte 0 && !S->in(c, safe)) + t += sys->sprint("%%%.2X", c); + else + t[len t] = c; + } + return t; +} + +URI.eq(u: self ref URI, v: ref URI): int +{ + if(v == nil) + return 0; + return u.scheme == v.scheme && u.userinfo == v.userinfo && + u.host == v.host && u.port == v.port && u.path == v.path && # path might need canon + u.query == v.query; # not fragment +} + +URI.eqf(u: self ref URI, v: ref URI): int +{ + return u.eq(v) && u.fragment == v.fragment; +} diff --git a/appl/spree/man/styxservers-nametree.man2 b/appl/spree/man/styxservers-nametree.man2 deleted file mode 100644 index f64e519a..00000000 --- a/appl/spree/man/styxservers-nametree.man2 +++ /dev/null @@ -1,180 +0,0 @@ -.TH STYXSERVERS-NAMETREE 2 -.SH NAME -Styxservers: nametree \- -hierarchical name storage for use with Styxservers. -.SH SYNOPSIS -.EX -include "sys.m"; -include "styx.m"; -include "styxservers.m"; -nametree := load Nametree Nametree->PATH; - Tree: import nametree; - -Tree: adt { - create: fn(t: self ref Tree, parentpath: big, d: Sys->Dir): string; - remove: fn(t: self ref Tree, path: big): string; - wstat: fn(t: self ref Tree, path: big, d: Sys->Dir); - quit: fn(t: self ref Tree); -}; -init: fn(); -start: fn(): (ref Tree, chan of ref Styxservers->Navop); -.EE -.SH DESCRIPTION -.B Nametree -provides the storage for a hierarchical namespace -to be used by -.IR styxservers (2). -After the module is loaded, the -.B init -function should be called to -initialise the module's internal variables. -.B Start -spawns a new -.B nametree -process; it returns a tuple, say -.RI ( tree ,\ c ), -where c is a channel that can be used to create -an instance of -.BR Styxservers->Navigator , -to access files inside -.BR nametree , -and -.I tree -is an adt that allows creation and removal of those files. -On failure, these functions return a string describing -the error. -.PP -Note that the full set of operations on -.B Nametree -(i.e. stat, walk, readdir, wstate, create and remove), -is only available in conjunction with -.BR Styxserver 's -.B Navigator -interface. -Files in the name space are ultimately identified by a 64-bit -.I path -value, which forms the path component of the file's Qid. -(See -.IR intro (5) -for a description of the system's interpretation of Qids.) -.PP -The -.B Tree -operations -are: -.TP 10 -.IB t .create(\fIparentpath\fP,\ \fId\fP) -Create a new file or directory. -.I D -gives the directory information that will be stored -for the file, including its own path value, -given by -.IB d .qid.path . -If the file referenced by -.I parentpath -does not exist, creation will not be allowed, -other than in the special case when -.IB d .qid.path -is equal to -.IR parentpath , -in which case it is assumed to be a root directory -and may be created. This potentially allows a single -.B Nametree -instance to hold many distinct directory hierarchies. -Note that no attempt is made to ensure that -.I parentpath -refers to a directory; the check is assumed to have -been made previously. -When a hierarchy is traversed, -.B Nametree -interprets the name -.RB ` .. ' -itself as `parent directory', and that name should not be created explicitly. -.TP -.IB t .remove(\fIpath\fP) -Remove the file referred to by -.IR path , -and all its descendants. -.TP -.IB t .wstat(\fIpath\fP,\ \fId\fP) -Change the directory information held on file -.IR path . -The Qid path itself cannot be changed by -.IR d . -.TP -.IB t .quit() -Shut down the -.B nametree -process. -.SH EXAMPLE -Here is a complete example that uses -.B Nametree -in conjunction with -.B Styxservers -in order to serve two files -.B data -and -.BR ctl " ..." -and do nothing with them: -.EX -implement Tst; -include "sys.m"; - sys: Sys; -include "draw.m"; -include "styx.m"; -include "styxservers.m"; - styxservers: Styxservers; - Styxserver, Navigator: import styxservers; - nametree: Nametree; - Tree: import nametree; - -Tst: module -{ - init: fn(nil: ref Draw->Context, argv: list of string); -}; - -Qroot, Qctl, Qdata: con big iota; # paths -init(nil: ref Draw->Context, args: list of string) -{ - sys = load Sys Sys->PATH; - styx := load Styx Styx->PATH; - styx->init(); - styxservers = load Styxservers Styxservers->PATH; - styxservers->init(styx); - nametree = load Nametree Nametree->PATH; - nametree->init(); - sys->pctl(Sys->FORKNS, nil); - (tree, treeop) := nametree->start(); - tree.create(Qroot, dir(".", 8r555|Sys->DMDIR, Qroot)); - tree.create(Qroot, dir("ctl", 8r666, Qctl)); - tree.create(Qroot, dir("data", 8r444, Qdata)); - (tchan, srv) := Styxserver.new(sys->fildes(0), - Navigator.new(treeop), Qroot); - while((gm := <-tchan) != nil) { - # normally a pick on gm would act on - # Tmsg.Read and Tmsg.Write at least - srv.default(gm); - } - tree.quit(); -} - -dir(name: string, perm: int, qid: big): Sys->Dir -{ - d := sys->zerodir; - d.name = name; - d.uid = "me"; - d.gid = "me"; - d.qid.path = qid; - if (perm & Sys->DMDIR) - d.qid.qtype = Sys->QTDIR; - else - d.qid.qtype = Sys->QTFILE; - d.mode = perm; - return d; -} -.EE -.SH SOURCE -.B /appl/lib/nametree.b -.SH SEE ALSO -.IR styxservers (2), -.IR intro (5) diff --git a/appl/spree/man/styxservers.man2 b/appl/spree/man/styxservers.man2 deleted file mode 100644 index fca2748a..00000000 --- a/appl/spree/man/styxservers.man2 +++ /dev/null @@ -1,902 +0,0 @@ -.TH STYXSERVERS 2 -.SH NAME -styxservers \- -Styx server implementation assistance -.SH SYNOPSIS -.EX -include "sys.m"; -include "styx.m"; -Tmsg, Rmsg: import Styx; -include "styxservers.m"; -styxservers := load Styxservers Styxservers->PATH; -Styxserver, Fid, Navigator: import styxservers; - -Styxserver: adt { - fd: ref Sys->FD; # file server end of connection - t: ref Navigator; # name space navigator for this server - msize: int; # negotiated Styx message size - - new: fn(fd: ref Sys->FD, t: ref Navigator, rootpath: big) - :(chan of ref Tmsg, ref Styxserver); - reply: fn(srv: self ref Styxserver, m: ref Rmsg): int; - - # protocol operations - attach: fn(srv: self ref Styxserver, m: ref Tmsg.Attach): ref Fid; - clunk: fn(srv: self ref Styxserver, m: ref Tmsg.Clunk): ref Fid; - walk: fn(srv: self ref Styxserver, m: ref Tmsg.Walk): ref Fid; - open: fn(srv: self ref Styxserver, m: ref Tmsg.Open): ref Fid; - read: fn(srv: self ref Styxserver, m: ref Tmsg.Read): ref Fid; - remove: fn(srv: self ref Styxserver, m: ref Tmsg.Remove): ref Fid; - stat: fn(srv: self ref Styxserver, m: ref Tmsg.Stat); - default: fn(srv: self ref Styxserver, gm: ref Tmsg); - - # check validity - cancreate: fn(srv: self ref Styxserver, m: ref Tmsg.Create) - :(ref Fid, int, ref Sys->Dir, string); - canopen: fn(srv: self ref Styxserver, m: ref Tmsg.Open) - :(ref Fid, int, ref Sys->Dir, string); - canread: fn(srv: self ref Styxserver, m: ref Tmsg.Read) - :(ref Fid, string); - canwrite: fn(srv: self ref Styxserver, m: ref Tmsg.Write) - :(ref Fid, string); - - # fid management - getfid: fn(srv: self ref Styxserver, fid: int): ref Fid; - newfid: fn(srv: self ref Styxserver, fid: int): ref Fid; - delfid: fn(srv: self ref Styxserver, c: ref Fid); - allfids: fn(srv: self ref Styxserver): list of ref Fid; - - iounit: fn(srv: self ref Styxserver): int; -}; - -Fid: adt { - fid: int; # client's fid - path: big; # file's 64-bit unique path - qtype: int; # file's qid type (eg, Sys->QTDIR if directory) - isopen: int; # non-zero if file is open - mode: int; # if open, the open mode - uname: string; # user name from original attach - param: string; # attach aname from original attach - data: array of byte; # application data - - clone: fn(f: self ref Fid, nf: ref Fid): ref Fid; - open: fn(f: self ref Fid, mode: int, qid: Sys->Qid); - walk: fn(f: self ref Fid, qid: Sys->Qid); -}; - -Navop: adt { - reply: chan of (ref Sys->Dir, string); # channel for reply - path: big; # file or directory path - pick { - Stat => - Walk => - name: string; - Readdir => - offset: int; # index (origin 0) of first entry to return - count: int; # number of directory entries requested - } -}; - -Navigator: adt { - new: fn(c: chan of ref Navop): ref Navigator; - stat: fn(t: self ref Navigator, path: big): (ref Sys->Dir, string); - walk: fn(t: self ref Navigator, parent: big, name: string) - : (ref Sys->Dir, string); - readdir:fn(t: self ref Navigator, path: big, - offset, count: int): array of ref Sys->Dir; -}; - -init: fn(styx: Styx); -traceset: fn(on: int); - -readbytes: fn(m: ref Styx->Tmsg.Read, d: array of byte): - ref Styx->Rmsg.Read; -readstr: fn(m: ref Styx->Tmsg.Read, s: string): - ref Styx->Rmsg.Read; -openok: fn(uname: string, omode, - perm: int, funame, fgname: string): int; -openmode: fn(o: int): int; -.EE -.SH DESCRIPTION -When writing a Styx file server, there are some -commonly performed tasks that are -fiddly or tedious to implement each time. -.B Styxservers -provides a framework to automate some of these -routine tasks. -In particular, it helps manage the fid space, -implements common default processing for protocol messages, -and assists walking around the -directory hierarchy and reading of directories. Other -tasks, such as defining the structure of the -name space, and reading and writing files in it, are -left to the file server program itself. -Familiarity with Section 5 of the manual which defines the protocol -(see -.IR intro (5)), -and with the representation of Styx messages in Limbo -(see -.IR styx (2)), -is a prerequisite for use of this module. -.PP -.B Styxservers -does not define or store any of the directory hierarchy itself; -instead it queries an external process for information -when necessary, through a value of type -.BR Navigator , -which encapsulates communication with that process. -That process must be started up -independently of each -.BR Styxserver ; -a channel to such a process should be provided -when starting a new -.BR Styxserver . -The channel carries messages of type -.BR Navop . -.IR Styxservers-nametree (2) -provides a ready-made -implementation of such a process that is sufficient for many applications. -.PP -.B Styxserver -keeps tabs on the fids that are currently in use, and remembers -some associated information, such as the Qid path -of the file, whether it has been opened, etc. -It does this using values of type -.BR Fid . -.PP -Once the -.B Styxservers -module has been loaded, -the -.B init -function must be called before anything else, -to initialise its internal state. The -.I styx -argument should be an implementation of -the -.IR styx (2) -module, which will be used to translate messages. -Individual -.B Styxserver -instances do not share state, and are therefore -independently thread-safe. -.SS Fid representation -.B Styxservers -represents each active fid as a -.B Fid -value, -which has the following public members: -.TF param -.TP -.B fid -The integer -.I fid -value provided by the client to refer to an active instance of a file in the file server, -as described in -.IR intro (5). -.TP -.B path -The 64-bit qid path that uniquely identifies the file on the file server, -as described in -.IR intro (5). -It is set by -.IB f .walk -and -.IB f .open -(see below). -.TP -.B qtype -The file's qid type; it is -.B Sys->QTDIR -if and only if the fid refers to a directory. -The value is set by -.IB f .walk -and -.IB f .open -(see below). -.TP -.B isopen -Non-zero if and only if the fid has been opened by an -.IR open (5) -message. -It is initially zero, and set by -.IB f .open -(see below). -.TP -.B mode -Valid only if the fid has been opened. -It has one of the values -.BR Sys->OREAD , -.BR Sys->OWRITE , -.BR Sys->ORDWR , -possibly ORed with -.BR Sys->ORCLOSE , -corresponding to the mode with which the file was opened. -It is set by -.IB f .open -(see below). -.TP -.B uname -The name of the user that created the fid. -.TP -.B param -Set by -.B Styxservers -to the -.B aname -of the initial -.IR attach (5) -message, -and subsequently inherited by each new fid created by -.IR walk (5), -but not otherwise used by -.B Styxservers -itself, and may be changed by the application. -.TP -.B data -Unused by -.BR Styxservers ; -for application use. -It might be used, for instance, to implement a file that gives different -data to different clients. -.TP -.IB f .clone( nf ) -Copy the current state of all members of -.I f -except -.IB f .fid\f1,\fP -into -.IR nf , -and return -.IR nf . -Used by -.BR Styxserver.walk , -and is needed by an application only if it replaces that function. -.TP -.IB f .walk( qid ) -Make -.I f -refer to the file with the given -.IR qid : -set -.IB f .path -and -.IB f .qtype -from -.IB qid .path -and -.IB qid .qtype . -Used by -.IB Styxserver.walk -and is needed by an application only if it replaces that function. -.TP -.IB f .open( mode,\ qid ) -Mark -.I f -as `open', -set -.IR f .mode -to -.IR mode , -and set -.B path -and -.B qtype -to the path and type of -.IR qid . -Used by the -implementations of -.B open -and -.B create -messages. -The default implementation of -.IR open (5) -in -.B Styxserver -obtains the value of -.I mode -from -.B Styxserver.canopen -(below), -and -obtains the value of -.I qid -by querying the application's navigator. -.SS Styxserver and file server state -Each -.B Styxserver -value holds the state for a single file server, including its active fids, -the link to the external name space process, and other internal data. -Most of the state is manipulated through the member functions described below. -The exceptions are two read-only values: -the -.B Navigator -reference -.IB srv .t -which can be used to access that navigator; and -the file descriptor -.IB srv .fd -that is the file server's end of the connection to the Styx client. -Both values are initially provided by the file serving application, -but can be accessed through the -.B Styxserver -value for convenience. -The file descriptor value is normally used only through -.BR Styxserver.reply , -but will be needed directly if the caller needs the file descriptor value -as a parameter to -.IR sys-pctl (2) -when insulating the serving process's file descriptors from the surrounding environment. -.PP -The first set of functions in -.B Styxserver -provides common and default actions: -.TP -.B Styxserver.new(\fIfd\fP,\ \fIt\fP,\ \fIrootpath\fP) -Create a new -.BR Styxserver . -It returns a tuple, say -.RI ( c ", " srv ), -and spawns a new process, which uses -.IR styx (2) -to read and parse Styx messages read -from -.IR fd , -and send them down -.IR c ; -.I t -should be a -.B Navigator -adt which the -.B Styxserver -can use to answer queries -on the name space (see ``Navigating file trees'', below). -.I Rootpath -gives the Qid path of the root of the served name space. -.TP -.IB srv .reply(\fIm\fP) -Send a reply (R-message) to a client. The various utility methods, -listed below, call this function to make their response. -.TP -.IB srv .attach(\fIm\fP) -Respond to an -.IR attach (5) -message -.IR m , -creating a new fid in the process, and returning it. -Returns -.B nil -if -.IB m .fid -is a duplicate of an existing fid. -The value of the attach parameter -.IB m .aname -is copied into the new fid's -.B param -field, as is the attaching user name, -.IB m .uname . -.TP -.IB srv .clunk(\fIm\fP) -Respond to a -.IR clunk (5) -message -.IR m , -and return the old -.BR Fid . -Note that this does nothing about remove-on-close -files; that should be programmed explicitly if needed. -.TP -.IB srv .walk(\fIm\fP) -Respond to a -.IR walk (5) -message -.IR m , -querying -.IB srv . t -for information on existing files. -.TP -.IB srv .open(\fIm\fP) -Respond to an -.IR open (5) -message -.IR m . -This will allow a file to be opened if its permissions allow the -specified mode of access. -.TP -.IB srv .read(\fIm\fP) -Respond to a -.IR read (5) -message -.IR m . -If a directory is being read, the appropriate reply -is made; for files, an error is given. -.TP -.IB srv .remove(\fIm\fP) -Respond to a -.IR remove (5) -message -.IR m -with an error, clunking the fid as it does so, -and returning the old -.BR Fid . -.TP -.IB srv .stat(\fIm\fP) -Respond to a -.IR stat (5) -message -.IR m . -.TP -.IB srv .default(\fIgm\fP) -Respond to an arbitrary T-message, -.IR gm , -as appropriate (eg, by calling -.IB srv .walk -for a -.IR walk (5) -message). -It responds appropriately to -.IR version (5), -and replies to -.B Tauth -(see -.IR attach (5)) -stating that authentication is not required. -Other messages without an associated -.B Styxserver -function are generally responded to -with a ``permission denied'' error. -.PP -All the functions above check the validity of the fids, modes, counts and offsets -in the messages, and automatically reply to the client with a suitable -.IR error (5) -message on error. -.PP -The following further -.B Styxserver -operations are useful -in applications that override all or part of the default handling -(in particular, -to process read and write requests): -.TP -.IB srv .canopen( m ) -Check whether it is legal to open a file as requested by message -.IR m : -the fid is valid but not already open, the corresponding file exists and its -permissions allow access in the requested mode, and if -.B Sys->ORCLOSE -is requested, the parent directory is writable (to allow the file to be removed when closed). -.B Canopen -returns a tuple, say -.RI ( f ,\ mode ,\ d,\ err\ \fP). -If the open request was invalid, -.I f -will be nil, and the string -.I err -will diagnose the error (for return to the client in an -.B Rmsg.Error -message). -If the request was valid: -.I f -contains the -.B Fid -representing the file to be opened; -.I mode -is the access mode derived from -.IB m .mode , -.BR Sys->OREAD , -.BR Sys->OWRITE , -.BR Sys->ORDWR , -ORed with -.BR Sys->ORCLOSE ; -.I d -is a -.B Dir -value giving the file's attributes, obtained from the navigator; -and -.I err -is nil. -Once the application has done what it must to open the file, -it must call -.IB f .open -to mark it open. -.TP -.IB srv .cancreate( m ) -Checks whether the -creation of the file requested by -message -.I m -is legal: -the fid is valid but not open, refers to a directory, -the permissions returned by -.IR srv .t.stat -show that directory is writable by the requesting user, -the name does not already exist in that directory, -and the mode with which the new file would be opened is valid. -.B Cancreate -returns a tuple, say -.RI ( f ,\ mode,\ d,\ err\ \fP). -If the creation request was invalid, -.I f -will be nil, and the string -.I err -will diagnose the error, for use in an error reply to the client. -If the request was valid: -.I f -contains the -.B Fid -representing the parent directory; -.I mode -is the open mode as defined for -.B canopen -above; -.I d -is a -.B Dir -value containing some initial attributes for the new file or directory; -and -.I err -is nil. -The initial attributes set in -.I d -are: -.IB d .name -(the name of the file to be created); -.IB d .uid -and -.IB d .muid -(the user that did the initial attach); -.IB d .gid , -.IB d .dtype , -.IB d .dev -(taken from the parent directory's attributes); -and -.IB d .mode -holds the file mode that should be attributed to the new -file (taking into account the parent mode, as -described in -.IR open (5)). -The caller must supply -.IB d .qid -once the file has successfully been created, -and -.IB d .atime -and -.IB d .mtime ; -it must also call -.IB f .open -to mark -.I f -open and set its path to the file's path. -If the file cannot be created successfully, the application should reply with -an -.IR error (5) -message and leave -.I f -untouched. -The -.B Fid -.I f -will then continue to refer to the original directory, and remain unopened. -.TP -.IB srv .canread( m ) -Checks whether -.IR read (5) -message -.I m -refers to a valid fid that has been opened for reading, -and that the count and file offset are non-negative. -.B Canread -returns a tuple, say -.RI ( f ,\ err ); -if the attempted access is illegal, -.I f -will be nil, and -.I err -contains a description of the error, -otherwise -.I f -contains the -.B Fid -corresponding to the file in question. -It is typically called by an application's implementation of -.B Tmsg.Read -to obtain the -.B Fid -corresponding to the fid in the message, and check the access. -.TP -.IB srv .canwrite( m ) -Checks whether -message -.I m -refers to a valid fid that has been opened for writing, -and that the file offset is non-negative. -.B Canwrite -returns a tuple, say -.RI ( f ,\ err ); -if the attempted access is illegal, -.I f -will be nil, and -.I err -contains a description of the error, -otherwise -.I f -contains the -.B Fid -corresponding to the file in question. -It is typically called by an application's implementation of -.B Tmsg.Write -to obtain the -.B Fid -corresponding to the fid in the message, and check the access. -.TP -.IB srv .iounit() -Return an appropriate value for use as the -.I iounit -element in -.B Rmsg.Open -and -.B Rmsg.Create -replies, -as defined in -.IR open (5), -based on the message size negotiated by the initial -.IR version (5) -message. -.PP -The remaining functions are normally used only by servers that need to -override default actions. -They maintain and access the mapping between a client's fid values presented in -.B Tmsg -messages and the -.B Fid -values that represent the corresponding files internally. -.TP -.IB srv .newfid(\fIfid\fP) -Create a new -.B Fid -associated with number -.I fid -and return it. -Return nil if the -.I fid -is already in use (implies a client error if the server correctly clunks fids). -.TP -.IB srv .getfid(\fIfid\fP) -Get the -.B Fid -data associated with numeric id -.IR fid ; -return nil if there is none such (a malicious or erroneous client -can cause this). -.TP -.IB srv .delfid(\fIfid\fP) -Delete -.I fid -from the table of fids in the -.BR Styxserver . -(There is no error return.) -.TP -.IB srv .allfids() -Return a list of all current fids (ie, the files currently active on the client). -.PP -.B Newfid -is required when processing -.IR auth (5), -.IR attach (5) -and -.IR walk (5) -messages to create new fids. -.B Delfid -is used to clunk fids when processing -.IR clunk (5), -.IR remove (5), -and in a failed -.IR walk (5) -when it specified a new fid. -All other messages should refer only to already existing fids, and the associated -.B Fid -data is fetched by -.BR getfid . -.SS Navigating file trees -When a -.B Styxserver -instance needs to know about the namespace, -it queries an external process through a channel -by sending a -.B Navop -request; -each such request carries with it a -.B reply -channel through which the -reply should be made. -The reply tuple has a reference to a -.B Sys->Dir -value that is non-nil on success, and a diagnostic string -that is non-nil on error. -.PP -Files in the tree are referred to -by their Qid -.BR path . -The requests are: -.TF Walk -.TP -.BR Stat -.br -Find a file in the hierarchy by its -.BR path , -and reply with the corresponding -.B Dir -data if found (or a diagnostic on error). -.TP -.BR Walk -.br -Look for file -.B name -in the directory with the given -.BR path . -.TP -.BR Readdir -.br -Get information on selected files in the directory with the given -.BR path . -In this case, the reply channel is used to send -a sequence of values, one for each entry in the directory, finishing with a tuple value -.BR (nil,nil) . -The entries to return are those selected by an -.B offset -that is the index (origin 0) of the first directory entry to return, -and a -.B count -of a number of entries to return starting with that index. -Note that both values are expressed in units of directory entries, not as byte counts. -.PP -.B Styxserver -provides a -.B Navigator -adt to enable convenient access to this functionality; calls -into the -.B Navigator -adt are bundled up into requests on the channel, and the -reply returned. -The functions provided are: -.TP 10 -.BI Navigator.new( c ) -Create a new -.BR Navigator , -sending requests down -.IR c . -.TP -.IB t .stat(\fIpath\fP) -Find the file with the given -.IR path . -Return a tuple -.RI ( d ,\ err ), -where -.I d -holds directory information for the file -if found; otherwise -.I err -contains an error message. -.TP -.IB t .walk(\fIparent\fP,\ \fIname\fP) -Find the file with name -.I name -inside parent directory -.IR parent . -Return a tuple as for -.BR stat . -.TP -.IB t .readdir(\fIpath\fP,\ \fIoffset\fP,\ \fIcount\fP) -Return directory data read from directory -.IR path , -starting at entry -.I offset -for -.I count -entries. -.SS Other functions -The following functions provide some commonly used functionality: -.TP 10 -.BI readbytes( m ,\ d ) -Assuming that the file in question contains data -.IR d , -.B readbytes -returns an appropriate reply to -.IR read (5) -message -.IR m , -taking account of -.IB m .offset -and -.IB m.count -when extracting data from -.IR d . -.TP 10 -.BI readstr( m ,\ s ) -Assuming that the file in question contains string -.IR s , -.B readstr -returns an appropriate reply to -.IR read (5) -message -.IR m , -taking account of -.IB m .offset -and -.IB m.count -when extracting data from the UTF-8 representation of -.IR s . -.TP -.BI openok (\fIuname\fP,\ \fIomode\fP,\ \fIperm\fP,\ \fIfuid\fP,\ \fIfgid\fP) -Does standard permission checking, assuming user -.I uname -is trying to open a file with access mode -.IR omode , -where the file is owned by -.IR fuid , -has group -.IR fgid , -and permissions -.IR perm . -Returns true (non-zero) if permission would be granted, and false (zero) otherwise. -.TP -.BI openmode( o ) -Checks to see whether the open mode -.I o -is well-formed; if it is not, -.B openmode -returns -1; if it is, it returns the mode -with OTRUNC and ORCLOSE flags removed. -.TP -.BI traceset( on ) -If -.I on -is true (non-zero), -will trace Styx requests and replies, on standard error. -This option must be set before creating a -.BR Styxserver , -to ensure that it preserves its standard error descriptor. -.SS Constants -.B Styxservers -defines a number of constants applicable to the writing -of Styx servers, including: -.TP -.BR Einuse\fP,\fP\ Ebadfid\fP,\fP\ Eopen\fP,\fP\ Enotfound\fP,\fP\ Enotdir\fP,\fP\ Eperm\fP,\fP\ Ebadarg\fP,\fP\ Eexists -These provide standard strings for commonly used error conditions, -to be used in -.B Rmsg.Error -replies. -.SS Authentication -If authentication is required beyond that provided at the link level -(for instance by -.IR security-auth (2)), -the server application must handle -.B Tauth -itself, -remember the value of -.I afid -in that message, and generate an -.B Rauth -reply with a suitable Qid referring to a file with -.B Qid.qtype -of -.BR QTAUTH . -Following successful authentication by read and write on that file, -it must associate that status with the -.IR afid . -Then, on a subsequent -.B Tattach -message, before calling -.I srv .attach -it must check that the -.BR Tattach 's -.I afid -value corresponds to one previously authenticated, and -reply with an appropriate error if not. -.SH SOURCE -.B /appl/lib/styxservers.b -.SH SEE ALSO -.IR styxservers-nametree (2), -.IR sys-stat (2), -.IR intro (5) diff --git a/lib/mimetype b/lib/mimetype new file mode 100644 index 00000000..45cdb78d --- /dev/null +++ b/lib/mimetype @@ -0,0 +1,157 @@ +#suffix generic type specific type encoding safe? +.C text plain - y # C++ program +.Z - - compress m +.a application octet-stream - y +.ada text plain - y # ada program +.ai application postscript - y +.aif audio x-aiff - y +.aifc audio x-aiff - y +.aiff audio x-aiff - y +.asf video x-ms-asf - m # MS streaming +.asx video x-ms-asf - m # MS streaming +.au audio basic - y # sun audio +.avi video x-msvideo - m +.awk text plain - y # awk program +.bas text plain - y # basic program +.bat application octet-stream - r # DOS executable +.bbl text plain - y # BibTex output +.bcpio application x-bcpio - m +.bib text plain - y # BibTex input +.bmp image bmp - y # bitmapped image +.c text plain - y # C program +.c++ text plain - y # C++ program +.cacert application x-x509-ca-cert - y # DER X.509 CA certificate +.cc text plain - y +.cdf application x-netcdf - y +.class application java - y # Java bytecodes +.com application octet-stream - r # DOS executable +.cpio application x-cpio - y +.cpp text plain - y # DOS C++ program +.crt application x-x509-ca-cert - y # DER X.509 CA certificate +.css text css - m +.csv application vnd.ms-excel - y # Microsoft Excel comma-separated-values +.dat text plain - y # AMPL et al. +.diff text plain - y +.doc application msword - n # Microsoft Word +.dvi application x-dvi - y # TeX output +.enc application octet-stream - y # encrypted file +.eps application postscript - y +.etx text x-setext - m +.exe application octet-stream - r # DOS executable +.executable application octet-stream - r # DOS executable +.exz application octet-stream gzip n # gzipped DOS executable +.f text plain - y # fortran-77 program +.fm application framemaker - y +.f90 text plain - y # fortran-90 program +.flc video x-flc - m +.fli video x-fli - m +.gif image gif - y +.gtar application x-gtar - m +.gz - - gzip m # gzipped file +.h text plain - y # C header file +.hdf application x-hdf - y +.hqx application octet-stream - m # Mac BinHex +.htm text html - m +.html text html - m +.ico image x-icon - y +.ief image ief - y +.jar application java-archive - y +.jfif image jpeg - y +.jfif-tbnl image jpeg - y +.jpe image jpeg - y +.jpeg image jpeg - y +.jpg image jpeg - y +.jpg image pjpeg - y +.latex application x-latex - y +.ltx application x-latex - y +.man application x-troff-man - y +.me application x-troff-me - y +.mid audio midi - y # MIDI music +.mime message rfc822 - y +.mod text plain - y # AMPL et al. +.mov video quicktime - y +.movie video x-sgi-movie - y +.mpe video mpeg - y +.mpeg video mpeg - y +.mpg video mpeg - y +.ms application x-troff-ms - y +.mv video x-sgi-movie - y +.nc application x-netcdf - y +.o application octet-stream - y +.oda application oda - m +.p text plain - y # Pascal program +.p7s application x-pkcs7-signature - y # SMIME +.pbm image x-portable-bitmap - y +.pdf application pdf - y # Adobe Portable Document Format +.pif application octet-stream - r # DOS executable +.pgm image x-portable-graymap - y +.pl text plain - y +.pnm image x-portable-anymap - y +.ppm image x-portable-pixmap - y +.ppt application vnd.ms-powerpoint - n # Microsoft PowerPoint +.ps application postscript - m +.qcp audio vnd.qcelp - y # Qualcomm CELP +.qcp2 audio qcp - y # Qualcomm CELP +.qt video quicktime - y +.r text plain - y # ratfor program +.ra audio x-pn-realaudio - y # G2 RealAudio +.ram audio x-pn-realaudio - y # G2 RealAudio +.ras image x-cmu-rast - y +.rc text plain - y # rc +.rfr text plain - y # refer +.rgb image x-rgb - y +.rm application x-pn-realmedia - y # G2 RealAudio +.roff application x-troff - y +.rpm audio x-pn-realaudio-plugin - y # G2 RealAudio +.rtf application rtf - y +.rtx text richtext - y +.scr application octet-stream - r # DOS executable (screen saver) +.sh application x-shar - m +.shar application x-shar - m +.smi application smil - m # sync multimedia +.smil application smil - m # sync multimedia +.snd audio basic - y +.suspect application octet-stream - y # upas/vf +.sv4cpio application x-sv4cpio - m +.sv4crc application x-sv4crc - y +.t application x-troff - y +.tar application x-tar - m +.tardist application x-tardist - n # SGI SoftwareManager +.taz application x-tar compress m +.tcl application x-tcl - y +.tex application x-tex - y # Tex input +.texi application x-texinfo - y +.texinfo application x-texinfo - y +.text text plain - y +.tgz application x-tar gzip m +.tif image tiff - y +.tiff image tiff - y +.toc text plain - y # table of contents +.tr application x-troff - y +.trz application x-tar compress m +.tsv text tab-separated-values - y +.txt text - - y +.txt text plain - y +.ucert application x-x509-user-cert - y # DER X.509 user certificate +.ustar application x-ustar - m +.vcf text x-vcard - y # vCard +.wav audio x-wav - y +.wbmp image vnd.wap.wbmp - y # wireless bitmap +.wml text vnd.wap.wml - m # WML doc +.wmlc application vnd.wap.wmlc - m # compiled WML doc +.wmls text vnd.wap.wmlscript - m # WMLScript +.wmlsc application vnd.wap.wmlscriptc - m # compiled WMLScript +.wsrc application x-wais-source - y +.xbm image x-xbitmap - y # X bitmap +.xgz - - x-gzip m # gzipped file +.xls application vnd.ms-excel - n # Microsoft Excel +.xml text xml - m +.xpm image x-xpixmap - y +.xwd image x-xwindowdump - y +.z - - compress m +.Z - - compress m +.zip application zip - n +.zzz application sleep - n # testing +- application x-gunzip - p # type for .tar.gz +- message delivery-status - y # mail bounces +- application pgp-signature - y diff --git a/lib/mk/binds b/lib/mk/binds new file mode 100644 index 00000000..34d4b2ae --- /dev/null +++ b/lib/mk/binds @@ -0,0 +1,2 @@ +/lib/mk/mkconfig /mkconfig +/lib/mk/mksubdirs /mkfiles/mksubdirs diff --git a/lib/mk/mkconfig b/lib/mk/mkconfig new file mode 100644 index 00000000..17d31a93 --- /dev/null +++ b/lib/mk/mkconfig @@ -0,0 +1,28 @@ +# +# Set the following 4 variables. The host system is the system where +# the software will be built; the target system is where it will run. +# They are almost always the same. + +# On Nt systems, the ROOT path MUST be of the form `drive:/path' +ROOT= + +# +# Except for building kernels, SYSTARG must always be the same as SYSHOST +# +SYSHOST=Plan9 # build system OS type (Hp, Inferno, Irix, Linux, Nt, Plan9, Solaris) +SYSTARG=$SYSHOST # target system OS type (Hp, Inferno, Irix, Linux, Nt, Plan9, Solaris) + +# +# specify the architecture of the target system - Inferno imports it from the +# environment; for other systems it is usually just hard-coded +# +#OBJTYPE=386 # target system object type (s800, mips, 386, arm, sparc) +OBJTYPE=386 + +# +# no changes required beyond this point +# +OBJDIR=$SYSTARG/$OBJTYPE + +<$ROOT/mkfiles/mkhost-$SYSHOST # variables appropriate for host system +<$ROOT/mkfiles/mkfile-$SYSTARG-$OBJTYPE # variables used to build target object type diff --git a/lib/mk/mksubdirs b/lib/mk/mksubdirs new file mode 100644 index 00000000..9b076930 --- /dev/null +++ b/lib/mk/mksubdirs @@ -0,0 +1,13 @@ +all:V: all-$SHELLTYPE +install:V: install-$SHELLTYPE +uninstall:V: uninstall-$SHELLTYPE +nuke:V: nuke-$SHELLTYPE +clean:V: clean-$SHELLTYPE + +%-rc %-nt %-sh:QV: + for j in $DIRS { + if { ftest -d $j } { + echo 'cd' $j '; mk' $MKFLAGS $stem + cd $j; mk $MKFLAGS $stem; cd .. + } + } diff --git a/lib9/getcallerpc-Linux-arm.S b/lib9/getcallerpc-Linux-arm.S new file mode 100644 index 00000000..44c8e627 --- /dev/null +++ b/lib9/getcallerpc-Linux-arm.S @@ -0,0 +1 @@ +/* getcallerpc for arm Linux is placed in lib9.h as inline function*/ diff --git a/lib9/getcallerpc-MacOSX-386.s b/lib9/getcallerpc-MacOSX-386.s new file mode 100644 index 00000000..92f940d7 --- /dev/null +++ b/lib9/getcallerpc-MacOSX-386.s @@ -0,0 +1,8 @@ + .file "getcallerpc-MacOSX-386.s" + + .text + +.globl _getcallerpc +_getcallerpc: + movl 4(%ebp), %eax + ret diff --git a/mkfiles/mkfile-Linux-arm b/mkfiles/mkfile-Linux-arm new file mode 100644 index 00000000..709cb3fc --- /dev/null +++ b/mkfiles/mkfile-Linux-arm @@ -0,0 +1,27 @@ +TARGMODEL= Posix +TARGSHTYPE= sh +CPUS= arm + +O= o +OS= o + +AR= ar +ARFLAGS= ruvs + +AS= arm-gcc -c +ASFLAGS= + +CC= arm-gcc -c +CFLAGS= -O\ + -I$ROOT/Linux/arm/include\ + -I$ROOT/include\ + -DLINUX_ARM + +ANSICPP= +LD= arm-gcc +LDFLAGS= + +SYSLIBS= + +YACC= yacc +YFLAGS= -d diff --git a/mkfiles/mkfile-MacOSX-386 b/mkfiles/mkfile-MacOSX-386 new file mode 100644 index 00000000..5aab0869 --- /dev/null +++ b/mkfiles/mkfile-MacOSX-386 @@ -0,0 +1,41 @@ +TARGMODEL= Posix +TARGSHTYPE= sh +CPUS= 386 + +O= o +OS= o + +AR= ar +ARFLAGS= ruvs +A= a + +AS= cc -c -arch i386 +ASFLAGS= + +ISYSROOT= -isysroot /Developer/SDKs/MacOSX10.4u.sdk + +CC= cc -c +COPTFLAGS= -Os +CDEBUGFLAGS= +CTHREADFLAGS= +CFLAGS= -arch i386\ + -mmacosx-version-min=10.4\ + -Wno-deprecated-declarations -Wuninitialized -Wunused -Wreturn-type -Wimplicit -Wno-four-char-constants -Wno-unknown-pragmas\ + -pipe\ + -fno-strict-aliasing\ + -no-cpp-precomp\ + -mno-fused-madd\ + -I$ROOT/MacOSX/386/include\ + -I$ROOT/include\ + $COPTFLAGS $CDEBUGFLAGS\ + +LD= cc -arch i386 +LDFLAGS=\ + -mmacosx-version-min=10.4\ + -multiply_defined suppress + +SYSLIBS= + +YACC= yacc +YFLAGS= -d + diff --git a/module/csv.m b/module/csv.m new file mode 100644 index 00000000..059d97ec --- /dev/null +++ b/module/csv.m @@ -0,0 +1,8 @@ +CSV: module +{ + PATH: con "/dis/lib/csv.dis"; + + init: fn(b: Bufio); + getline: fn(fd: ref Bufio->Iobuf): list of string; + quote: fn(s: string): string; +}; diff --git a/module/ida.m b/module/ida.m new file mode 100644 index 00000000..f2503dd7 --- /dev/null +++ b/module/ida.m @@ -0,0 +1,24 @@ +Ida: module +{ + PATH: con "/dis/lib/ida/ida.dis"; + + Frag: adt { + dlen: int; # length of original data + m: int; # minimum pieces for reconstruction + a: array of int; # encoding array row for this fragment + enc: array of int; # encoded data + + tag: array of byte; # user data, such as SHA1 hash + }; + + init: fn(); + fragment: fn(data: array of byte, m: int): ref Frag; + consistent: fn(frags: array of ref Frag): array of ref Frag; + reconstruct: fn(frags: array of ref Frag): (array of byte, string); +}; + +Idatab: module +{ + PATH: con "/dis/lib/ida/idatab.dis"; + init: fn(): array of int; +}; diff --git a/module/rfc822.m b/module/rfc822.m new file mode 100644 index 00000000..7e4c8897 --- /dev/null +++ b/module/rfc822.m @@ -0,0 +1,68 @@ +RFC822: module +{ + PATH: con "/dis/lib/rfc822.dis"; + + init: fn(b: Bufio); + + # TO DO: multipart ... + + # token values reserved to represent a word and a quoted string + Word, QString: con 1+iota; + + Maxrequest: con 16*1024; # more than enough for anything sensible + + Rfclex: adt { + fd: ref Bufio->Iobuf; # open on a single line + wordval: string; # text if Word or QString + tok: int; # last token seen + eof: int; # end of file (ignore subsequent ungetc) + + seen: list of (int, string); # pushback + + mk: fn(a: array of byte): ref Rfclex; + getc: fn(p: self ref Rfclex): int; + ungetc: fn(p: self ref Rfclex); + lex: fn(p: self ref Rfclex): int; + unlex: fn(p: self ref Rfclex); + skipws: fn(p: self ref Rfclex): int; + + line: fn(p: self ref Rfclex): string; + }; + + readheaders: fn(fd: ref Bufio->Iobuf, limit: int): array of (string, array of byte); + parseparams: fn(ps: ref Rfclex): list of (string, string); + parsecontent: fn(ps: ref Rfclex, multipart: int, head: list of ref Content): list of ref Content; + mimefields: fn(ps: ref Rfclex): list of (string, list of (string, string)); + # TO DO: parse addresses + + quotable: fn(s: string): int; + quote: fn(s: string): string; + + # convert an epoch time into http-formatted text + sec2date: fn(secs: int): string; + + # convert a date in http text format to seconds from epoch + date2sec: fn(s: string): int; + + # current time + now: fn(): int; + + # current time as a string + time: fn(): string; + + # + # mime-related things + # + Content: adt{ + generic: string; + specific: string; + params: list of (string, string); + + mk: fn(generic: string, specific: string, params: list of (string, string)): ref Content; + check: fn(c: self ref Content, oks: list of ref Content): int; + text: fn(c: self ref Content): string; + }; + + suffixclass: fn(name: string): (ref Content, ref Content); + dataclass: fn(a: array of byte): (ref Content, ref Content); +}; diff --git a/module/uris.m b/module/uris.m new file mode 100644 index 00000000..cc2226c6 --- /dev/null +++ b/module/uris.m @@ -0,0 +1,36 @@ +URIs: module +{ + PATH: con "/dis/lib/w3c/uris.dis"; + + # URI Generic Syntax (RFC 3986) + # + # scheme://authority/path?query#fragment + # + URI: adt + { + scheme: string; + userinfo: string; # authority, part I + host: string; # authority, part II + port: string; # authority, part III + path: string; # starts with / if path-abempty or path-absolute + query: string; # includes ? if not nil + fragment: string; # includes # if not nil + + parse: fn(s: string): ref URI; + text: fn(u: self ref URI): string; + authority: fn(u: self ref URI): string; + addbase: fn(u: self ref URI, base: ref URI): ref URI; + copy: fn(u: self ref URI): ref URI; + hasauthority: fn(u: self ref URI): int; + isabsolute: fn(u: self ref URI): int; + nodots: fn(u: self ref URI): ref URI; + pathonly: fn(u: self ref URI): ref URI; + userpw: fn(u: self ref URI): (string, string); # ``deprecated format'' + eq: fn(u: self ref URI, v: ref URI): int; + eqf: fn(u: self ref URI, v: ref URI): int; + }; + + init: fn(); + dec: fn(s: string): string; + enc: fn(s: string, safe: string): string; +}; diff --git a/utils/cvbit/cvbit.c b/utils/cvbit/cvbit.c new file mode 100644 index 00000000..dcd140f0 --- /dev/null +++ b/utils/cvbit/cvbit.c @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include + +static int invert = 0; + +void +main(int argc, char **argv) +{ + Memimage *im, *om; + char *s; + ulong ofmt; + + ofmt = 0; + ARGBEGIN{ + case 'i': + invert = 1; + break; + case 'c': + s = ARGF(); + if(s==nil) + break; + ofmt = strtochan(s); + if(ofmt == 0){ + fprint(2, "cvbit: bad chan: %s\n", s); + exits("chan"); + } + break; + }ARGEND + + memimageinit(); + im = readmemimage(0); + if(im == nil){ + fprint(2, "cvbit: can't read image: %r\n"); + exits("read"); + } + if(ofmt){ + om = allocmemimage(im->r, ofmt); + if(om == nil){ + fprint(2, "cvbit: can't allocate new image: %r\n"); + exits("alloc"); + } + memimagedraw(om, om->r, im, im->r.min, nil, ZP, S); + }else + om = im; + if(invert){ + uchar *buf; + int bpl, y, x; + + bpl = bytesperline(om->r, om->depth); + buf = malloc(bpl); + for(y=om->r.min.y; yr.max.y; y++){ + if(unloadmemimage(om, Rpt(Pt(om->r.min.x,y), Pt(om->r.max.x,y+1)), buf, bpl) != bpl){ + fprint(2, "cvbit: can't unload image line\n"); + exits("unload"); + } + for(x=0; xr.min.x,y), Pt(om->r.max.x,y+1)), buf, bpl) != bpl){ + fprint(2, "cvbit: can't load image line\n"); + exits("load"); + } + } + } + if(writememimage(1, om) < 0){ + fprint(2, "cvbit: can't write image: %r\n"); + exits("write"); + } + exits(nil); +} + +char* +poolname(Pool *p) +{ + USED(p); + return "none"; +} + +void +poolsetcompact(Pool *p, void (*f)(void*, void*)) +{ + USED(p); + USED(f); +} diff --git a/utils/cvbit/mkfile b/utils/cvbit/mkfile new file mode 100644 index 00000000..8d4b853a --- /dev/null +++ b/utils/cvbit/mkfile @@ -0,0 +1,15 @@ +<../../mkconfig + +TARG=cvbit + +OFILES= cvbit.$O\ + +HFILES= \ + $ROOT/include/draw.h\ + $ROOT/include/memdraw.h\ + +LIBS= draw memdraw draw bio 9 + +BIN=$ROOT/$OBJDIR/bin + +<$ROOT/mkfiles/mkone-$SHELLTYPE -- cgit v1.2.3