diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-08-03 16:54:30 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-08-03 16:54:30 +0200 |
commit | 28592ee1ea1f5cdffcf85472f9de0285d928cf12 (patch) | |
tree | 32944e18b23f7fe4a0818a694aa2a6dfb1835463 /src | |
parent | e836bee4716dc0d4d913537ad3ad1925a7ac32d0 (diff) | |
download | golang-28592ee1ea1f5cdffcf85472f9de0285d928cf12.tar.gz |
Imported Upstream version 59upstream/59
Diffstat (limited to 'src')
562 files changed, 22810 insertions, 7089 deletions
diff --git a/src/Make.ccmd b/src/Make.ccmd index 88f647152..40cc3a0e8 100644 --- a/src/Make.ccmd +++ b/src/Make.ccmd @@ -9,7 +9,7 @@ TARG:=$(TARG).exe endif $(TARG): $(OFILES) $(LIB) - $(HOST_LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) $(LIB) -lbio -l9 -lm $(HOST_LDFLAGS) + $(HOST_LD) -o $(TARG) -L"$(GOROOT)"/lib $(OFILES) $(LIB) -lmach -lbio -l9 -lm $(HOST_LDFLAGS) $(OFILES): $(HFILES) @@ -36,6 +36,7 @@ y.tab.c: y.tab.h all: $(TARG) +# Use $(PWD)/$*.c so that gdb shows full path in stack traces. %.$(HOST_O): %.c $(HOST_CC) $(HOST_CFLAGS) -c "$(PWD)/$*.c" diff --git a/src/Make.clib b/src/Make.clib index 25fe88463..4a7ea02d9 100644 --- a/src/Make.clib +++ b/src/Make.clib @@ -6,6 +6,7 @@ all: $(LIB) +# Use $(PWD)/$*.c so that gdb shows full path in stack traces. %.$(HOST_O): %.c $(HOST_CC) $(HOST_CFLAGS) -c "$(PWD)/$*.c" diff --git a/src/all-qemu.bash b/src/all-qemu.bash index 6d5cd6edd..c7079ba13 100755 --- a/src/all-qemu.bash +++ b/src/all-qemu.bash @@ -8,7 +8,8 @@ export NOTEST="" -NOTEST="$NOTEST big" # xxx +NOTEST="$NOTEST big" # just slow +NOTEST="$NOTEST go/build" # wants to run cgo NOTEST="$NOTEST http net rpc syslog websocket" # no localhost network NOTEST="$NOTEST os" # 64-bit seek fails diff --git a/src/cmd/5c/gc.h b/src/cmd/5c/gc.h index 549e0c88a..ff6d51916 100644 --- a/src/cmd/5c/gc.h +++ b/src/cmd/5c/gc.h @@ -28,7 +28,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. - +#include <u.h> #include "../cc/cc.h" #include "../5l/5.out.h" diff --git a/src/cmd/5c/txt.c b/src/cmd/5c/txt.c index 4be1f6f62..a32387bc1 100644 --- a/src/cmd/5c/txt.c +++ b/src/cmd/5c/txt.c @@ -292,8 +292,7 @@ tmpreg(void) void regalloc(Node *n, Node *tn, Node *o) { - int i, j; - static int lasti; + int i; switch(tn->type->etype) { case TCHAR: @@ -310,16 +309,9 @@ regalloc(Node *n, Node *tn, Node *o) if(i >= 0 && i < NREG) goto out; } - j = lasti + REGRET+1; - for(i=REGRET+1; i<NREG; i++) { - if(j >= NREG) - j = REGRET+1; - if(reg[j] == 0) { - i = j; + for(i=REGRET+1; i<=REGEXT-2; i++) + if(reg[i] == 0) goto out; - } - j++; - } diag(tn, "out of fixed registers"); goto err; @@ -331,16 +323,9 @@ regalloc(Node *n, Node *tn, Node *o) if(i >= NREG && i < NREG+NFREG) goto out; } - j = 0*2 + NREG; - for(i=NREG; i<NREG+NFREG; i++) { - if(j >= NREG+NFREG) - j = NREG; - if(reg[j] == 0) { - i = j; + for(i=NREG; i<NREG+NFREG; i++) + if(reg[i] == 0) goto out; - } - j++; - } diag(tn, "out of float registers"); goto err; } @@ -350,9 +335,6 @@ err: return; out: reg[i]++; -/* lasti++; *** StrongARM does register forwarding */ - if(lasti >= 5) - lasti = 0; nodreg(n, tn, i); } diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c index 0bc1b38fc..d5b00b34d 100644 --- a/src/cmd/5g/ggen.c +++ b/src/cmd/5g/ggen.c @@ -22,6 +22,32 @@ defframe(Prog *ptxt) maxstksize = 0; } +// Sweep the prog list to mark any used nodes. +void +markautoused(Prog* p) +{ + for (; p; p = p->link) { + if (p->from.name == D_AUTO && p->from.node) + p->from.node->used++; + + if (p->to.name == D_AUTO && p->to.node) + p->to.node->used++; + } +} + +// Fixup instructions after compactframe has moved all autos around. +void +fixautoused(Prog* p) +{ + for (; p; p = p->link) { + if (p->from.name == D_AUTO && p->from.node) + p->from.offset += p->from.node->stkdelta; + + if (p->to.name == D_AUTO && p->to.node) + p->to.offset += p->to.node->stkdelta; + } +} + /* * generate: * call f @@ -147,13 +173,13 @@ cgen_callinter(Node *n, Node *res, int proc) nodindreg(&nodsp, types[tptr], REGSP); nodsp.xoffset = 4; nodo.xoffset += widthptr; - cgen(&nodo, &nodsp); // 4(SP) = 8(REG) -- i.s + cgen(&nodo, &nodsp); // 4(SP) = 4(REG) -- i.data nodo.xoffset -= widthptr; - cgen(&nodo, &nodr); // REG = 0(REG) -- i.m + cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab nodo.xoffset = n->left->xoffset + 3*widthptr + 8; - cgen(&nodo, &nodr); // REG = 32+offset(REG) -- i.m->fun[f] + cgen(&nodo, &nodr); // REG = 20+offset(REG) -- i.tab->fun[f] // BOTCH nodr.type = fntype; nodr.type = n->left->type; diff --git a/src/cmd/5g/gobj.c b/src/cmd/5g/gobj.c index acece6c0d..27c8be67d 100644 --- a/src/cmd/5g/gobj.c +++ b/src/cmd/5g/gobj.c @@ -182,6 +182,8 @@ dumpfuncs(void) // fix up pc pcloc = 0; for(pl=plist; pl!=nil; pl=pl->link) { + if(isblank(pl->name)) + continue; for(p=pl->firstpc; p!=P; p=p->link) { p->loc = pcloc; if(p->as != ADATA && p->as != AGLOBL) @@ -191,6 +193,8 @@ dumpfuncs(void) // put out functions for(pl=plist; pl!=nil; pl=pl->link) { + if(isblank(pl->name)) + continue; if(debug['S']) { s = S; diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c index caaa3e246..2d9218461 100644 --- a/src/cmd/5g/gsubr.c +++ b/src/cmd/5g/gsubr.c @@ -102,6 +102,19 @@ patch(Prog *p, Prog *to) p->to.offset = to->loc; } +Prog* +unpatch(Prog *p) +{ + Prog *q; + + if(p->to.type != D_BRANCH) + fatal("unpatch: not a branch"); + q = p->to.branch; + p->to.branch = P; + p->to.offset = 0; + return q; +} + /* * start a new Prog list. */ diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c index 5fba02c9e..77d0a87eb 100644 --- a/src/cmd/5g/reg.c +++ b/src/cmd/5g/reg.c @@ -1525,6 +1525,7 @@ noreturn(Prog *p) symlist[1] = pkglookup("panicslice", runtimepkg); symlist[2] = pkglookup("throwinit", runtimepkg); symlist[3] = pkglookup("panic", runtimepkg); + symlist[4] = pkglookup("panicwrap", runtimepkg); } s = p->to.sym; @@ -1555,27 +1556,27 @@ dumpone(Reg *r) r->regdiff.b[z] | r->act.b[z] | 0; -// if(bany(&bit)) { -// print("\t"); -// if(bany(&r->set)) -// print(" s:%Q", r->set); -// if(bany(&r->use1)) -// print(" u1:%Q", r->use1); -// if(bany(&r->use2)) -// print(" u2:%Q", r->use2); -// if(bany(&r->refbehind)) -// print(" rb:%Q ", r->refbehind); -// if(bany(&r->refahead)) -// print(" ra:%Q ", r->refahead); -// if(bany(&r->calbehind)) -// print("cb:%Q ", r->calbehind); -// if(bany(&r->calahead)) -// print(" ca:%Q ", r->calahead); -// if(bany(&r->regdiff)) -// print(" d:%Q ", r->regdiff); -// if(bany(&r->act)) -// print(" a:%Q ", r->act); -// } + if(bany(&bit)) { + print("\t"); + if(bany(&r->set)) + print(" s:%Q", r->set); + if(bany(&r->use1)) + print(" u1:%Q", r->use1); + if(bany(&r->use2)) + print(" u2:%Q", r->use2); + if(bany(&r->refbehind)) + print(" rb:%Q ", r->refbehind); + if(bany(&r->refahead)) + print(" ra:%Q ", r->refahead); + if(bany(&r->calbehind)) + print("cb:%Q ", r->calbehind); + if(bany(&r->calahead)) + print(" ca:%Q ", r->calahead); + if(bany(&r->regdiff)) + print(" d:%Q ", r->regdiff); + if(bany(&r->act)) + print(" a:%Q ", r->act); + } print("\n"); } diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c index 4afed2b80..2c9e50d00 100644 --- a/src/cmd/5l/asm.c +++ b/src/cmd/5l/asm.c @@ -68,9 +68,6 @@ enum { ElfStrText, ElfStrData, ElfStrBss, - ElfStrGosymcounts, - ElfStrGosymtab, - ElfStrGopclntab, ElfStrSymtab, ElfStrStrtab, ElfStrShstrtab, @@ -160,12 +157,11 @@ doelf(void) elfstr[ElfStrEmpty] = addstring(shstrtab, ""); elfstr[ElfStrText] = addstring(shstrtab, ".text"); elfstr[ElfStrData] = addstring(shstrtab, ".data"); - addstring(shstrtab, ".rodata"); elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); + addstring(shstrtab, ".rodata"); + addstring(shstrtab, ".gosymtab"); + addstring(shstrtab, ".gopclntab"); if(!debug['s']) { - elfstr[ElfStrGosymcounts] = addstring(shstrtab, ".gosymcounts"); - elfstr[ElfStrGosymtab] = addstring(shstrtab, ".gosymtab"); - elfstr[ElfStrGopclntab] = addstring(shstrtab, ".gopclntab"); elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab"); elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab"); } @@ -307,10 +303,11 @@ asmb(void) seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); codeblk(sect->vaddr, sect->len); - /* output read-only data in text segment */ - sect = segtext.sect->next; - seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); - datblk(sect->vaddr, sect->len); + /* output read-only data in text segment (rodata, gosymtab and pclntab) */ + for(sect = sect->next; sect != nil; sect = sect->next) { + seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); + datblk(sect->vaddr, sect->len); + } if(debug['v']) Bprint(&bso, "%5.2f datblk\n", cputime()); @@ -572,18 +569,6 @@ asmb(void) elfshbits(sect); if (!debug['s']) { - sh = newElfShdr(elfstr[ElfStrGosymtab]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - shsym(sh, lookup("symtab", 0)); - - sh = newElfShdr(elfstr[ElfStrGopclntab]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - shsym(sh, lookup("pclntab", 0)); - sh = newElfShdr(elfstr[ElfStrSymtab]); sh->type = SHT_SYMTAB; sh->off = symo; diff --git a/src/cmd/5l/mkenam b/src/cmd/5l/mkenam index 265cb9988..6cccb0263 100644 --- a/src/cmd/5l/mkenam +++ b/src/cmd/5l/mkenam @@ -28,18 +28,18 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -ed - ../5l/5.out.h <<'!' -v/^ A/d -,s/^ A/ "/ -g/ .*$/s/// -,s/,*$/",/ -1i -char* anames[] = -{ -. -$a -}; -. -w enam.c -Q -! +awk ' +BEGIN { + print "char* anames[] =" + print "{" +} + +/^ A/ { + name=$1 + sub(/,/, "", name) + sub(/^A/, "", name) + print "\t\"" name "\"," +} + +END { print "};" } +' ../5l/5.out.h >enam.c diff --git a/src/cmd/5l/noop.c b/src/cmd/5l/noop.c index e7c2db5f2..eb44344f4 100644 --- a/src/cmd/5l/noop.c +++ b/src/cmd/5l/noop.c @@ -45,8 +45,6 @@ static Sym* sym_divu; static Sym* sym_mod; static Sym* sym_modu; -static void setdiv(int); - void noops(void) { @@ -93,7 +91,6 @@ noops(void) if(prog_div == P) initdiv(); cursym->text->mark &= ~LEAF; - setdiv(p->as); continue; case ANOP: @@ -533,27 +530,6 @@ initdiv(void) } } -static void -setdiv(int as) -{ - Prog *p = nil; - - switch(as){ - case ADIV: - p = prog_div; - break; - case ADIVU: - p = prog_divu; - break; - case AMOD: - p = prog_mod; - break; - case AMODU: - p = prog_modu; - break; - } -} - void nocache(Prog *p) { diff --git a/src/cmd/6c/cgen.c b/src/cmd/6c/cgen.c index 90394884f..7aa4aa976 100644 --- a/src/cmd/6c/cgen.c +++ b/src/cmd/6c/cgen.c @@ -1544,7 +1544,7 @@ sugen(Node *n, Node *nn, int32 w) nod0.addable = 0; nod0.right = l; - /* prtree(&nod0, "hand craft"); /* */ + // prtree(&nod0, "hand craft"); cgen(&nod0, Z); } break; diff --git a/src/cmd/6c/gc.h b/src/cmd/6c/gc.h index 735cd8909..775d97281 100644 --- a/src/cmd/6c/gc.h +++ b/src/cmd/6c/gc.h @@ -28,6 +28,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include <u.h> #include "../cc/cc.h" #include "../6l/6.out.h" diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c index 5260335df..9e7fbab0d 100644 --- a/src/cmd/6g/ggen.c +++ b/src/cmd/6g/ggen.c @@ -18,6 +18,32 @@ defframe(Prog *ptxt) ptxt->to.offset |= rnd(stksize+maxarg, widthptr); } +// Sweep the prog list to mark any used nodes. +void +markautoused(Prog* p) +{ + for (; p; p = p->link) { + if (p->from.type == D_AUTO && p->from.node) + p->from.node->used++; + + if (p->to.type == D_AUTO && p->to.node) + p->to.node->used++; + } +} + +// Fixup instructions after compactframe has moved all autos around. +void +fixautoused(Prog* p) +{ + for (; p; p = p->link) { + if (p->from.type == D_AUTO && p->from.node) + p->from.offset += p->from.node->stkdelta; + + if (p->to.type == D_AUTO && p->to.node) + p->to.offset += p->to.node->stkdelta; + } +} + /* * generate: @@ -102,13 +128,13 @@ cgen_callinter(Node *n, Node *res, int proc) nodindreg(&nodsp, types[tptr], D_SP); nodo.xoffset += widthptr; - cgen(&nodo, &nodsp); // 0(SP) = 8(REG) -- i.s + cgen(&nodo, &nodsp); // 0(SP) = 8(REG) -- i.data nodo.xoffset -= widthptr; - cgen(&nodo, &nodr); // REG = 0(REG) -- i.m + cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab nodo.xoffset = n->left->xoffset + 3*widthptr + 8; - cgen(&nodo, &nodr); // REG = 32+offset(REG) -- i.m->fun[f] + cgen(&nodo, &nodr); // REG = 32+offset(REG) -- i.tab->fun[f] // BOTCH nodr.type = fntype; nodr.type = n->left->type; diff --git a/src/cmd/6g/gobj.c b/src/cmd/6g/gobj.c index 507764a3b..ba8a4870e 100644 --- a/src/cmd/6g/gobj.c +++ b/src/cmd/6g/gobj.c @@ -228,6 +228,8 @@ dumpfuncs(void) // fix up pc pcloc = 0; for(pl=plist; pl!=nil; pl=pl->link) { + if(isblank(pl->name)) + continue; for(p=pl->firstpc; p!=P; p=p->link) { p->loc = pcloc; if(p->as != ADATA && p->as != AGLOBL) @@ -237,6 +239,8 @@ dumpfuncs(void) // put out functions for(pl=plist; pl!=nil; pl=pl->link) { + if(isblank(pl->name)) + continue; if(debug['S']) { s = S; diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c index 49d66e083..211915f54 100644 --- a/src/cmd/6g/gsubr.c +++ b/src/cmd/6g/gsubr.c @@ -98,6 +98,19 @@ patch(Prog *p, Prog *to) p->to.offset = to->loc; } +Prog* +unpatch(Prog *p) +{ + Prog *q; + + if(p->to.type != D_BRANCH) + fatal("unpatch: not a branch"); + q = p->to.branch; + p->to.branch = P; + p->to.offset = 0; + return q; +} + /* * start a new Prog list. */ diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c index af9b29cbc..4d4263047 100644 --- a/src/cmd/6g/reg.c +++ b/src/cmd/6g/reg.c @@ -1677,6 +1677,7 @@ noreturn(Prog *p) symlist[1] = pkglookup("panicslice", runtimepkg); symlist[2] = pkglookup("throwinit", runtimepkg); symlist[3] = pkglookup("panic", runtimepkg); + symlist[4] = pkglookup("panicwrap", runtimepkg); } s = p->to.sym; diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c index 4c04112b7..9136e0379 100644 --- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -87,9 +87,6 @@ enum { ElfStrText, ElfStrData, ElfStrBss, - ElfStrGosymcounts, - ElfStrGosymtab, - ElfStrGopclntab, ElfStrShstrtab, ElfStrSymtab, ElfStrStrtab, @@ -571,10 +568,9 @@ doelf(void) elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); addstring(shstrtab, ".elfdata"); addstring(shstrtab, ".rodata"); + addstring(shstrtab, ".gosymtab"); + addstring(shstrtab, ".gopclntab"); if(!debug['s']) { - elfstr[ElfStrGosymcounts] = addstring(shstrtab, ".gosymcounts"); - elfstr[ElfStrGosymtab] = addstring(shstrtab, ".gosymtab"); - elfstr[ElfStrGopclntab] = addstring(shstrtab, ".gopclntab"); elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab"); elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab"); dwarfaddshstrings(shstrtab); @@ -718,10 +714,11 @@ asmb(void) seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); codeblk(sect->vaddr, sect->len); - /* output read-only data in text segment */ - sect = segtext.sect->next; - seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); - datblk(sect->vaddr, sect->len); + /* output read-only data in text segment (rodata, gosymtab and pclntab) */ + for(sect = sect->next; sect != nil; sect = sect->next) { + seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); + datblk(sect->vaddr, sect->len); + } if(debug['v']) Bprint(&bso, "%5.2f datblk\n", cputime()); @@ -1013,18 +1010,6 @@ asmb(void) elfshbits(sect); if (!debug['s']) { - sh = newElfShdr(elfstr[ElfStrGosymtab]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - shsym(sh, lookup("symtab", 0)); - - sh = newElfShdr(elfstr[ElfStrGopclntab]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - shsym(sh, lookup("pclntab", 0)); - sh = newElfShdr(elfstr[ElfStrSymtab]); sh->type = SHT_SYMTAB; sh->off = symo; diff --git a/src/cmd/6l/mkenam b/src/cmd/6l/mkenam index 5cabb2633..3001dbe93 100644 --- a/src/cmd/6l/mkenam +++ b/src/cmd/6l/mkenam @@ -28,18 +28,18 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -/bin/ed - ../6l/6.out.h <<'!' -v/^ A/d -,s/^ A/ "/ -g/ .*$/s/// -,s/,*$/",/ -1i -char* anames[] = -{ -. -$a -}; -. -w enam.c -Q -! +awk ' +BEGIN { + print "char* anames[] =" + print "{" +} + +/^ A/ { + name=$1 + sub(/,/, "", name) + sub(/^A/, "", name) + print "\t\"" name "\"," +} + +END { print "};" } +' ../6l/6.out.h >enam.c diff --git a/src/cmd/8a/a.h b/src/cmd/8a/a.h index 3cb30f4c2..c5c22d7ba 100644 --- a/src/cmd/8a/a.h +++ b/src/cmd/8a/a.h @@ -28,8 +28,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include <u.h> -#include <libc.h> #include <bio.h> #include "../8l/8.out.h" @@ -57,7 +55,9 @@ typedef struct Gen2 Gen2; #define NSYMB 500 #define BUFSIZ 8192 #define HISTSZ 20 +#ifndef EOF #define EOF (-1) +#endif #define IGN (-2) #define GETC() ((--fi.c < 0)? filbuf(): *fi.p++ & 0xff) #define NHASH 503 diff --git a/src/cmd/8a/a.y b/src/cmd/8a/a.y index 04662f83d..a8ac773da 100644 --- a/src/cmd/8a/a.y +++ b/src/cmd/8a/a.y @@ -29,7 +29,9 @@ // THE SOFTWARE. %{ +#include <u.h> #include <stdio.h> /* if we don't, bison will, and a.h re-#defines getc */ +#include <libc.h> #include "a.h" %} %union { diff --git a/src/cmd/8a/l.s b/src/cmd/8a/l.s deleted file mode 100644 index 4e193a31a..000000000 --- a/src/cmd/8a/l.s +++ /dev/null @@ -1,734 +0,0 @@ -// Inferno utils/8a/l.s -// http://code.google.com/p/inferno-os/source/browse/utils/8a/l.s -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -/* - * Memory and machine-specific definitions. Used in C and assembler. - */ - -/* - * Sizes - */ -#define BI2BY 8 /* bits per byte */ -#define BI2WD 32 /* bits per word */ -#define BY2WD 4 /* bytes per word */ -#define BY2PG 4096 /* bytes per page */ -#define WD2PG (BY2PG/BY2WD) /* words per page */ -#define PGSHIFT 12 /* log(BY2PG) */ -#define PGROUND(s) (((s)+(BY2PG-1))&~(BY2PG-1)) - -#define MAXMACH 1 /* max # cpus system can run */ - -/* - * Time - */ -#define HZ (20) /* clock frequency */ -#define MS2HZ (1000/HZ) /* millisec per clock tick */ -#define TK2SEC(t) ((t)/HZ) /* ticks to seconds */ -#define TK2MS(t) ((((ulong)(t))*1000)/HZ) /* ticks to milliseconds */ -#define MS2TK(t) ((((ulong)(t))*HZ)/1000) /* milliseconds to ticks */ - -/* - * Fundamental addresses - */ - -/* - * Address spaces - * - * User is at 0-2GB - * Kernel is at 2GB-4GB - * - * To avoid an extra page map, both the user stack (USTKTOP) and - * the temporary user stack (TSTKTOP) should be in the the same - * 4 meg. - */ -#define UZERO 0 /* base of user address space */ -#define UTZERO (UZERO+BY2PG) /* first address in user text */ -#define KZERO 0x80000000 /* base of kernel address space */ -#define KTZERO KZERO /* first address in kernel text */ -#define USERADDR 0xC0000000 /* struct User */ -#define UREGADDR (USERADDR+BY2PG-4*19) -#define TSTKTOP USERADDR /* end of new stack in sysexec */ -#define TSTKSIZ 10 -#define USTKTOP (TSTKTOP-TSTKSIZ*BY2PG) /* byte just beyond user stack */ -#define USTKSIZE (16*1024*1024 - TSTKSIZ*BY2PG) /* size of user stack */ -#define ROMBIOS (KZERO|0xF0000) - -#define MACHSIZE 4096 - -#define isphys(x) (((ulong)x)&KZERO) - -/* - * known 80386 segments (in GDT) and their selectors - */ -#define NULLSEG 0 /* null segment */ -#define KDSEG 1 /* kernel data/stack */ -#define KESEG 2 /* kernel executable */ -#define UDSEG 3 /* user data/stack */ -#define UESEG 4 /* user executable */ -#define TSSSEG 5 /* task segment */ - -#define SELGDT (0<<3) /* selector is in gdt */ -#define SELLDT (1<<3) /* selector is in ldt */ - -#define SELECTOR(i, t, p) (((i)<<3) | (t) | (p)) - -#define NULLSEL SELECTOR(NULLSEG, SELGDT, 0) -#define KESEL SELECTOR(KESEG, SELGDT, 0) -#define KDSEL SELECTOR(KDSEG, SELGDT, 0) -#define UESEL SELECTOR(UESEG, SELGDT, 3) -#define UDSEL SELECTOR(UDSEG, SELGDT, 3) -#define TSSSEL SELECTOR(TSSSEG, SELGDT, 0) - -/* - * fields in segment descriptors - */ -#define SEGDATA (0x10<<8) /* data/stack segment */ -#define SEGEXEC (0x18<<8) /* executable segment */ -#define SEGTSS (0x9<<8) /* TSS segment */ -#define SEGCG (0x0C<<8) /* call gate */ -#define SEGIG (0x0E<<8) /* interrupt gate */ -#define SEGTG (0x0F<<8) /* task gate */ -#define SEGTYPE (0x1F<<8) - -#define SEGP (1<<15) /* segment present */ -#define SEGPL(x) ((x)<<13) /* priority level */ -#define SEGB (1<<22) /* granularity 1==4k (for expand-down) */ -#define SEGG (1<<23) /* granularity 1==4k (for other) */ -#define SEGE (1<<10) /* expand down */ -#define SEGW (1<<9) /* writable (for data/stack) */ -#define SEGR (1<<9) /* readable (for code) */ -#define SEGD (1<<22) /* default 1==32bit (for code) */ - -/* - * virtual MMU - */ -#define PTEMAPMEM (1024*1024) /* ??? */ -#define SEGMAPSIZE 16 /* ??? */ -#define PTEPERTAB (PTEMAPMEM/BY2PG) /* ??? */ -#define PPN(x) ((x)&~(BY2PG-1)) - -/* - * physical MMU - */ -#define PTEVALID (1<<0) -#define PTEUNCACHED 0 /* everything is uncached */ -#define PTEWRITE (1<<1) -#define PTERONLY (0<<1) -#define PTEKERNEL (0<<2) -#define PTEUSER (1<<2) - -/* - * flag register bits that we care about - */ -#define IFLAG 0x200 - -#define OP16 BYTE $0x66 - -/* - * about to walk all over ms/dos - turn off interrupts - */ -TEXT origin(SB),$0 - - CLI - -#ifdef BOOT -/* - * This part of l.s is used only in the boot kernel. - * It assumes that we are in real address mode, i.e., - * that we look like an 8086. - */ -/* - * relocate everything to a half meg and jump there - * - looks wierd because it is being assembled by a 32 bit - * assembler for a 16 bit world - */ - MOVL $0,BX - INCL BX - SHLL $15,BX - MOVL BX,CX - MOVW BX,ES - MOVL $0,SI - MOVL SI,DI - CLD; REP; MOVSL -/* JMPFAR 0X8000:$lowcore(SB) /**/ - BYTE $0xEA - WORD $lowcore(SB) - WORD $0X8000 - -TEXT lowcore(SB),$0 - -/* - * now that we're in low core, update the DS - */ - - MOVW BX,DS - -/* - * goto protected mode - */ -/* MOVL tgdtptr(SB),GDTR /**/ - BYTE $0x0f - BYTE $0x01 - BYTE $0x16 - WORD $tgdtptr(SB) - MOVL CR0,AX - ORL $1,AX - MOVL AX,CR0 - -/* - * clear prefetch queue (wierd code to avoid optimizations) - */ - CLC - JCC flush - MOVL AX,AX -flush: - -/* - * set all segs - */ -/* MOVW $SELECTOR(1, SELGDT, 0),AX /**/ - BYTE $0xc7 - BYTE $0xc0 - WORD $SELECTOR(1, SELGDT, 0) - MOVW AX,DS - MOVW AX,SS - MOVW AX,ES - MOVW AX,FS - MOVW AX,GS - -/* JMPFAR SELECTOR(2, SELGDT, 0):$mode32bit(SB) /**/ - BYTE $0x66 - BYTE $0xEA - LONG $mode32bit-KZERO(SB) - WORD $SELECTOR(2, SELGDT, 0) - -TEXT mode32bit(SB),$0 - -#endif BOOT - - /* - * Clear BSS - */ - LEAL edata-KZERO(SB),SI - MOVL SI,DI - ADDL $4,DI - MOVL $0,AX - MOVL AX,(SI) - LEAL end-KZERO(SB),CX - SUBL DI,CX - SHRL $2,CX - CLD; REP; MOVSL - - /* - * make a bottom level page table page that maps the first - * 16 meg of physical memory - */ - LEAL tpt-KZERO(SB),AX /* get phys addr of temporary page table */ - ADDL $(BY2PG-1),AX /* must be page aligned */ - ANDL $(~(BY2PG-1)),AX /* ... */ - MOVL $(4*1024),CX /* pte's per page */ - MOVL $((((4*1024)-1)<<PGSHIFT)|PTEVALID|PTEKERNEL|PTEWRITE),BX -setpte: - MOVL BX,-4(AX)(CX*4) - SUBL $(1<<PGSHIFT),BX - LOOP setpte - - /* - * make a top level page table page that maps the first - * 16 meg of memory to 0 thru 16meg and to KZERO thru KZERO+16meg - */ - MOVL AX,BX - ADDL $(4*BY2PG),AX - ADDL $(PTEVALID|PTEKERNEL|PTEWRITE),BX - MOVL BX,0(AX) - MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+0)(AX) - ADDL $BY2PG,BX - MOVL BX,4(AX) - MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+4)(AX) - ADDL $BY2PG,BX - MOVL BX,8(AX) - MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+8)(AX) - ADDL $BY2PG,BX - MOVL BX,12(AX) - MOVL BX,((((KZERO>>1)&0x7FFFFFFF)>>(2*PGSHIFT-1-4))+12)(AX) - - /* - * point processor to top level page & turn on paging - */ - MOVL AX,CR3 - MOVL CR0,AX - ORL $0X80000000,AX - ANDL $~(0x8|0x2),AX /* TS=0, MP=0 */ - MOVL AX,CR0 - - /* - * use a jump to an absolute location to get the PC into - * KZERO. - */ - LEAL tokzero(SB),AX - JMP* AX - -TEXT tokzero(SB),$0 - - /* - * stack and mach - */ - MOVL $mach0(SB),SP - MOVL SP,m(SB) - MOVL $0,0(SP) - ADDL $(MACHSIZE-4),SP /* start stack under machine struct */ - MOVL $0, u(SB) - - /* - * clear flags - */ - MOVL $0,AX - PUSHL AX - POPFL - - CALL main(SB) - -loop: - JMP loop - -GLOBL mach0+0(SB), $MACHSIZE -GLOBL u(SB), $4 -GLOBL m(SB), $4 -GLOBL tpt(SB), $(BY2PG*6) - -/* - * gdt to get us to 32-bit/segmented/unpaged mode - */ -TEXT tgdt(SB),$0 - - /* null descriptor */ - LONG $0 - LONG $0 - - /* data segment descriptor for 4 gigabytes (PL 0) */ - LONG $(0xFFFF) - LONG $(SEGG|SEGB|(0xF<<16)|SEGP|SEGPL(0)|SEGDATA|SEGW) - - /* exec segment descriptor for 4 gigabytes (PL 0) */ - LONG $(0xFFFF) - LONG $(SEGG|SEGD|(0xF<<16)|SEGP|SEGPL(0)|SEGEXEC|SEGR) - -/* - * pointer to initial gdt - */ -TEXT tgdtptr(SB),$0 - - WORD $(3*8) - LONG $tgdt-KZERO(SB) - -/* - * input a byte - */ -TEXT inb(SB),$0 - - MOVL p+0(FP),DX - XORL AX,AX - INB - RET - -/* - * output a byte - */ -TEXT outb(SB),$0 - - MOVL p+0(FP),DX - MOVL b+4(FP),AX - OUTB - RET - -/* - * input a string of shorts from a port - */ -TEXT inss(SB),$0 - MOVL p+0(FP),DX - MOVL a+4(FP),DI - MOVL c+8(FP),CX - CLD; REP; OP16; INSL - RET - -/* - * output a string of shorts to a port - */ -TEXT outss(SB),$0 - MOVL p+0(FP),DX - MOVL a+4(FP),SI - MOVL c+8(FP),CX - CLD; REP; OP16; OUTSL - RET - -/* - * test and set - */ -TEXT tas(SB),$0 - MOVL $0xdeadead,AX - MOVL l+0(FP),BX - XCHGL AX,(BX) - RET - -/* - * routines to load/read various system registers - */ -GLOBL idtptr(SB),$6 -TEXT putidt(SB),$0 /* interrupt descriptor table */ - MOVL t+0(FP),AX - MOVL AX,idtptr+2(SB) - MOVL l+4(FP),AX - MOVW AX,idtptr(SB) - MOVL idtptr(SB),IDTR - RET - -GLOBL gdtptr(SB),$6 -TEXT putgdt(SB),$0 /* global descriptor table */ - MOVL t+0(FP),AX - MOVL AX,gdtptr+2(SB) - MOVL l+4(FP),AX - MOVW AX,gdtptr(SB) - MOVL gdtptr(SB),GDTR - RET - -TEXT putcr3(SB),$0 /* top level page table pointer */ - MOVL t+0(FP),AX - MOVL AX,CR3 - RET - -TEXT puttr(SB),$0 /* task register */ - MOVL t+0(FP),AX - MOVW AX,TASK - RET - -TEXT getcr0(SB),$0 /* coprocessor bits */ - MOVL CR0,AX - RET - -TEXT getcr2(SB),$0 /* fault address */ - MOVL CR2,AX - RET - -#define FPOFF\ - WAIT;\ - MOVL CR0,AX;\ - ORL $0x4,AX /* EM=1 */;\ - MOVL AX,CR0 - -#define FPON\ - MOVL CR0,AX;\ - ANDL $~0x4,AX /* EM=0 */;\ - MOVL AX,CR0 - -TEXT fpoff(SB),$0 /* turn off floating point */ - FPOFF - RET - -TEXT fpinit(SB),$0 /* turn on & init the floating point */ - FPON - FINIT - WAIT - PUSHW $0x0330 - FLDCW 0(SP) /* ignore underflow/precision, signal others */ - POPW AX - WAIT - RET - -TEXT fpsave(SB),$0 /* save floating point state and turn off */ - MOVL p+0(FP),AX - WAIT - FSAVE 0(AX) - FPOFF - RET - -TEXT fprestore(SB),$0 /* turn on floating point and restore regs */ - FPON - MOVL p+0(FP),AX - FRSTOR 0(AX) - WAIT - RET - -TEXT fpstatus(SB),$0 /* get floating point status */ - FSTSW AX - RET - -/* - * special traps - */ -TEXT intr0(SB),$0 - PUSHL $0 - PUSHL $0 - JMP intrcommon -TEXT intr1(SB),$0 - PUSHL $0 - PUSHL $1 - JMP intrcommon -TEXT intr2(SB),$0 - PUSHL $0 - PUSHL $2 - JMP intrcommon -TEXT intr3(SB),$0 - PUSHL $0 - PUSHL $3 - JMP intrcommon -TEXT intr4(SB),$0 - PUSHL $0 - PUSHL $4 - JMP intrcommon -TEXT intr5(SB),$0 - PUSHL $0 - PUSHL $5 - JMP intrcommon -TEXT intr6(SB),$0 - PUSHL $0 - PUSHL $6 - JMP intrcommon -TEXT intr7(SB),$0 - PUSHL $0 - PUSHL $7 - JMP intrcommon -TEXT intr8(SB),$0 - PUSHL $8 - JMP intrscommon -TEXT intr9(SB),$0 - PUSHL $0 - PUSHL $9 - JMP intrcommon -TEXT intr10(SB),$0 - PUSHL $10 - JMP intrscommon -TEXT intr11(SB),$0 - PUSHL $11 - JMP intrscommon -TEXT intr12(SB),$0 - PUSHL $12 - JMP intrscommon -TEXT intr13(SB),$0 - PUSHL $13 - JMP intrscommon -TEXT intr14(SB),$0 - PUSHL $14 - JMP intrscommon -TEXT intr15(SB),$0 - PUSHL $0 - PUSHL $15 - JMP intrcommon -TEXT intr16(SB),$0 - PUSHL $0 - PUSHL $16 - JMP intrcommon -TEXT intr24(SB),$0 - PUSHL $0 - PUSHL $24 - JMP intrcommon -TEXT intr25(SB),$0 - PUSHL $0 - PUSHL $25 - JMP intrcommon -TEXT intr26(SB),$0 - PUSHL $0 - PUSHL $26 - JMP intrcommon -TEXT intr27(SB),$0 - PUSHL $0 - PUSHL $27 - JMP intrcommon -TEXT intr28(SB),$0 - PUSHL $0 - PUSHL $28 - JMP intrcommon -TEXT intr29(SB),$0 - PUSHL $0 - PUSHL $29 - JMP intrcommon -TEXT intr30(SB),$0 - PUSHL $0 - PUSHL $30 - JMP intrcommon -TEXT intr31(SB),$0 - PUSHL $0 - PUSHL $31 - JMP intrcommon -TEXT intr32(SB),$0 - PUSHL $0 - PUSHL $16 - JMP intrcommon -TEXT intr33(SB),$0 - PUSHL $0 - PUSHL $33 - JMP intrcommon -TEXT intr34(SB),$0 - PUSHL $0 - PUSHL $34 - JMP intrcommon -TEXT intr35(SB),$0 - PUSHL $0 - PUSHL $35 - JMP intrcommon -TEXT intr36(SB),$0 - PUSHL $0 - PUSHL $36 - JMP intrcommon -TEXT intr37(SB),$0 - PUSHL $0 - PUSHL $37 - JMP intrcommon -TEXT intr38(SB),$0 - PUSHL $0 - PUSHL $38 - JMP intrcommon -TEXT intr39(SB),$0 - PUSHL $0 - PUSHL $39 - JMP intrcommon -TEXT intr64(SB),$0 - PUSHL $0 - PUSHL $64 - JMP intrcommon -TEXT intrbad(SB),$0 - PUSHL $0 - PUSHL $0x1ff - JMP intrcommon - -intrcommon: - PUSHL DS - PUSHL ES - PUSHL FS - PUSHL GS - PUSHAL - MOVL $(KDSEL),AX - MOVW AX,DS - MOVW AX,ES - LEAL 0(SP),AX - PUSHL AX - CALL trap(SB) - POPL AX - POPAL - POPL GS - POPL FS - POPL ES - POPL DS - ADDL $8,SP /* error code and trap type */ - IRETL - -intrscommon: - PUSHL DS - PUSHL ES - PUSHL FS - PUSHL GS - PUSHAL - MOVL $(KDSEL),AX - MOVW AX,DS - MOVW AX,ES - LEAL 0(SP),AX - PUSHL AX - CALL trap(SB) - POPL AX - POPAL - POPL GS - POPL FS - POPL ES - POPL DS - ADDL $8,SP /* error code and trap type */ - IRETL - -/* - * interrupt level is interrupts on or off - */ -TEXT spllo(SB),$0 - PUSHFL - POPL AX - STI - RET - -TEXT splhi(SB),$0 - PUSHFL - POPL AX - CLI - RET - -TEXT splx(SB),$0 - MOVL s+0(FP),AX - PUSHL AX - POPFL - RET - -/* - * do nothing whatsoever till interrupt happens - */ -TEXT idle(SB),$0 - HLT - RET - -/* - * label consists of a stack pointer and a PC - */ -TEXT gotolabel(SB),$0 - MOVL l+0(FP),AX - MOVL 0(AX),SP /* restore sp */ - MOVL 4(AX),AX /* put return pc on the stack */ - MOVL AX,0(SP) - MOVL $1,AX /* return 1 */ - RET - -TEXT setlabel(SB),$0 - MOVL l+0(FP),AX - MOVL SP,0(AX) /* store sp */ - MOVL 0(SP),BX /* store return pc */ - MOVL BX,4(AX) - MOVL $0,AX /* return 0 */ - RET - -/* - * Used to get to the first process. - * Set up an interrupt return frame and IRET to user level. - */ -TEXT touser(SB),$0 - PUSHL $(UDSEL) /* old ss */ - PUSHL $(USTKTOP) /* old sp */ - PUSHFL /* old flags */ - PUSHL $(UESEL) /* old cs */ - PUSHL $(UTZERO+32) /* old pc */ - MOVL $(UDSEL),AX - MOVW AX,DS - MOVW AX,ES - MOVW AX,GS - MOVW AX,FS - IRETL - -/* - * set configuration register - */ -TEXT config(SB),$0 - MOVL l+0(FP),AX - MOVL $0x3F3,DX - OUTB - OUTB - RET diff --git a/src/cmd/8a/lex.c b/src/cmd/8a/lex.c index 078861877..ab4de417a 100644 --- a/src/cmd/8a/lex.c +++ b/src/cmd/8a/lex.c @@ -29,9 +29,10 @@ // THE SOFTWARE. #define EXTERN +#include <u.h> +#include <libc.h> #include "a.h" #include "y.tab.h" -#include <ctype.h> enum { diff --git a/src/cmd/8c/cgen.c b/src/cmd/8c/cgen.c index edb29ad8c..7f02bd96e 100644 --- a/src/cmd/8c/cgen.c +++ b/src/cmd/8c/cgen.c @@ -1581,7 +1581,7 @@ sugen(Node *n, Node *nn, int32 w) nod0.addable = 0; nod0.right = l; - /* prtree(&nod0, "hand craft"); /* */ + // prtree(&nod0, "hand craft"); cgen(&nod0, Z); } break; diff --git a/src/cmd/8c/gc.h b/src/cmd/8c/gc.h index 9fead60e4..32b80e995 100644 --- a/src/cmd/8c/gc.h +++ b/src/cmd/8c/gc.h @@ -28,6 +28,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include <u.h> #include "../cc/cc.h" #include "../8l/8.out.h" @@ -400,6 +401,7 @@ void shiftit(Type*, Node*, Node*); #pragma varargck type "A" int #pragma varargck type "B" Bits #pragma varargck type "D" Adr* +#pragma varargck type "lD" Adr* #pragma varargck type "P" Prog* #pragma varargck type "R" int #pragma varargck type "S" char* diff --git a/src/cmd/8c/swt.c b/src/cmd/8c/swt.c index d07a5439c..769ef2c66 100644 --- a/src/cmd/8c/swt.c +++ b/src/cmd/8c/swt.c @@ -237,10 +237,10 @@ outcode(void) Bprint(&b, "\n"); Bprint(&b, "$$ // exports\n\n"); Bprint(&b, "$$ // local types\n\n"); - Bprint(&b, "$$ // dynimport\n", thestring); + Bprint(&b, "$$ // dynimport\n"); for(i=0; i<ndynimp; i++) Bprint(&b, "dynimport %s %s %s\n", dynimp[i].local, dynimp[i].remote, dynimp[i].path); - Bprint(&b, "\n$$ // dynexport\n", thestring); + Bprint(&b, "\n$$ // dynexport\n"); for(i=0; i<ndynexp; i++) Bprint(&b, "dynexport %s %s\n", dynexp[i].local, dynexp[i].remote); Bprint(&b, "\n$$\n\n"); diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c index 25adb38c0..6db0474c9 100644 --- a/src/cmd/8g/ggen.c +++ b/src/cmd/8g/ggen.c @@ -20,6 +20,32 @@ defframe(Prog *ptxt) maxstksize = 0; } +// Sweep the prog list to mark any used nodes. +void +markautoused(Prog* p) +{ + for (; p; p = p->link) { + if (p->from.type == D_AUTO && p->from.node) + p->from.node->used++; + + if (p->to.type == D_AUTO && p->to.node) + p->to.node->used++; + } +} + +// Fixup instructions after compactframe has moved all autos around. +void +fixautoused(Prog* p) +{ + for (; p; p = p->link) { + if (p->from.type == D_AUTO && p->from.node) + p->from.offset += p->from.node->stkdelta; + + if (p->to.type == D_AUTO && p->to.node) + p->to.offset += p->to.node->stkdelta; + } +} + void clearfat(Node *nl) { @@ -141,15 +167,15 @@ cgen_callinter(Node *n, Node *res, int proc) nodindreg(&nodsp, types[tptr], D_SP); nodo.xoffset += widthptr; - cgen(&nodo, &nodsp); // 0(SP) = 8(REG) -- i.s + cgen(&nodo, &nodsp); // 0(SP) = 4(REG) -- i.data nodo.xoffset -= widthptr; - cgen(&nodo, &nodr); // REG = 0(REG) -- i.m + cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab if(n->left->xoffset == BADWIDTH) fatal("cgen_callinter: badwidth"); nodo.xoffset = n->left->xoffset + 3*widthptr + 8; - cgen(&nodo, &nodr); // REG = 32+offset(REG) -- i.m->fun[f] + cgen(&nodo, &nodr); // REG = 20+offset(REG) -- i.tab->fun[f] // BOTCH nodr.type = fntype; nodr.type = n->left->type; diff --git a/src/cmd/8g/gobj.c b/src/cmd/8g/gobj.c index bc1dfe8bf..31c42a3f2 100644 --- a/src/cmd/8g/gobj.c +++ b/src/cmd/8g/gobj.c @@ -226,6 +226,8 @@ dumpfuncs(void) // fix up pc pcloc = 0; for(pl=plist; pl!=nil; pl=pl->link) { + if(isblank(pl->name)) + continue; for(p=pl->firstpc; p!=P; p=p->link) { p->loc = pcloc; if(p->as != ADATA && p->as != AGLOBL) @@ -235,6 +237,8 @@ dumpfuncs(void) // put out functions for(pl=plist; pl!=nil; pl=pl->link) { + if(isblank(pl->name)) + continue; if(debug['S']) { s = S; diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c index a8d65cf22..a35c81eb1 100644 --- a/src/cmd/8g/gsubr.c +++ b/src/cmd/8g/gsubr.c @@ -100,6 +100,19 @@ patch(Prog *p, Prog *to) p->to.offset = to->loc; } +Prog* +unpatch(Prog *p) +{ + Prog *q; + + if(p->to.type != D_BRANCH) + fatal("unpatch: not a branch"); + q = p->to.branch; + p->to.branch = P; + p->to.offset = 0; + return q; +} + /* * start a new Prog list. */ diff --git a/src/cmd/8g/reg.c b/src/cmd/8g/reg.c index a2f3def37..a4828c3a3 100644 --- a/src/cmd/8g/reg.c +++ b/src/cmd/8g/reg.c @@ -1533,6 +1533,7 @@ noreturn(Prog *p) symlist[1] = pkglookup("panicslice", runtimepkg); symlist[2] = pkglookup("throwinit", runtimepkg); symlist[3] = pkglookup("panic", runtimepkg); + symlist[4] = pkglookup("panicwrap", runtimepkg); } s = p->to.sym; diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c index cb900d28d..e1ccfb8a3 100644 --- a/src/cmd/8l/asm.c +++ b/src/cmd/8l/asm.c @@ -83,9 +83,6 @@ enum { ElfStrText, ElfStrData, ElfStrBss, - ElfStrGosymcounts, - ElfStrGosymtab, - ElfStrGopclntab, ElfStrShstrtab, ElfStrSymtab, ElfStrStrtab, @@ -531,10 +528,9 @@ doelf(void) elfstr[ElfStrBss] = addstring(shstrtab, ".bss"); addstring(shstrtab, ".elfdata"); addstring(shstrtab, ".rodata"); + addstring(shstrtab, ".gosymtab"); + addstring(shstrtab, ".gopclntab"); if(!debug['s']) { - elfstr[ElfStrGosymcounts] = addstring(shstrtab, ".gosymcounts"); - elfstr[ElfStrGosymtab] = addstring(shstrtab, ".gosymtab"); - elfstr[ElfStrGopclntab] = addstring(shstrtab, ".gopclntab"); elfstr[ElfStrSymtab] = addstring(shstrtab, ".symtab"); elfstr[ElfStrStrtab] = addstring(shstrtab, ".strtab"); dwarfaddshstrings(shstrtab); @@ -679,10 +675,11 @@ asmb(void) seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); codeblk(sect->vaddr, sect->len); - /* output read-only data in text segment */ - sect = segtext.sect->next; - seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); - datblk(sect->vaddr, sect->len); + /* output read-only data in text segment (rodata, gosymtab and pclntab) */ + for(sect = sect->next; sect != nil; sect = sect->next) { + seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); + datblk(sect->vaddr, sect->len); + } if(debug['v']) Bprint(&bso, "%5.2f datblk\n", cputime()); @@ -920,10 +917,6 @@ asmb(void) break; Elfput: - /* elf 386 */ - if(HEADTYPE == Htiny) - debug['d'] = 1; - eh = getElfEhdr(); startva = INITTEXT - HEADR; @@ -1087,18 +1080,6 @@ asmb(void) elfshbits(sect); if (!debug['s']) { - sh = newElfShdr(elfstr[ElfStrGosymtab]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - shsym(sh, lookup("symtab", 0)); - - sh = newElfShdr(elfstr[ElfStrGopclntab]); - sh->type = SHT_PROGBITS; - sh->flags = SHF_ALLOC; - sh->addralign = 1; - shsym(sh, lookup("pclntab", 0)); - sh = newElfShdr(elfstr[ElfStrSymtab]); sh->type = SHT_SYMTAB; sh->off = symo; @@ -1276,6 +1257,6 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) put(nil, a->asym->name, 'p', a->aoffset, 0, 0, a->gotype); } if(debug['v'] || debug['n']) - Bprint(&bso, "symsize = %uld\n", symsize); + Bprint(&bso, "symsize = %d\n", symsize); Bflush(&bso); } diff --git a/src/cmd/8l/list.c b/src/cmd/8l/list.c index 4e199d767..31ae02346 100644 --- a/src/cmd/8l/list.c +++ b/src/cmd/8l/list.c @@ -176,7 +176,7 @@ Dconv(Fmt *fp) } brk: if(a->index != D_NONE) { - sprint(s, "(%R*%d)", a->index, a->scale); + sprint(s, "(%R*%d)", (int)a->index, a->scale); strcat(str, s); } conv: diff --git a/src/cmd/8l/mkenam b/src/cmd/8l/mkenam index b33fec7cc..992aa3160 100644 --- a/src/cmd/8l/mkenam +++ b/src/cmd/8l/mkenam @@ -28,18 +28,18 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -ed - ../8l/8.out.h <<'!' -v/^ A/d -,s/^ A/ "/ -g/ .*$/s/// -,s/,*$/",/ -1i -char* anames[] = -{ -. -$a -}; -. -w enam.c -Q -! +awk ' +BEGIN { + print "char* anames[] =" + print "{" +} + +/^ A/ { + name=$1 + sub(/,/, "", name) + sub(/^A/, "", name) + print "\t\"" name "\"," +} + +END { print "};" } +' ../8l/8.out.h >enam.c diff --git a/src/cmd/8l/obj.c b/src/cmd/8l/obj.c index 440dcb77f..ce7b59518 100644 --- a/src/cmd/8l/obj.c +++ b/src/cmd/8l/obj.c @@ -54,11 +54,9 @@ Header headers[] = { "msdosexe", Hmsdosexe, "darwin", Hdarwin, "linux", Hlinux, - "nacl", Hnacl, "freebsd", Hfreebsd, "windows", Hwindows, "windowsgui", Hwindows, - "tiny", Htiny, 0, 0 }; @@ -70,10 +68,8 @@ Header headers[] = { * -Hmsdosexe -Tx -Rx is fake MS-DOS .EXE * -Hdarwin -Tx -Rx is Apple Mach-O * -Hlinux -Tx -Rx is Linux ELF32 - * -Hnacl -Tx -Rx was Google Native Client * -Hfreebsd -Tx -Rx is FreeBSD ELF32 * -Hwindows -Tx -Rx is MS Windows PE32 - * -Htiny -Tx -Rx is tiny (os image) */ void @@ -254,17 +250,6 @@ main(int argc, char *argv[]) if(INITRND == -1) INITRND = PESECTALIGN; break; - case Htiny: - tlsoffset = 0; - elfinit(); - HEADR = ELFRESERVE; - if(INITTEXT == -1) - INITTEXT = 0x100000+HEADR; - if(INITDAT == -1) - INITDAT = 0; - if(INITRND == -1) - INITRND = 4096; - break; } if(INITDAT != 0 && INITRND != 0) print("warning: -D0x%ux is ignored because of -R0x%ux\n", @@ -488,7 +473,6 @@ loop: s = lookup(x, r); if(x != name) free(x); - name = nil; if(debug['S'] && r == 0) sig = 1729; @@ -718,7 +702,6 @@ loop: lastp = p; goto loop; } - goto loop; eof: diag("truncated object file: %s", pn); diff --git a/src/cmd/8l/pass.c b/src/cmd/8l/pass.c index 72ae043d6..2e0990c5a 100644 --- a/src/cmd/8l/pass.c +++ b/src/cmd/8l/pass.c @@ -414,7 +414,6 @@ dostkoff(void) autoffset = 0; q = P; - q1 = P; if(pmorestack != P) if(!(p->from.scale & NOSPLIT)) { p = appendp(p); // load g into CX diff --git a/src/cmd/8l/span.c b/src/cmd/8l/span.c index 66a843b23..a4cba1257 100644 --- a/src/cmd/8l/span.c +++ b/src/cmd/8l/span.c @@ -89,7 +89,7 @@ span1(Sym *s) *bp++ = v; *bp++ = v>>8; *bp++ = v>>16; - *bp++ = v>>24; + *bp = v>>24; } } p->comefrom = P; @@ -1319,7 +1319,7 @@ asmins(Prog *p) andptr = and; doasm(p); if(andptr > and+sizeof and) { - print("and[] is too short - %d byte instruction\n", andptr - and); + print("and[] is too short - %ld byte instruction\n", andptr - and); errorexit(); } } diff --git a/src/cmd/cc/acid.c b/src/cmd/cc/acid.c index c6a6722bd..23147e519 100644 --- a/src/cmd/cc/acid.c +++ b/src/cmd/cc/acid.c @@ -28,6 +28,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include <u.h> #include "cc.h" static char *kwd[] = diff --git a/src/cmd/cc/bits.c b/src/cmd/cc/bits.c index aef4449e8..4496d65e7 100644 --- a/src/cmd/cc/bits.c +++ b/src/cmd/cc/bits.c @@ -28,6 +28,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include <u.h> #include "cc.h" Bits diff --git a/src/cmd/cc/cc.h b/src/cmd/cc/cc.h index 8e8f6af44..a38e658ce 100644 --- a/src/cmd/cc/cc.h +++ b/src/cmd/cc/cc.h @@ -28,10 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include <u.h> #include <libc.h> #include <bio.h> -#include <ctype.h> #pragma lib "../cc/cc.a$O" @@ -816,7 +814,9 @@ int machcap(Node*); #pragma varargck type "L" int32 #pragma varargck type "Q" int32 #pragma varargck type "O" int +#pragma varargck type "O" uint #pragma varargck type "T" Type* +#pragma varargck type "U" char* #pragma varargck type "|" int enum diff --git a/src/cmd/cc/cc.y b/src/cmd/cc/cc.y index 470fdae26..515a80372 100644 --- a/src/cmd/cc/cc.y +++ b/src/cmd/cc/cc.y @@ -29,6 +29,7 @@ // THE SOFTWARE. %{ +#include <u.h> #include <stdio.h> /* if we don't, bison will, and cc.h re-#defines getc */ #include "cc.h" %} diff --git a/src/cmd/cc/com.c b/src/cmd/cc/com.c index b1a8a4704..6e470ee64 100644 --- a/src/cmd/cc/com.c +++ b/src/cmd/cc/com.c @@ -28,6 +28,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include <u.h> #include "cc.h" int compar(Node*, int); @@ -127,7 +128,7 @@ tcomo(Node *n, int f) case ORETURN: if(l == Z) { if(n->type->etype != TVOID) - warn(n, "null return of a typed function"); + diag(n, "null return of a typed function"); break; } if(tcom(l)) diff --git a/src/cmd/cc/com64.c b/src/cmd/cc/com64.c index 8d6e07d1b..fb7a3f750 100644 --- a/src/cmd/cc/com64.c +++ b/src/cmd/cc/com64.c @@ -28,6 +28,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include <u.h> #include "cc.h" /* diff --git a/src/cmd/cc/dcl.c b/src/cmd/cc/dcl.c index d7604b649..d624bf247 100644 --- a/src/cmd/cc/dcl.c +++ b/src/cmd/cc/dcl.c @@ -28,6 +28,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include <u.h> #include "cc.h" Node* @@ -1378,7 +1379,6 @@ tmerge(Type *t1, Sym *s) Type *ta, *tb, *t2; t2 = s->type; -/*print("merge %T; %T\n", t1, t2);/**/ for(;;) { if(t1 == T || t2 == T || t1 == t2) break; diff --git a/src/cmd/cc/dpchk.c b/src/cmd/cc/dpchk.c index 0e51101f1..42c245b56 100644 --- a/src/cmd/cc/dpchk.c +++ b/src/cmd/cc/dpchk.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include <u.h> +#include <ctype.h> #include "cc.h" #include "y.tab.h" @@ -56,7 +58,9 @@ struct Tname { char* name; int param; + int count; Tname* link; + Tprot* prot; }; static Type* indchar; @@ -131,8 +135,8 @@ getflag(char *s) return flag; } -void -newprot(Sym *m, Type *t, char *s) +static void +newprot(Sym *m, Type *t, char *s, Tprot **prot) { Bits flag; Tprot *l; @@ -142,32 +146,37 @@ newprot(Sym *m, Type *t, char *s) return; } flag = getflag(s); - for(l=tprot; l; l=l->link) + for(l=*prot; l; l=l->link) if(beq(flag, l->flag) && sametype(t, l->type)) return; l = alloc(sizeof(*l)); l->type = t; l->flag = flag; - l->link = tprot; - tprot = l; + l->link = *prot; + *prot = l; } -void -newname(char *s, int p) +static Tname* +newname(char *s, int p, int count) { Tname *l; for(l=tname; l; l=l->link) if(strcmp(l->name, s) == 0) { - if(l->param != p) + if(p >= 0 && l->param != p) yyerror("vargck %s already defined\n", s); - return; + return l; } + if(p < 0) + return nil; + l = alloc(sizeof(*l)); l->name = s; l->param = p; l->link = tname; + l->count = count; tname = l; + return l; } void @@ -234,6 +243,7 @@ pragvararg(void) int n, c; char *t; Type *ty; + Tname *l; if(!debug['F']) goto out; @@ -244,6 +254,8 @@ pragvararg(void) goto cktype; if(s && strcmp(s->name, "flag") == 0) goto ckflag; + if(s && strcmp(s->name, "countpos") == 0) + goto ckcount; yyerror("syntax in #pragma varargck"); goto out; @@ -255,7 +267,18 @@ ckpos: n = getnsn(); if(n < 0) goto bad; - newname(s->name, n); + newname(s->name, n, 0); + goto out; + +ckcount: +/*#pragma varargck countpos name 2*/ + s = getsym(); + if(s == S) + goto bad; + n = getnsn(); + if(n < 0) + goto bad; + newname(s->name, 0, n); goto out; ckflag: @@ -276,6 +299,25 @@ ckflag: goto out; cktype: + c = getnsc(); + unget(c); + if(c != '"') { +/*#pragma varargck type name int*/ + s = getsym(); + if(s == S) + goto bad; + l = newname(s->name, -1, -1); + s = getsym(); + if(s == S) + goto bad; + ty = s->type; + while((c = getnsc()) == '*') + ty = typ(TIND, ty); + unget(c); + newprot(s, ty, "a", &l->prot); + goto out; + } + /*#pragma varargck type O int*/ t = getquoted(); if(t == nil) @@ -287,7 +329,7 @@ cktype: while((c = getnsc()) == '*') ty = typ(TIND, ty); unget(c); - newprot(s, ty, t); + newprot(s, ty, t, &tprot); goto out; bad: @@ -384,7 +426,8 @@ dpcheck(Node *n) char *s; Node *a, *b; Tname *l; - int i; + Tprot *tl; + int i, j; if(n == Z) return; @@ -398,20 +441,76 @@ dpcheck(Node *n) if(l == 0) return; + if(l->count > 0) { + // fetch count, then check remaining length + i = l->count; + a = nil; + b = n->right; + while(i > 0) { + b = nextarg(b, &a); + i--; + } + if(a == Z) { + diag(n, "can't find count arg"); + return; + } + if(a->op != OCONST || !typechl[a->type->etype]) { + diag(n, "count is invalid constant"); + return; + } + j = a->vconst; + i = 0; + while(b != Z) { + b = nextarg(b, &a); + i++; + } + if(i != j) + diag(n, "found %d argument%s after count %d", i, i == 1 ? "" : "s", j); + } + + if(l->prot != nil) { + // check that all arguments after param or count + // are listed in type list. + i = l->count; + if(i == 0) + i = l->param; + if(i == 0) + return; + a = nil; + b = n->right; + while(i > 0) { + b = nextarg(b, &a); + i--; + } + if(a == Z) { + diag(n, "can't find count/param arg"); + return; + } + while(b != Z) { + b = nextarg(b, &a); + for(tl=l->prot; tl; tl=tl->link) + if(sametype(a->type, tl->type)) + break; + if(tl == nil) + diag(a, "invalid type %T in call to %s", a->type, s); + } + } + + if(l->param <= 0) + return; i = l->param; a = nil; b = n->right; - a = Z; while(i > 0) { b = nextarg(b, &a); i--; } if(a == Z) { - warn(n, "cant find format arg"); + diag(n, "can't find format arg"); return; } if(!sametype(indchar, a->type)) { - warn(n, "format arg type %T", a->type); + diag(n, "format arg type %T", a->type); return; } if(a->op != OADDR || a->left->op != ONAME || a->left->sym != symstring) { diff --git a/src/cmd/cc/funct.c b/src/cmd/cc/funct.c index 21d86258f..99477b2b2 100644 --- a/src/cmd/cc/funct.c +++ b/src/cmd/cc/funct.c @@ -28,6 +28,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include <u.h> #include "cc.h" typedef struct Ftab Ftab; diff --git a/src/cmd/cc/godefs.c b/src/cmd/cc/godefs.c index 9503cb2f2..3ba979c8a 100644 --- a/src/cmd/cc/godefs.c +++ b/src/cmd/cc/godefs.c @@ -29,6 +29,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include <u.h> #include "cc.h" static int upper; @@ -238,7 +239,7 @@ printtypename(Type *t) Bprint(&outbuf, "%U", n); break; case TFUNC: - Bprint(&outbuf, "func(", t); + Bprint(&outbuf, "func("); for(t1 = t->down; t1 != T; t1 = t1->down) { if(t1->etype == TVOID) break; diff --git a/src/cmd/cc/lex.c b/src/cmd/cc/lex.c index 71cc89bf0..15f2d374d 100644 --- a/src/cmd/cc/lex.c +++ b/src/cmd/cc/lex.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include <u.h> +#include <ctype.h> #include "cc.h" #include "y.tab.h" @@ -384,7 +386,7 @@ lookup(void) }else *w++ = *r; } - *w++ = '\0'; + *w = '\0'; h = 0; for(p=symb; *p;) { @@ -1524,7 +1526,7 @@ alloc(int32 n) p = malloc(n); if(p == nil) { print("alloc out of mem\n"); - exit(1); + exits("alloc: out of mem"); } memset(p, 0, n); return p; @@ -1538,7 +1540,7 @@ allocn(void *p, int32 n, int32 d) p = realloc(p, n+d); if(p == nil) { print("allocn out of mem\n"); - exit(1); + exits("allocn: out of mem"); } if(d > 0) memset((char*)p+n, 0, d); diff --git a/src/cmd/cc/lexbody b/src/cmd/cc/lexbody index 24f9bdc85..f4cc19c2e 100644 --- a/src/cmd/cc/lexbody +++ b/src/cmd/cc/lexbody @@ -96,7 +96,7 @@ alloc(int32 n) p = malloc(n); if(p == nil) { print("alloc out of mem\n"); - exit(1); + exits("alloc: out of mem"); } memset(p, 0, n); return p; @@ -110,7 +110,7 @@ allocn(void *p, int32 n, int32 d) p = realloc(p, n+d); if(p == nil) { print("allocn out of mem\n"); - exit(1); + exits("allocn: out of mem"); } if(d > 0) memset((char*)p+n, 0, d); @@ -245,7 +245,7 @@ lookup(void) }else *w++ = *r; } - *w++ = '\0'; + *w = '\0'; h = 0; for(p=symb; c = *p; p++) diff --git a/src/cmd/cc/mac.c b/src/cmd/cc/mac.c index c08cd9c97..43ae214d7 100644 --- a/src/cmd/cc/mac.c +++ b/src/cmd/cc/mac.c @@ -28,6 +28,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include <u.h> +#include <ctype.h> #include "cc.h" #include "macbody" diff --git a/src/cmd/cc/macbody b/src/cmd/cc/macbody index ca8a54c0b..ed66361f1 100644 --- a/src/cmd/cc/macbody +++ b/src/cmd/cc/macbody @@ -830,11 +830,11 @@ linehist(char *f, int offset) if(debug['f']) if(f) { if(offset) - print("%4ld: %s (#line %d)\n", lineno, f, offset); + print("%4d: %s (#line %d)\n", lineno, f, offset); else - print("%4ld: %s\n", lineno, f); + print("%4d: %s\n", lineno, f); } else - print("%4ld: <pop>\n", lineno); + print("%4d: <pop>\n", lineno); newflag = 0; h = alloc(sizeof(Hist)); diff --git a/src/cmd/cc/omachcap.c b/src/cmd/cc/omachcap.c index ec5aa86e9..f8fc1d88b 100644 --- a/src/cmd/cc/omachcap.c +++ b/src/cmd/cc/omachcap.c @@ -28,11 +28,13 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include <u.h> #include "cc.h" /* default, like old cc */ int machcap(Node *n) { + USED(n); return 0; } diff --git a/src/cmd/cc/pgen.c b/src/cmd/cc/pgen.c index 5d17cafc9..0e5e8c059 100644 --- a/src/cmd/cc/pgen.c +++ b/src/cmd/cc/pgen.c @@ -112,7 +112,7 @@ codgen(Node *n, Node *nn) warnreach = 1; gen(n); if(canreach && thisfn->link->etype != TVOID) - warn(Z, "no return at end of function: %s", n1->sym->name); + diag(Z, "no return at end of function: %s", n1->sym->name); noretval(3); gbranch(ORETURN); diff --git a/src/cmd/cc/scon.c b/src/cmd/cc/scon.c index 3047ca44f..193331f77 100644 --- a/src/cmd/cc/scon.c +++ b/src/cmd/cc/scon.c @@ -28,6 +28,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include <u.h> #include "cc.h" static Node* diff --git a/src/cmd/cc/sub.c b/src/cmd/cc/sub.c index e0d5df719..e5992e213 100644 --- a/src/cmd/cc/sub.c +++ b/src/cmd/cc/sub.c @@ -28,6 +28,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +#include <u.h> #include "cc.h" Node* diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index e4e56d8dd..a4d83f1e7 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -66,7 +66,7 @@ func cname(s string) string { // preamble. Multiple occurrences are concatenated with a separating space, // even across files. func (p *Package) ParseFlags(f *File, srcfile string) { - linesIn := strings.Split(f.Preamble, "\n", -1) + linesIn := strings.Split(f.Preamble, "\n") linesOut := make([]string, 0, len(linesIn)) NextLine: @@ -78,7 +78,7 @@ NextLine: } l = strings.TrimSpace(l[4:]) - fields := strings.Split(l, ":", 2) + fields := strings.SplitN(l, ":", 2) if len(fields) != 2 { fatalf("%s: bad #cgo line: %s", srcfile, line) } @@ -230,9 +230,9 @@ func splitQuoted(s string) (r []string, err os.Error) { args = append(args, string(arg[:i])) } if quote != 0 { - err = os.ErrorString("unclosed quote") + err = os.NewError("unclosed quote") } else if escaped { - err = os.ErrorString("unfinished escaping") + err = os.NewError("unfinished escaping") } return args, err } @@ -275,7 +275,7 @@ func (p *Package) loadDefines(f *File) { b.WriteString(f.Preamble) stdout := p.gccDefines(b.Bytes()) - for _, line := range strings.Split(stdout, "\n", -1) { + for _, line := range strings.Split(stdout, "\n") { if len(line) < 9 || line[0:7] != "#define" { continue } @@ -397,7 +397,7 @@ func (p *Package) guessKinds(f *File) []*Name { isConst[i] = true // until proven otherwise } - for _, line := range strings.Split(stderr, "\n", -1) { + for _, line := range strings.Split(stderr, "\n") { if len(line) < 9 || line[0:9] != "cgo-test:" { // the user will see any compiler errors when the code is compiled later. continue @@ -697,7 +697,7 @@ func (p *Package) gccMachine() []string { return nil } -const gccTmp = "_obj/_cgo_.o" +var gccTmp = objDir + "_cgo_.o" // gccCmd returns the gcc command line to use for compiling // the input. @@ -1188,7 +1188,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { if ss, ok := dwarfToName[s]; ok { s = ss } - s = strings.Join(strings.Split(s, " ", -1), "") // strip spaces + s = strings.Join(strings.Split(s, " "), "") // strip spaces name := c.Ident("_Ctype_" + s) typedef[name.Name] = t.Go t.Go = name diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index 84aeccc21..be9c2bc4f 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -18,6 +18,7 @@ import ( "go/token" "io" "os" + "path/filepath" "reflect" "strings" ) @@ -228,7 +229,7 @@ func main() { } pkg := f.Package if dir := os.Getenv("CGOPKGPATH"); dir != "" { - pkg = dir + "/" + pkg + pkg = filepath.Join(dir, pkg) } p.PackagePath = pkg p.writeOutput(f, input) diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index dbc7bcf69..6802dd1cf 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -14,17 +14,20 @@ import ( "go/printer" "go/token" "os" + "path/filepath" "strings" ) +var objDir = "_obj" + string(filepath.Separator) + // writeDefs creates output files to be compiled by 6g, 6c, and gcc. // (The comments here say 6g and 6c but the code applies to the 8 and 5 tools too.) func (p *Package) writeDefs() { - fgo2 := creat("_obj/_cgo_gotypes.go") - fc := creat("_obj/_cgo_defun.c") - fm := creat("_obj/_cgo_main.c") + fgo2 := creat(objDir + "_cgo_gotypes.go") + fc := creat(objDir + "_cgo_defun.c") + fm := creat(objDir + "_cgo_main.c") - fflg := creat("_obj/_cgo_flags") + fflg := creat(objDir + "_cgo_flags") for k, v := range p.CgoFlags { fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, v) } @@ -145,7 +148,7 @@ func dynimport(obj string) { fatalf("cannot load imported symbols from PE file %s: %v", obj, err) } for _, s := range sym { - ss := strings.Split(s, ":", -1) + ss := strings.Split(s, ":") fmt.Printf("#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1])) } return @@ -285,8 +288,8 @@ func (p *Package) writeOutput(f *File, srcfile string) { base = base[0 : len(base)-3] } base = strings.Map(slashToUnderscore, base) - fgo1 := creat("_obj/" + base + ".cgo1.go") - fgcc := creat("_obj/" + base + ".cgo2.c") + fgo1 := creat(objDir + base + ".cgo1.go") + fgcc := creat(objDir + base + ".cgo2.c") p.GoFiles = append(p.GoFiles, base+".cgo1.go") p.GccFiles = append(p.GccFiles, base+".cgo2.c") @@ -361,7 +364,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { // Write out the various stubs we need to support functions exported // from Go so that they are callable from C. func (p *Package) writeExports(fgo2, fc, fm *os.File) { - fgcc := creat("_obj/_cgo_export.c") + fgcc := creat(objDir + "_cgo_export.c") fgcch := creat("_cgo_export.h") fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\n") diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go index 1ca24103e..e79b0e1bf 100644 --- a/src/cmd/cgo/util.go +++ b/src/cmd/cgo/util.go @@ -103,7 +103,7 @@ func creat(name string) *os.File { } func slashToUnderscore(c int) int { - if c == '/' { + if c == '/' || c == '\\' || c == ':' { c = '_' } return c diff --git a/src/cmd/cov/Makefile b/src/cmd/cov/Makefile index fdeb14636..95dba9c60 100644 --- a/src/cmd/cov/Makefile +++ b/src/cmd/cov/Makefile @@ -17,9 +17,6 @@ OFILES=\ HFILES=\ tree.h\ -LIB=\ - ../../../lib/libmach.a\ - NOINSTALL=1 include ../../Make.ccmd diff --git a/src/cmd/ebnflint/ebnflint.go b/src/cmd/ebnflint/ebnflint.go index cac39179f..0b0443156 100644 --- a/src/cmd/ebnflint/ebnflint.go +++ b/src/cmd/ebnflint/ebnflint.go @@ -35,6 +35,12 @@ var ( ) +func report(err os.Error) { + scanner.PrintError(os.Stderr, err) + os.Exit(1) +} + + func extractEBNF(src []byte) []byte { var buf bytes.Buffer @@ -75,34 +81,35 @@ func extractEBNF(src []byte) []byte { func main() { flag.Parse() - var filename string + var ( + filename string + src []byte + err os.Error + ) switch flag.NArg() { case 0: - filename = "/dev/stdin" + filename = "<stdin>" + src, err = ioutil.ReadAll(os.Stdin) case 1: filename = flag.Arg(0) + src, err = ioutil.ReadFile(filename) default: usage() } - - src, err := ioutil.ReadFile(filename) if err != nil { - scanner.PrintError(os.Stderr, err) - os.Exit(1) + report(err) } - if filepath.Ext(filename) == ".html" { + if filepath.Ext(filename) == ".html" || bytes.Index(src, open) >= 0 { src = extractEBNF(src) } grammar, err := ebnf.Parse(fset, filename, src) if err != nil { - scanner.PrintError(os.Stderr, err) - os.Exit(1) + report(err) } if err = ebnf.Verify(fset, grammar, *start); err != nil { - scanner.PrintError(os.Stderr, err) - os.Exit(1) + report(err) } } diff --git a/src/cmd/gc/builtin.c.boot b/src/cmd/gc/builtin.c.boot index c9bf501d1..95098c8af 100644 --- a/src/cmd/gc/builtin.c.boot +++ b/src/cmd/gc/builtin.c.boot @@ -6,6 +6,7 @@ char *runtimeimport = "func \"\".panicslice ()\n" "func \"\".throwreturn ()\n" "func \"\".throwinit ()\n" + "func \"\".panicwrap (? string, ? string, ? string)\n" "func \"\".panic (? interface { })\n" "func \"\".recover (? *int32) interface { }\n" "func \"\".printbool (? bool)\n" @@ -22,6 +23,7 @@ char *runtimeimport = "func \"\".printsp ()\n" "func \"\".goprintf ()\n" "func \"\".concatstring ()\n" + "func \"\".append ()\n" "func \"\".appendslice (typ *uint8, x any, y []any) any\n" "func \"\".cmpstring (? string, ? string) int\n" "func \"\".slicestring (? string, ? int, ? int) string\n" @@ -81,7 +83,7 @@ char *runtimeimport = "func \"\".selectgo (sel *uint8)\n" "func \"\".block ()\n" "func \"\".makeslice (typ *uint8, nel int64, cap int64) []any\n" - "func \"\".growslice (typ *uint8, old []any, cap int64) []any\n" + "func \"\".growslice (typ *uint8, old []any, n int64) []any\n" "func \"\".sliceslice1 (old []any, lb uint64, width uint64) []any\n" "func \"\".sliceslice (old []any, lb uint64, hb uint64, width uint64) []any\n" "func \"\".slicearray (old *any, nel uint64, lb uint64, hb uint64, width uint64) []any\n" @@ -101,9 +103,9 @@ char *unsafeimport = "package unsafe\n" "import runtime \"runtime\"\n" "type \"\".Pointer uintptr\n" - "func \"\".Offsetof (? any) int\n" - "func \"\".Sizeof (? any) int\n" - "func \"\".Alignof (? any) int\n" + "func \"\".Offsetof (? any) uintptr\n" + "func \"\".Sizeof (? any) uintptr\n" + "func \"\".Alignof (? any) uintptr\n" "func \"\".Typeof (i interface { }) interface { }\n" "func \"\".Reflect (i interface { }) (typ interface { }, addr \"\".Pointer)\n" "func \"\".Unreflect (typ interface { }, addr \"\".Pointer) interface { }\n" diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c index 906dadbc9..7e7b40526 100644 --- a/src/cmd/gc/closure.c +++ b/src/cmd/gc/closure.c @@ -116,12 +116,11 @@ typecheckclosure(Node *func, int top) } } -Node* -walkclosure(Node *func, NodeList **init) +static Node* +makeclosure(Node *func, NodeList **init, int nowrap) { - int narg; - Node *xtype, *v, *addr, *xfunc, *call, *clos; - NodeList *l, *in; + Node *xtype, *v, *addr, *xfunc; + NodeList *l; static int closgen; char *p; @@ -133,7 +132,6 @@ walkclosure(Node *func, NodeList **init) // each closure variable has a corresponding // address parameter. - narg = 0; for(l=func->cvars; l; l=l->next) { v = l->n; if(v->op == 0) @@ -146,7 +144,6 @@ walkclosure(Node *func, NodeList **init) addr->class = PPARAM; addr->addable = 1; addr->ullman = 1; - narg++; v->heapaddr = addr; @@ -154,7 +151,8 @@ walkclosure(Node *func, NodeList **init) } // then a dummy arg where the closure's caller pc sits - xtype->list = list(xtype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR]))); + if (!nowrap) + xtype->list = list(xtype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR]))); // then the function arguments xtype->list = concat(xtype->list, func->list); @@ -176,15 +174,36 @@ walkclosure(Node *func, NodeList **init) typecheck(&xfunc, Etop); closures = list(closures, xfunc); + return xfunc; +} + +Node* +walkclosure(Node *func, NodeList **init) +{ + int narg; + Node *xtype, *xfunc, *call, *clos; + NodeList *l, *in; + + /* + * wrap body in external function + * with extra closure parameters. + */ + + // create the function + xfunc = makeclosure(func, init, 0); + xtype = xfunc->nname->ntype; + // prepare call of sys.closure that turns external func into func literal value. clos = syslook("closure", 1); clos->type = T; clos->ntype = nod(OTFUNC, N, N); in = list1(nod(ODCLFIELD, N, typenod(types[TINT]))); // siz in = list(in, nod(ODCLFIELD, N, xtype)); + narg = 0; for(l=func->cvars; l; l=l->next) { if(l->n->op == 0) continue; + narg++; in = list(in, nod(ODCLFIELD, N, l->n->heapaddr->ntype)); } clos->ntype->list = in; @@ -211,33 +230,18 @@ walkclosure(Node *func, NodeList **init) void walkcallclosure(Node *n, NodeList **init) { - Node *z; - NodeList *ll, *cargs; - - walkexpr(&n->left, init); - cargs = n->left // FUNC runtime.closure - ->list // arguments - ->next // skip first - ->next; // skip second - - n->left = n->left // FUNC runtime.closure - ->list // arguments - ->next // skip first - ->n // AS (to indreg) - ->right; // argument == the generated function - - // New arg list for n. First the closure-args, stolen from - // runtime.closure's 3rd and following, - ll = nil; - for (; cargs; cargs = cargs->next) - ll = list(ll, cargs->n->right); // cargs->n is the OAS(INDREG, arg) - - // then an extra zero, to fill the dummy return pointer slot, - z = nod(OXXX, N, N); - nodconst(z, types[TUINTPTR], 0); - z->typecheck = 1; - ll = list(ll, z); - - // and finally the original parameter list. - n->list = concat(ll, n->list); + if (n->op != OCALLFUNC || n->left->op != OCLOSURE) { + dump("walkcallclosure", n); + fatal("abuse of walkcallclosure"); + } + + // New arg list for n. First the closure-args + // and then the original parameter list. + n->list = concat(n->left->enter, n->list); + n->left = makeclosure(n->left, init, 1)->nname; + dowidth(n->left->type); + n->type = getoutargx(n->left->type); + // for a single valued function, pull the field type out of the struct + if (n->type && n->type->type && !n->type->type->down) + n->type = n->type->type->type; } diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c index 335d056a0..7290f9d3b 100644 --- a/src/cmd/gc/dcl.c +++ b/src/cmd/gc/dcl.c @@ -39,6 +39,7 @@ push(void) Sym *d; d = mal(sizeof(*d)); + d->lastlineno = lineno; d->link = dclstack; dclstack = d; return d; @@ -60,6 +61,7 @@ void popdcl(void) { Sym *d, *s; + int lno; // if(dflag()) // print("revert\n"); @@ -68,7 +70,9 @@ popdcl(void) if(d->name == nil) break; s = pkglookup(d->name, d->pkg); + lno = s->lastlineno; dcopy(s, d); + d->lastlineno = lno; if(dflag()) print("\t%L pop %S %p\n", lineno, s, s->def); } @@ -81,19 +85,12 @@ popdcl(void) void poptodcl(void) { - Sym *d, *s; - - for(d=dclstack; d!=S; d=d->link) { - if(d->name == nil) - break; - s = pkglookup(d->name, d->pkg); - dcopy(s, d); - if(dflag()) - print("\t%L pop %S\n", lineno, s); - } - if(d == S) - fatal("poptodcl: no mark"); - dclstack = d; + // pop the old marker and push a new one + // (cannot reuse the existing one) + // because we use the markers to identify blocks + // for the goto restriction checks. + popdcl(); + markdcl(); } void @@ -1241,10 +1238,7 @@ funccompile(Node *n, int isclosure) stksize = 0; dclcontext = PAUTO; funcdepth = n->funcdepth + 1; - hasgoto = 0; compile(n); - if(hasgoto) - clearstk(); curfn = nil; funcdepth = 0; dclcontext = PEXTERN; diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c index ad7b65b30..cb66921ba 100644 --- a/src/cmd/gc/gen.c +++ b/src/cmd/gc/gen.c @@ -11,6 +11,10 @@ static void cgen_dcl(Node *n); static void cgen_proc(Node *n, int proc); +static void checkgoto(Node*, Node*); + +static Label *labellist; +static Label *lastlabel; Node* sysfunc(char *name) @@ -80,71 +84,122 @@ clearlabels(void) lastlabel = L; } -static void -newlab(int op, Node *nlab, Node *stmt) +static Label* +newlab(Node *n) { - Label *lab; Sym *s; - int32 lno; + Label *lab; + + s = n->left->sym; + if((lab = s->label) == L) { + lab = mal(sizeof(*lab)); + if(lastlabel == nil) + labellist = lab; + else + lastlabel->link = lab; + lastlabel = lab; + lab->sym = s; + s->label = lab; + } - s = nlab->left->sym; - lno = nlab->left->lineno; - - lab = mal(sizeof(*lab)); - if(lastlabel == nil) - labellist = lab; - else - lastlabel->link = lab; - lastlabel = lab; - - lab->lineno = lno; - lab->sym = s; - lab->op = op; - lab->label = pc; - lab->stmt = stmt; - if(op == OLABEL) { - if(s->label != L) { - lineno = lno; - yyerror("label %S already defined at %L", s, s->label->lineno); - } else - s->label = lab; - } + if(n->op == OLABEL) { + if(lab->def != N) + yyerror("label %S already defined at %L", s, lab->def->lineno); + else + lab->def = n; + } else + lab->use = list(lab->use, n); + + return lab; } void checklabels(void) { - Label *l; - Sym *s; - int lno; + Label *lab; + NodeList *l; - lno = lineno; - - // resolve goto using syms - for(l=labellist; l!=L; l=l->link) { - switch(l->op) { - case OGOTO: - s = l->sym; - if(s->label == L) { - lineno = l->lineno; - yyerror("label %S not defined", s); - break; - } - s->label->used = 1; - patch(l->label, s->label->label); - break; + for(lab=labellist; lab!=L; lab=lab->link) { + if(lab->def == N) { + for(l=lab->use; l; l=l->next) + yyerrorl(l->n->lineno, "label %S not defined", lab->sym); + continue; } + if(lab->use == nil && !lab->used) { + yyerrorl(lab->def->lineno, "label %S defined and not used", lab->sym); + continue; + } + if(lab->gotopc != P) + fatal("label %S never resolved", lab->sym); + for(l=lab->use; l; l=l->next) + checkgoto(l->n, lab->def); } - - // diagnose unused labels - for(l=labellist; l!=L; l=l->link) { - if(l->op == OLABEL && !l->used) { - lineno = l->lineno; - yyerror("label %S defined and not used", l->sym); +} + +static void +checkgoto(Node *from, Node *to) +{ + int nf, nt; + Sym *block, *dcl, *fs, *ts; + int lno; + + if(from->sym == to->sym) + return; + + nf = 0; + for(fs=from->sym; fs; fs=fs->link) + nf++; + nt = 0; + for(fs=to->sym; fs; fs=fs->link) + nt++; + fs = from->sym; + for(; nf > nt; nf--) + fs = fs->link; + if(fs != to->sym) { + lno = lineno; + setlineno(from); + + // decide what to complain about. + // prefer to complain about 'into block' over declarations, + // so scan backward to find most recent block or else dcl. + block = S; + dcl = S; + ts = to->sym; + for(; nt > nf; nt--) { + if(ts->pkg == nil) + block = ts; + else + dcl = ts; + ts = ts->link; } + while(ts != fs) { + if(ts->pkg == nil) + block = ts; + else + dcl = ts; + ts = ts->link; + fs = fs->link; + } + + if(block) + yyerror("goto %S jumps into block starting at %L", from->left->sym, block->lastlineno); + else + yyerror("goto %S jumps over declaration of %S at %L", from->left->sym, dcl, dcl->lastlineno); + lineno = lno; } - - lineno = lno; +} + +static Label* +stmtlabel(Node *n) +{ + Label *lab; + + if(n->sym != S) + if((lab = n->sym->label) != L) + if(lab->def != N) + if(lab->def->right == n) + return lab; + return L; } /* @@ -193,11 +248,6 @@ gen(Node *n) break; case OEMPTY: - // insert no-op so that - // L:; for { } - // does not treat L as a label for the loop. - if(lastlabel != L && lastlabel->label == p3) - gused(N); break; case OBLOCK: @@ -205,13 +255,41 @@ gen(Node *n) break; case OLABEL: - newlab(OLABEL, n, n->right); + lab = newlab(n); + + // if there are pending gotos, resolve them all to the current pc. + for(p1=lab->gotopc; p1; p1=p2) { + p2 = unpatch(p1); + patch(p1, pc); + } + lab->gotopc = P; + if(lab->labelpc == P) + lab->labelpc = pc; + + if(n->right) { + switch(n->right->op) { + case OFOR: + case OSWITCH: + case OSELECT: + // so stmtlabel can find the label + n->right->sym = lab->sym; + } + } break; case OGOTO: - hasgoto = 1; - newlab(OGOTO, n, N); - gjmp(P); + // if label is defined, emit jump to it. + // otherwise save list of pending gotos in lab->gotopc. + // the list is linked through the normal jump target field + // to avoid a second list. (the jumps are actually still + // valid code, since they're just going to another goto + // to the same label. we'll unwind it when we learn the pc + // of the label in the OLABEL case above.) + lab = newlab(n); + if(lab->labelpc != P) + gjmp(lab->labelpc); + else + lab->gotopc = gjmp(lab->gotopc); break; case OBREAK: @@ -266,12 +344,10 @@ gen(Node *n) continpc = pc; // define break and continue labels - if((lab = lastlabel) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) { + if((lab = stmtlabel(n)) != L) { lab->breakpc = breakpc; lab->continpc = continpc; - } else - lab = L; - + } gen(n->nincr); // contin: incr patch(p1, pc); // test: bgen(n->ntest, 0, breakpc); // if(!test) goto break @@ -304,10 +380,8 @@ gen(Node *n) breakpc = gjmp(P); // break: goto done // define break label - if((lab = lastlabel) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) + if((lab = stmtlabel(n)) != L) lab->breakpc = breakpc; - else - lab = L; patch(p1, pc); // test: genlist(n->nbody); // switch(test) body @@ -323,10 +397,8 @@ gen(Node *n) breakpc = gjmp(P); // break: goto done // define break label - if((lab = lastlabel) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) + if((lab = stmtlabel(n)) != L) lab->breakpc = breakpc; - else - lab = L; patch(p1, pc); // test: genlist(n->nbody); // select() body diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index c16903e77..8ca086ee0 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -278,6 +278,7 @@ struct Node int32 iota; }; #define N ((Node*)0) +EXTERN int32 walkgen; struct NodeList { @@ -301,6 +302,7 @@ struct Sym uchar flags; uchar sym; // huffman encoding in object file Sym* link; + int32 npkg; // number of imported packages with this name // saved and restored by dcopy Pkg* pkg; @@ -632,21 +634,20 @@ typedef struct Prog Prog; struct Label { - uchar op; // OGOTO/OLABEL uchar used; Sym* sym; - Node* stmt; - Prog* label; // pointer to code + Node* def; + NodeList* use; + Label* link; + + // for use during gen + Prog* gotopc; // pointer to unresolved gotos + Prog* labelpc; // pointer to code Prog* breakpc; // pointer to code Prog* continpc; // pointer to code - Label* link; - int32 lineno; }; #define L ((Label*)0) -EXTERN Label* labellist; -EXTERN Label* lastlabel; - /* * note this is the runtime representation * of the compilers arrays. @@ -691,6 +692,7 @@ EXTERN char* infile; EXTERN char* outfile; EXTERN Biobuf* bout; EXTERN int nerrors; +EXTERN int nsavederrors; EXTERN int nsyntaxerrors; EXTERN int safemode; EXTERN char namebuf[NSYMB]; @@ -776,6 +778,7 @@ EXTERN int32 nhunk; EXTERN int32 thunk; EXTERN int exporting; +EXTERN int erroring; EXTERN int noargnames; EXTERN int funcdepth; @@ -913,8 +916,8 @@ Type* pkgtype(Sym *s); void allocparams(void); void cgen_as(Node *nl, Node *nr); void cgen_callmeth(Node *n, int proc); -void checklabels(void); void clearlabels(void); +void checklabels(void); int dotoffset(Node *n, int *oary, Node **nn); void gen(Node *n); void genlist(NodeList *l); @@ -1132,6 +1135,7 @@ Type* ptrto(Type *t); void* remal(void *p, int32 on, int32 n); Sym* restrictlookup(char *name, Pkg *pkg); Node* safeexpr(Node *n, NodeList **init); +void saveerrors(void); Node* cheapexpr(Node *n, NodeList **init); int32 setlineno(Node *n); void setmaxarg(Type *t); @@ -1195,7 +1199,7 @@ void walkstmt(Node **np); void walkstmtlist(NodeList *l); /* - * arch-specific ggen.c/gsubr.c/gobj.c + * arch-specific ggen.c/gsubr.c/gobj.c/pgen.c */ #define P ((Prog*)0) @@ -1237,6 +1241,7 @@ int dsymptr(Sym *s, int off, Sym *x, int xoff); int duintxx(Sym *s, int off, uint64 v, int wid); void dumpdata(void); void dumpfuncs(void); +void fixautoused(Prog*); void gdata(Node*, Node*, int); void gdatacomplex(Node*, Mpcplx*); void gdatastring(Node*, Strlit*); @@ -1246,15 +1251,15 @@ void ggloblsym(Sym *s, int32 width, int dupok); Prog* gjmp(Prog*); void gused(Node*); int isfat(Type*); +void markautoused(Prog*); Plist* newplist(void); Node* nodarg(Type*, int); void nopout(Prog*); void patch(Prog*, Prog*); +Prog* unpatch(Prog*); void zfile(Biobuf *b, char *p, int n); void zhist(Biobuf *b, int line, vlong offset); void zname(Biobuf *b, Sym *s, int t); void data(void); void text(void); -EXTERN int hasgoto; -void clearstk(void); diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y index 1278c2586..01a4e822f 100644 --- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -66,7 +66,7 @@ static void fixlbrace(int); %type <node> switch_stmt uexpr %type <node> xfndcl typedcl -%type <list> xdcl fnbody fnres switch_body loop_body dcl_name_list +%type <list> xdcl fnbody fnres loop_body dcl_name_list %type <list> new_name_list expr_list keyval_list braced_keyval_list expr_or_type_list xdcl_list %type <list> oexpr_list caseblock_list stmt_list oarg_type_list_ocomma arg_type_list %type <list> interfacedcl_list vardcl vardcl_list structdcl structdcl_list @@ -237,7 +237,11 @@ import_here: import_package: LPACKAGE sym import_safety ';' { - importpkg->name = $2->name; + if(importpkg->name == nil) { + importpkg->name = $2->name; + pkglookup($2->name, nil)->npkg++; + } else if(strcmp(importpkg->name, $2->name) != 0) + yyerror("conflicting names %s and %s for package %Z", importpkg->name, $2->name, importpkg->path); importpkg->direct = 1; if(safemode && !curio.importsafe) @@ -449,7 +453,7 @@ case: // will be converted to OCASE // right will point to next case // done in casebody() - poptodcl(); + markdcl(); $$ = nod(OXCASE, N, N); $$->list = $2; if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) { @@ -468,7 +472,7 @@ case: // will be converted to OCASE // right will point to next case // done in casebody() - poptodcl(); + markdcl(); $$ = nod(OXCASE, N, N); if($2->next == nil) n = nod(OAS, $2->n, $4); @@ -484,7 +488,7 @@ case: // will be converted to OCASE // right will point to next case // done in casebody() - poptodcl(); + markdcl(); $$ = nod(OXCASE, N, N); $$->list = list1(colas($2, list1($4))); } @@ -492,7 +496,7 @@ case: { Node *n; - poptodcl(); + markdcl(); $$ = nod(OXCASE, N, N); if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) { // type switch - declare variable @@ -514,17 +518,6 @@ compound_stmt: popdcl(); } -switch_body: - LBODY - { - markdcl(); - } - caseblock_list '}' - { - $$ = $3; - popdcl(); - } - caseblock: case { @@ -553,6 +546,7 @@ caseblock: yyerror("missing statement after label"); $$ = $1; $$->nbody = $3; + popdcl(); } caseblock_list: @@ -674,11 +668,11 @@ switch_stmt: n = N; typesw = nod(OXXX, typesw, n); } - switch_body + LBODY caseblock_list '}' { $$ = $3; $$->op = OSWITCH; - $$->list = $5; + $$->list = $6; typesw = typesw->left; popdcl(); } @@ -686,15 +680,13 @@ switch_stmt: select_stmt: LSELECT { - markdcl(); typesw = nod(OXXX, typesw, N); } - switch_body + LBODY caseblock_list '}' { $$ = nod(OSELECT, N, N); - $$->list = $3; + $$->list = $4; typesw = typesw->left; - popdcl(); } /* @@ -1474,13 +1466,19 @@ non_dcl_stmt: $$ = $1; $$->nelse = list1($3); } -| labelname ':' stmt +| labelname ':' + { + $1 = nod(OLABEL, $1, N); + $1->sym = dclstack; // context, for goto restrictions + } + stmt { NodeList *l; - l = list1(nod(OLABEL, $1, $3)); - if($3) - l = list(l, $3); + $1->right = $4; + l = list1($1); + if($4) + l = list(l, $4); $$ = liststmt(l); } | LFALL @@ -1507,6 +1505,7 @@ non_dcl_stmt: | LGOTO new_name { $$ = nod(OGOTO, $2, N); + $$->sym = dclstack; // context, for goto restrictions } | LRETURN oexpr_list { @@ -1662,7 +1661,11 @@ hidden_import: Pkg *p; p = mkpkg($3.u.sval); - p->name = $2->name; + if(p->name == nil) { + p->name = $2->name; + pkglookup($2->name, nil)->npkg++; + } else if(strcmp(p->name, $2->name) != 0) + yyerror("conflicting names %s and %s for package %Z", p->name, $2->name, p->path); } | LVAR hidden_pkg_importsym hidden_type ';' { diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index 88acb60af..5c642375a 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -97,7 +97,7 @@ fault(int s) // in the program, don't bother complaining // about the seg fault too; let the user clean up // the code and try again. - if(nerrors > 0) + if(nsavederrors + nerrors > 0) errorexit(); fatal("fault"); } @@ -256,7 +256,10 @@ main(int argc, char *argv[]) for(l=xtop; l; l=l->next) if(l->n->op == ODCLFUNC) { curfn = l->n; + saveerrors(); typechecklist(l->n->nbody, Etop); + if(nerrors != 0) + l->n->nbody = nil; // type errors; do not compile } curfn = nil; @@ -264,7 +267,7 @@ main(int argc, char *argv[]) if(l->n->op == ODCLFUNC) funccompile(l->n, 0); - if(nerrors == 0) + if(nsavederrors+nerrors == 0) fninit(xtop); while(closures) { @@ -278,12 +281,12 @@ main(int argc, char *argv[]) if(l->n->op == ONAME) typecheck(&l->n, Erv); - if(nerrors) + if(nerrors+nsavederrors) errorexit(); dumpobj(); - if(nerrors) + if(nerrors+nsavederrors) errorexit(); flusherrors(); @@ -291,6 +294,13 @@ main(int argc, char *argv[]) return 0; } +void +saveerrors(void) +{ + nsavederrors += nerrors; + nerrors = 0; +} + static int arsize(Biobuf *b, char *name) { diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c index ab6186697..552e405d8 100644 --- a/src/cmd/gc/pgen.c +++ b/src/cmd/gc/pgen.c @@ -30,6 +30,8 @@ compile(Node *fn) if(fn->nbody == nil) return; + saveerrors(); + // set up domain for labels clearlabels(); @@ -53,7 +55,7 @@ compile(Node *fn) hasdefer = 0; walk(curfn); - if(nerrors != 0 || isblank(curfn->nname)) + if(nerrors != 0) goto ret; allocparams(); @@ -67,7 +69,7 @@ compile(Node *fn) setlineno(curfn); nodconst(&nod1, types[TINT32], 0); - ptxt = gins(ATEXT, curfn->nname, &nod1); + ptxt = gins(ATEXT, isblank(curfn->nname) ? N : curfn->nname, &nod1); afunclit(&ptxt->from); ginit(); @@ -111,8 +113,7 @@ compile(Node *fn) } oldstksize = stksize; - if(thechar != '5') - compactframe(ptxt); + compactframe(ptxt); if(0) print("compactframe: %ld to %ld\n", oldstksize, stksize); @@ -142,12 +143,12 @@ cmpstackvar(Node *a, Node *b) } +// TODO(lvd) find out where the PAUTO/OLITERAL nodes come from. static void compactframe(Prog* ptxt) { NodeList *ll; Node* n; - Prog *p; uint32 w; if (stksize == 0) @@ -155,17 +156,10 @@ compactframe(Prog* ptxt) // Mark the PAUTO's unused. for(ll=curfn->dcl; ll != nil; ll=ll->next) - if (ll->n->class == PAUTO && ll->n->op == ONAME) + if (ll->n->class == PAUTO) ll->n->used = 0; - // Sweep the prog list to mark any used nodes. - for (p = ptxt; p; p = p->link) { - if (p->from.type == D_AUTO && p->from.node) - p->from.node->used++; - - if (p->to.type == D_AUTO && p->to.node) - p->to.node->used++; - } + markautoused(ptxt); listsort(&curfn->dcl, cmpstackvar); @@ -191,7 +185,6 @@ compactframe(Prog* ptxt) stksize = 0; for(ll = curfn->dcl; ll != nil; ll=ll->next) { n = ll->n; - // TODO find out where the literal autos come from if (n->class != PAUTO || n->op != ONAME) continue; @@ -205,14 +198,7 @@ compactframe(Prog* ptxt) n->stkdelta = -stksize - n->xoffset; } - // Fixup instructions. - for (p = ptxt; p; p = p->link) { - if (p->from.type == D_AUTO && p->from.node) - p->from.offset += p->from.node->stkdelta; - - if (p->to.type == D_AUTO && p->to.node) - p->to.offset += p->to.node->stkdelta; - } + fixautoused(ptxt); // The debug information needs accurate offsets on the symbols. for(ll = curfn->dcl ;ll != nil; ll=ll->next) { diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go index 00fc720b8..e13c95db9 100644 --- a/src/cmd/gc/runtime.go +++ b/src/cmd/gc/runtime.go @@ -15,6 +15,7 @@ func panicindex() func panicslice() func throwreturn() func throwinit() +func panicwrap(string, string, string) func panic(interface{}) func recover(*int32) interface{} diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index 49797f9df..40b0c4fd1 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -45,10 +45,12 @@ adderr(int line, char *fmt, va_list arg) Fmt f; Error *p; + erroring++; fmtstrinit(&f); fmtprint(&f, "%L: ", line); fmtvprint(&f, fmt, arg); fmtprint(&f, "\n"); + erroring--; if(nerr >= merr) { if(merr == 0) @@ -1122,7 +1124,14 @@ Sconv(Fmt *fp) return 0; } - if(s->pkg != localpkg || longsymnames || (fp->flags & FmtLong)) { + if(s->pkg && s->pkg != localpkg || longsymnames || (fp->flags & FmtLong)) { + // This one is for the user. If the package name + // was used by multiple packages, give the full + // import path to disambiguate. + if(erroring && pkglookup(s->pkg->name, nil)->npkg > 1) { + fmtprint(fp, "\"%Z\".%s", s->pkg->path, s->name); + return 0; + } fmtprint(fp, "%s.%s", s->pkg->name, s->name); return 0; } @@ -3131,8 +3140,9 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) NodeList *l, *args, *in, *out; Type *tpad; int isddd; + Val v; - if(0 && debug['r']) + if(debug['r']) print("genwrapper rcvrtype=%T method=%T newnam=%S\n", rcvr, method, newnam); @@ -3174,17 +3184,38 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) args = list(args, l->n->left); isddd = l->n->left->isddd; } + + // generate nil pointer check for better error + if(isptr[rcvr->etype] && rcvr->type == getthisx(method->type)->type->type) { + // generating wrapper from *T to T. + n = nod(OIF, N, N); + n->ntest = nod(OEQ, this->left, nodnil()); + // these strings are already in the reflect tables, + // so no space cost to use them here. + l = nil; + v.ctype = CTSTR; + v.u.sval = strlit(rcvr->type->sym->pkg->name); // package name + l = list(l, nodlit(v)); + v.u.sval = strlit(rcvr->type->sym->name); // type name + l = list(l, nodlit(v)); + v.u.sval = strlit(method->sym->name); + l = list(l, nodlit(v)); // method name + call = nod(OCALL, syslook("panicwrap", 0), N); + call->list = l; + n->nbody = list1(call); + fn->nbody = list(fn->nbody, n); + } // generate call call = nod(OCALL, adddot(nod(OXDOT, this->left, newname(method->sym))), N); call->list = args; call->isddd = isddd; - fn->nbody = list1(call); if(method->type->outtuple > 0) { n = nod(ORETURN, N, N); - n->list = fn->nbody; - fn->nbody = list1(n); + n->list = list1(call); + call = n; } + fn->nbody = list(fn->nbody, call); if(0 && debug['r']) dumplist("genwrapper body", fn->nbody); diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 04dc1a507..dfe0f30f7 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -2716,6 +2716,11 @@ typecheckdef(Node *n) default: fatal("typecheckdef %O", n->op); + case OGOTO: + case OLABEL: + // not really syms + break; + case OLITERAL: if(n->ntype != N) { typecheck(&n->ntype, Etype); @@ -2772,7 +2777,7 @@ typecheckdef(Node *n) if(n->defn == N) { if(n->etype != 0) // like OPRINTN break; - if(nerrors > 0) { + if(nsavederrors+nerrors > 0) { // Can have undefined variables in x := foo // that make x have an n->ndefn == nil. // If there are other errors anyway, don't diff --git a/src/cmd/gc/unsafe.c b/src/cmd/gc/unsafe.c index 540994ddd..d304077c8 100644 --- a/src/cmd/gc/unsafe.c +++ b/src/cmd/gc/unsafe.c @@ -92,6 +92,6 @@ ret: mpmovecfix(val.u.xval, v); n = nod(OLITERAL, N, N); n->val = val; - n->type = types[TINT]; + n->type = types[TUINTPTR]; return n; } diff --git a/src/cmd/gc/unsafe.go b/src/cmd/gc/unsafe.go index b2a341d39..db27d7425 100644 --- a/src/cmd/gc/unsafe.go +++ b/src/cmd/gc/unsafe.go @@ -10,9 +10,11 @@ package PACKAGE type Pointer uintptr // not really; filled in by compiler -func Offsetof(any) int -func Sizeof(any) int -func Alignof(any) int +// return types here are ignored; see unsafe.c +func Offsetof(any) uintptr +func Sizeof(any) uintptr +func Alignof(any) uintptr + func Typeof(i interface{}) (typ interface{}) func Reflect(i interface{}) (typ interface{}, addr Pointer) func Unreflect(typ interface{}, addr Pointer) (ret interface{}) diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index 65a504bff..c9ca9b3b3 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -243,7 +243,16 @@ walkstmt(Node **np) break; case OPROC: - walkexpr(&n->left, &n->ninit); + switch(n->left->op) { + case OPRINT: + case OPRINTN: + walkexprlist(n->left->list, &n->ninit); + n->left = walkprint(n->left, &n->ninit, 1); + break; + default: + walkexpr(&n->left, &n->ninit); + break; + } break; case ORETURN: @@ -485,9 +494,9 @@ walkexpr(Node **np, NodeList **init) if(n->left->op == OCLOSURE) { walkcallclosure(n, init); t = n->left->type; - } else - walkexpr(&n->left, init); + } + walkexpr(&n->left, init); walkexprlist(n->list, init); ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init); diff --git a/src/cmd/godefs/Makefile b/src/cmd/godefs/Makefile index b5c76fb0f..77cd26c04 100644 --- a/src/cmd/godefs/Makefile +++ b/src/cmd/godefs/Makefile @@ -14,3 +14,6 @@ OFILES=\ HFILES=a.h include ../../Make.ccmd + +test: $(TARG) + ./test.sh diff --git a/src/cmd/godefs/test.sh b/src/cmd/godefs/test.sh new file mode 100755 index 000000000..c035af8f4 --- /dev/null +++ b/src/cmd/godefs/test.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# Copyright 2011 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +eval $(gomake --no-print-directory -f ../../Make.inc go-env) + +TMP="testdata_tmp.go" +TEST="testdata.c" +GOLDEN="testdata_${GOOS}_${GOARCH}.golden" + +case ${GOARCH} in +"amd64") CCARG="-f-m64";; +"386") CCARG="-f-m32";; +*) CCARG="";; +esac + +cleanup() { + rm ${TMP} +} + +error() { + cleanup + echo $1 + exit 1 +} + +if [ ! -e ${GOLDEN} ]; then + echo "skipping - no golden defined for this platform" + exit +fi + +./godefs -g test ${CCARG} ${TEST} > ${TMP} +if [ $? != 0 ]; then + error "Error: Could not run godefs for ${TEST}" +fi + +diff ${TMP} ${GOLDEN} +if [ $? != 0 ]; then + error "FAIL: godefs for ${TEST} did not match ${GOLDEN}" +fi + +cleanup + +echo "PASS" diff --git a/src/cmd/godefs/testdata.c b/src/cmd/godefs/testdata.c new file mode 100644 index 000000000..3f459c41b --- /dev/null +++ b/src/cmd/godefs/testdata.c @@ -0,0 +1,41 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <stdint.h> + +// Issue 432 - enum fields in struct can cause misaligned struct fields +typedef enum { + a +} T1; + +struct T2 { + uint8_t a; + T1 b; + T1 c; + uint16_t d; +}; + +typedef struct T2 T2; +typedef T2 $T2; + +// Issue 1162 - structs with fields named Pad[0-9]+ conflict with field +// names used by godefs for padding +struct T3 { + uint8_t a; + int Pad0; +}; + +typedef struct T3 $T3; + +// Issue 1466 - forward references to types in stabs debug info were +// always treated as enums +struct T4 {}; + +struct T5 { + struct T4 *a; +}; + +typedef struct T5 T5; +typedef struct T4 $T4; +typedef T5 $T5;
\ No newline at end of file diff --git a/src/cmd/godefs/testdata_darwin_386.golden b/src/cmd/godefs/testdata_darwin_386.golden new file mode 100644 index 000000000..d929238b0 --- /dev/null +++ b/src/cmd/godefs/testdata_darwin_386.golden @@ -0,0 +1,31 @@ +// ./godefs -g test -f-m32 testdata.c + +// MACHINE GENERATED - DO NOT EDIT. + +package test + +// Constants + +// Types + +type T2 struct { + A uint8; + Pad_godefs_0 [3]byte; + B uint32; + C uint32; + D uint16; + Pad_godefs_1 [2]byte; +} + +type T3 struct { + A uint8; + Pad_godefs_0 [3]byte; + Pad0 int32; +} + +type T4 struct { +} + +type T5 struct { + A *T4; +} diff --git a/src/cmd/godefs/testdata_darwin_amd64.golden b/src/cmd/godefs/testdata_darwin_amd64.golden new file mode 100644 index 000000000..a694f4a73 --- /dev/null +++ b/src/cmd/godefs/testdata_darwin_amd64.golden @@ -0,0 +1,31 @@ +// ./godefs -g test -f-m64 testdata.c + +// MACHINE GENERATED - DO NOT EDIT. + +package test + +// Constants + +// Types + +type T2 struct { + A uint8; + Pad_godefs_0 [3]byte; + B uint32; + C uint32; + D uint16; + Pad_godefs_1 [2]byte; +} + +type T3 struct { + A uint8; + Pad_godefs_0 [3]byte; + Pad0 int32; +} + +type T4 struct { +} + +type T5 struct { + A *T4; +} diff --git a/src/cmd/godoc/Makefile b/src/cmd/godoc/Makefile index c4e0fd9f9..06a18be70 100644 --- a/src/cmd/godoc/Makefile +++ b/src/cmd/godoc/Makefile @@ -8,11 +8,13 @@ TARG=godoc GOFILES=\ codewalk.go\ dirtrees.go\ + filesystem.go\ format.go\ godoc.go\ index.go\ main.go\ mapping.go\ + parser.go\ snippet.go\ spec.go\ utils.go\ diff --git a/src/cmd/godoc/codewalk.go b/src/cmd/godoc/codewalk.go index 24087eb88..50043e2ab 100644 --- a/src/cmd/godoc/codewalk.go +++ b/src/cmd/godoc/codewalk.go @@ -17,7 +17,6 @@ import ( "fmt" "http" "io" - "io/ioutil" "log" "os" "regexp" @@ -42,7 +41,7 @@ func codewalk(w http.ResponseWriter, r *http.Request) { } // If directory exists, serve list of code walks. - dir, err := os.Lstat(abspath) + dir, err := fs.Lstat(abspath) if err == nil && dir.IsDirectory() { codewalkDir(w, r, relpath, abspath) return @@ -75,7 +74,7 @@ func codewalk(w http.ResponseWriter, r *http.Request) { // A Codewalk represents a single codewalk read from an XML file. type Codewalk struct { - Title string "attr" + Title string `xml:"attr"` File []string Step []*Codestep } @@ -84,9 +83,9 @@ type Codewalk struct { // A Codestep is a single step in a codewalk. type Codestep struct { // Filled in from XML - Src string "attr" - Title string "attr" - XML string "innerxml" + Src string `xml:"attr"` + Title string `xml:"attr"` + XML string `xml:"innerxml"` // Derived from Src; not in XML. Err os.Error @@ -114,8 +113,8 @@ func (st *Codestep) String() string { // loadCodewalk reads a codewalk from the named XML file. -func loadCodewalk(file string) (*Codewalk, os.Error) { - f, err := os.Open(file) +func loadCodewalk(filename string) (*Codewalk, os.Error) { + f, err := fs.Open(filename) if err != nil { return nil, err } @@ -125,7 +124,7 @@ func loadCodewalk(file string) (*Codewalk, os.Error) { p.Entity = xml.HTMLEntity err = p.Unmarshal(cw, nil) if err != nil { - return nil, &os.PathError{"parsing", file, err} + return nil, &os.PathError{"parsing", filename, err} } // Compute file list, evaluate line numbers for addresses. @@ -135,8 +134,8 @@ func loadCodewalk(file string) (*Codewalk, os.Error) { if i < 0 { i = len(st.Src) } - file := st.Src[0:i] - data, err := ioutil.ReadFile(absolutePath(file, *goroot)) + filename := st.Src[0:i] + data, err := fs.ReadFile(absolutePath(filename, *goroot)) if err != nil { st.Err = err continue @@ -158,8 +157,8 @@ func loadCodewalk(file string) (*Codewalk, os.Error) { st.Hi = byteToLine(data, hi-1) } st.Data = data - st.File = file - m[file] = true + st.File = filename + m[filename] = true } // Make list of files @@ -169,7 +168,7 @@ func loadCodewalk(file string) (*Codewalk, os.Error) { cw.File[i] = f i++ } - sort.SortStrings(cw.File) + sort.Strings(cw.File) return cw, nil } @@ -184,7 +183,7 @@ func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string Title string } - dir, err := ioutil.ReadDir(abspath) + dir, err := fs.ReadDir(abspath) if err != nil { log.Print(err) serveError(w, r, relpath, err) @@ -192,14 +191,15 @@ func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string } var v vector.Vector for _, fi := range dir { + name := fi.Name() if fi.IsDirectory() { - v.Push(&elem{fi.Name + "/", ""}) - } else if strings.HasSuffix(fi.Name, ".xml") { - cw, err := loadCodewalk(abspath + "/" + fi.Name) + v.Push(&elem{name + "/", ""}) + } else if strings.HasSuffix(name, ".xml") { + cw, err := loadCodewalk(abspath + "/" + name) if err != nil { continue } - v.Push(&elem{fi.Name[0 : len(fi.Name)-len(".xml")], cw.Title}) + v.Push(&elem{name[0 : len(name)-len(".xml")], cw.Title}) } } @@ -216,7 +216,7 @@ func codewalkDir(w http.ResponseWriter, r *http.Request, relpath, abspath string // the usual godoc HTML wrapper. func codewalkFileprint(w http.ResponseWriter, r *http.Request, f string) { abspath := absolutePath(f, *goroot) - data, err := ioutil.ReadFile(abspath) + data, err := fs.ReadFile(abspath) if err != nil { log.Print(err) serveError(w, r, f, err) diff --git a/src/cmd/godoc/dirtrees.go b/src/cmd/godoc/dirtrees.go index 97737ca5a..e98e93a46 100644 --- a/src/cmd/godoc/dirtrees.go +++ b/src/cmd/godoc/dirtrees.go @@ -11,9 +11,7 @@ import ( "go/doc" "go/parser" "go/token" - "io/ioutil" "log" - "os" "path/filepath" "strings" "unicode" @@ -29,21 +27,24 @@ type Directory struct { } -func isGoFile(f *os.FileInfo) bool { - return f.IsRegular() && - !strings.HasPrefix(f.Name, ".") && // ignore .files - filepath.Ext(f.Name) == ".go" +func isGoFile(fi FileInfo) bool { + name := fi.Name() + return fi.IsRegular() && + len(name) > 0 && name[0] != '.' && // ignore .files + filepath.Ext(name) == ".go" } -func isPkgFile(f *os.FileInfo) bool { - return isGoFile(f) && - !strings.HasSuffix(f.Name, "_test.go") // ignore test files +func isPkgFile(fi FileInfo) bool { + return isGoFile(fi) && + !strings.HasSuffix(fi.Name(), "_test.go") // ignore test files } -func isPkgDir(f *os.FileInfo) bool { - return f.IsDirectory() && len(f.Name) > 0 && f.Name[0] != '_' +func isPkgDir(fi FileInfo) bool { + name := fi.Name() + return fi.IsDirectory() && len(name) > 0 && + name[0] != '_' && name[0] != '.' // ignore _files and .files } @@ -101,12 +102,12 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i return &Directory{depth, path, name, "", nil} } - list, err := ioutil.ReadDir(path) + list, err := fs.ReadDir(path) if err != nil { // newDirTree is called with a path that should be a package // directory; errors here should not happen, but if they do, // we want to know about them - log.Printf("ioutil.ReadDir(%s): %s", path, err) + log.Printf("ReadDir(%s): %s", path, err) } // determine number of subdirectories and if there are package files @@ -123,7 +124,7 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i // though the directory doesn't contain any real package files - was bug) if synopses[0] == "" { // no "optimal" package synopsis yet; continue to collect synopses - file, err := parser.ParseFile(fset, filepath.Join(path, d.Name), nil, + file, err := parser.ParseFile(fset, filepath.Join(path, d.Name()), nil, parser.ParseComments|parser.PackageClauseOnly) if err == nil { hasPkgFiles = true @@ -156,7 +157,8 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i i := 0 for _, d := range list { if isPkgDir(d) { - dd := b.newDirTree(fset, filepath.Join(path, d.Name), d.Name, depth+1) + name := d.Name() + dd := b.newDirTree(fset, filepath.Join(path, name), name, depth+1) if dd != nil { dirs[i] = dd i++ @@ -195,8 +197,8 @@ func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth i // (i.e., in this case the tree may contain directories w/o any package files). // func newDirectory(root string, pathFilter func(string) bool, maxDepth int) *Directory { - // The root could be a symbolic link so use os.Stat not os.Lstat. - d, err := os.Stat(root) + // The root could be a symbolic link so use Stat not Lstat. + d, err := fs.Stat(root) // If we fail here, report detailed error messages; otherwise // is is hard to see why a directory tree was not built. switch { @@ -213,7 +215,7 @@ func newDirectory(root string, pathFilter func(string) bool, maxDepth int) *Dire b := treeBuilder{pathFilter, maxDepth} // the file set provided is only for local parsing, no position // information escapes and thus we don't need to save the set - return b.newDirTree(token.NewFileSet(), root, d.Name, 0) + return b.newDirTree(token.NewFileSet(), root, d.Name(), 0) } @@ -266,8 +268,8 @@ func (dir *Directory) lookupLocal(name string) *Directory { // lookup looks for the *Directory for a given path, relative to dir. func (dir *Directory) lookup(path string) *Directory { - d := strings.Split(dir.Path, string(filepath.Separator), -1) - p := strings.Split(path, string(filepath.Separator), -1) + d := strings.Split(dir.Path, string(filepath.Separator)) + p := strings.Split(path, string(filepath.Separator)) i := 0 for i < len(d) { if i >= len(p) || d[i] != p[i] { diff --git a/src/cmd/godoc/filesystem.go b/src/cmd/godoc/filesystem.go new file mode 100644 index 000000000..bf68378d4 --- /dev/null +++ b/src/cmd/godoc/filesystem.go @@ -0,0 +1,96 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file defines abstract file system access. + +package main + +import ( + "io" + "io/ioutil" + "os" +) + + +// The FileInfo interface provides access to file information. +type FileInfo interface { + Name() string + Size() int64 + IsRegular() bool + IsDirectory() bool +} + + +// The FileSystem interface specifies the methods godoc is using +// to access the file system for which it serves documentation. +type FileSystem interface { + Open(path string) (io.ReadCloser, os.Error) + Lstat(path string) (FileInfo, os.Error) + Stat(path string) (FileInfo, os.Error) + ReadDir(path string) ([]FileInfo, os.Error) + ReadFile(path string) ([]byte, os.Error) +} + + +// ---------------------------------------------------------------------------- +// OS-specific FileSystem implementation + +var OS FileSystem = osFS{} + + +// osFI is the OS-specific implementation of FileInfo. +type osFI struct { + *os.FileInfo +} + + +func (fi osFI) Name() string { + return fi.FileInfo.Name +} + + +func (fi osFI) Size() int64 { + if fi.IsDirectory() { + return 0 + } + return fi.FileInfo.Size +} + + +// osFS is the OS-specific implementation of FileSystem +type osFS struct{} + +func (osFS) Open(path string) (io.ReadCloser, os.Error) { + return os.Open(path) +} + + +func (osFS) Lstat(path string) (FileInfo, os.Error) { + fi, err := os.Lstat(path) + return osFI{fi}, err +} + + +func (osFS) Stat(path string) (FileInfo, os.Error) { + fi, err := os.Stat(path) + return osFI{fi}, err +} + + +func (osFS) ReadDir(path string) ([]FileInfo, os.Error) { + l0, err := ioutil.ReadDir(path) + if err != nil { + return nil, err + } + l1 := make([]FileInfo, len(l0)) + for i, e := range l0 { + l1[i] = osFI{e} + } + return l1, nil +} + + +func (osFS) ReadFile(path string) ([]byte, os.Error) { + return ioutil.ReadFile(path) +} diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go index f97c764f9..20ebd3183 100644 --- a/src/cmd/godoc/godoc.go +++ b/src/cmd/godoc/godoc.go @@ -9,13 +9,12 @@ import ( "flag" "fmt" "go/ast" + "go/build" "go/doc" - "go/parser" "go/printer" "go/token" "http" "io" - "io/ioutil" "log" "os" "path" @@ -71,10 +70,11 @@ var ( maxResults = flag.Int("maxresults", 10000, "maximum number of full text search results shown") // file system mapping - fsMap Mapping // user-defined mapping - fsTree RWValue // *Directory tree of packages, updated with each sync - pathFilter RWValue // filter used when building fsMap directory trees - fsModified RWValue // timestamp of last call to invalidateIndex + fs FileSystem // the underlying file system + fsMap Mapping // user-defined mapping + fsTree RWValue // *Directory tree of packages, updated with each sync + pathFilter RWValue // filter used when building fsMap directory trees + fsModified RWValue // timestamp of last call to invalidateIndex // http handlers fileServer http.Handler // default file server @@ -84,8 +84,16 @@ var ( func initHandlers() { - fsMap.Init(*pkgPath) - fileServer = http.FileServer(*goroot, "") + paths := filepath.SplitList(*pkgPath) + for _, t := range build.Path { + if t.Goroot { + continue + } + paths = append(paths, t.SrcDir()) + } + fsMap.Init(paths) + + fileServer = http.FileServer(http.Dir(*goroot)) cmdHandler = httpHandler{"/cmd/", filepath.Join(*goroot, "src", "cmd"), false} pkgHandler = httpHandler{"/pkg/", filepath.Join(*goroot, "src", "pkg"), true} } @@ -147,13 +155,13 @@ func getPathFilter() func(string) bool { // readDirList reads a file containing a newline-separated list // of directory paths and returns the list of paths. func readDirList(filename string) ([]string, os.Error) { - contents, err := ioutil.ReadFile(filename) + contents, err := fs.ReadFile(filename) if err != nil { return nil, err } // create a sorted list of valid directory names filter := func(path string) bool { - d, e := os.Lstat(path) + d, e := fs.Lstat(path) if e != nil && err == nil { // remember first error and return it from readDirList // so we have at least some information if things go bad @@ -161,7 +169,7 @@ func readDirList(filename string) ([]string, os.Error) { } return e == nil && isPkgDir(d) } - list := canonicalizePaths(strings.Split(string(contents), "\n", -1), filter) + list := canonicalizePaths(strings.Split(string(contents), "\n"), filter) // for each parent path, remove all it's children q // (requirement for binary search to work when filtering) i := 0 @@ -598,7 +606,7 @@ func timeFmt(w io.Writer, format string, x ...interface{}) { // Template formatter for "dir/" format. func dirslashFmt(w io.Writer, format string, x ...interface{}) { - if x[0].(*os.FileInfo).IsDirectory() { + if x[0].(FileInfo).IsDirectory() { w.Write([]byte{'/'}) } } @@ -642,12 +650,12 @@ func readTemplate(name string) *template.Template { if *templateDir != "" { defaultpath := path path = filepath.Join(*templateDir, name) - if _, err := os.Stat(path); err != nil { + if _, err := fs.Stat(path); err != nil { log.Print("readTemplate:", err) path = defaultpath } } - data, err := ioutil.ReadFile(path) + data, err := fs.ReadFile(path) if err != nil { log.Fatalf("ReadFile %s: %v", path, err) } @@ -742,9 +750,9 @@ func extractString(src []byte, rx *regexp.Regexp) (s string) { func serveHTMLDoc(w http.ResponseWriter, r *http.Request, abspath, relpath string) { // get HTML body contents - src, err := ioutil.ReadFile(abspath) + src, err := fs.ReadFile(abspath) if err != nil { - log.Printf("ioutil.ReadFile: %s", err) + log.Printf("ReadFile: %s", err) serveError(w, r, relpath, err) return } @@ -793,9 +801,9 @@ func redirect(w http.ResponseWriter, r *http.Request) (redirected bool) { } func serveTextFile(w http.ResponseWriter, r *http.Request, abspath, relpath, title string) { - src, err := ioutil.ReadFile(abspath) + src, err := fs.ReadFile(abspath) if err != nil { - log.Printf("ioutil.ReadFile: %s", err) + log.Printf("ReadFile: %s", err) serveError(w, r, relpath, err) return } @@ -814,19 +822,13 @@ func serveDirectory(w http.ResponseWriter, r *http.Request, abspath, relpath str return } - list, err := ioutil.ReadDir(abspath) + list, err := fs.ReadDir(abspath) if err != nil { - log.Printf("ioutil.ReadDir: %s", err) + log.Printf("ReadDir: %s", err) serveError(w, r, relpath, err) return } - for _, d := range list { - if d.IsDirectory() { - d.Size = 0 - } - } - contents := applyTemplate(dirlistHTML, "dirlistHTML", list) servePage(w, "Directory "+relpath, "", "", contents) } @@ -864,7 +866,7 @@ func serveFile(w http.ResponseWriter, r *http.Request) { return } - dir, err := os.Lstat(abspath) + dir, err := fs.Lstat(abspath) if err != nil { log.Print(err) serveError(w, r, relpath, err) @@ -942,15 +944,15 @@ type httpHandler struct { // func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInfoMode) PageInfo { // filter function to select the desired .go files - filter := func(d *os.FileInfo) bool { + filter := func(d FileInfo) bool { // If we are looking at cmd documentation, only accept // the special fakePkgFile containing the documentation. - return isPkgFile(d) && (h.isPkg || d.Name == fakePkgFile) + return isPkgFile(d) && (h.isPkg || d.Name() == fakePkgFile) } // get package ASTs fset := token.NewFileSet() - pkgs, err := parser.ParseDir(fset, abspath, filter, parser.ParseComments) + pkgs, err := parseDir(fset, abspath, filter) if err != nil && pkgs == nil { // only report directory read errors, ignore parse errors // (may be able to extract partial package information) diff --git a/src/cmd/godoc/index.go b/src/cmd/godoc/index.go index 5938d0b74..e0c89e794 100644 --- a/src/cmd/godoc/index.go +++ b/src/cmd/godoc/index.go @@ -45,7 +45,6 @@ import ( "go/token" "go/scanner" "index/suffixarray" - "io/ioutil" "os" "path/filepath" "regexp" @@ -624,7 +623,7 @@ func pkgName(filename string) string { // failed (that is, if the file was not added), it returns file == nil. func (x *Indexer) addFile(filename string, goFile bool) (file *token.File, ast *ast.File) { // open file - f, err := os.Open(filename) + f, err := fs.Open(filename) if err != nil { return } @@ -727,12 +726,12 @@ func isWhitelisted(filename string) bool { } -func (x *Indexer) visitFile(dirname string, f *os.FileInfo, fulltextIndex bool) { +func (x *Indexer) visitFile(dirname string, f FileInfo, fulltextIndex bool) { if !f.IsRegular() { return } - filename := filepath.Join(dirname, f.Name) + filename := filepath.Join(dirname, f.Name()) goFile := false switch { @@ -745,7 +744,7 @@ func (x *Indexer) visitFile(dirname string, f *os.FileInfo, fulltextIndex bool) } goFile = true - case !fulltextIndex || !isWhitelisted(f.Name): + case !fulltextIndex || !isWhitelisted(f.Name()): return } @@ -804,7 +803,7 @@ func NewIndex(dirnames <-chan string, fulltextIndex bool) *Index { // index all files in the directories given by dirnames for dirname := range dirnames { - list, err := ioutil.ReadDir(dirname) + list, err := fs.ReadDir(dirname) if err != nil { continue // ignore this directory } @@ -902,7 +901,7 @@ func isIdentifier(s string) bool { // identifier, Lookup returns a LookupResult, and a list of alternative // spellings, if any. If the query syntax is wrong, an error is reported. func (x *Index) Lookup(query string) (match *LookupResult, alt *AltWords, err os.Error) { - ss := strings.Split(query, ".", -1) + ss := strings.Split(query, ".") // check query syntax for _, s := range ss { @@ -955,7 +954,7 @@ func (list positionList) Swap(i, j int) { list[i], list[j] = list[j], list[ // unique returns the list sorted and with duplicate entries removed func unique(list []int) []int { - sort.SortInts(list) + sort.Ints(list) var last int i := 0 for _, x := range list { diff --git a/src/cmd/godoc/main.go b/src/cmd/godoc/main.go index 967ea8727..51fcf8dd0 100644 --- a/src/cmd/godoc/main.go +++ b/src/cmd/godoc/main.go @@ -31,6 +31,7 @@ import ( "flag" "fmt" "go/ast" + "go/build" "http" _ "http/pprof" // to serve /debug/pprof/* "io" @@ -222,6 +223,10 @@ func main() { flag.Usage = usage flag.Parse() + // Determine file system to use. + // TODO(gri) Complete this - for now we only have one. + fs = OS + // Clean goroot: normalize path separator. *goroot = filepath.Clean(*goroot) @@ -328,7 +333,10 @@ func main() { } relpath := path abspath := path - if !filepath.IsAbs(path) { + if t, pkg, err := build.FindTree(path); err == nil { + relpath = pkg + abspath = filepath.Join(t.SrcDir(), pkg) + } else if !filepath.IsAbs(path) { abspath = absolutePath(path, pkgHandler.fsRoot) } else { relpath = relativeURL(path) diff --git a/src/cmd/godoc/mapping.go b/src/cmd/godoc/mapping.go index 6ae9032e4..92614e83e 100644 --- a/src/cmd/godoc/mapping.go +++ b/src/cmd/godoc/mapping.go @@ -9,7 +9,6 @@ package main import ( "fmt" "io" - "os" "path" "path/filepath" "sort" @@ -60,10 +59,10 @@ type mapping struct { } -// Init initializes the Mapping from a list of paths separated by -// filepath.ListSeparator. Empty paths are ignored; relative paths -// are assumed to be relative to the current working directory and -// converted to absolute paths. For each path of the form: +// Init initializes the Mapping from a list of paths. +// Empty paths are ignored; relative paths are assumed to be relative to +// the current working directory and converted to absolute paths. +// For each path of the form: // // dirname/localname // @@ -81,8 +80,8 @@ type mapping struct { // user -> /home/user // public -> /home/build/public // -func (m *Mapping) Init(paths string) { - pathlist := canonicalizePaths(filepath.SplitList(paths), nil) +func (m *Mapping) Init(paths []string) { + pathlist := canonicalizePaths(paths, nil) list := make([]mapping, len(pathlist)) // create mapping list @@ -121,7 +120,7 @@ func (m *Mapping) PrefixList() []string { } // sort the list and remove duplicate entries - sort.SortStrings(list) + sort.Strings(list) i := 0 prev := "" for _, path := range list { @@ -174,7 +173,7 @@ func (m *Mapping) ToAbsolute(spath string) string { continue // no match } abspath := filepath.Join(e.path, tail) - if _, err := os.Stat(abspath); err == nil { + if _, err := fs.Stat(abspath); err == nil { return abspath } } diff --git a/src/cmd/godoc/parser.go b/src/cmd/godoc/parser.go new file mode 100644 index 000000000..423db222d --- /dev/null +++ b/src/cmd/godoc/parser.go @@ -0,0 +1,69 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains support functions for parsing .go files. +// Similar functionality is found in package go/parser but the +// functions here operate using godoc's file system fs instead +// of calling the OS's file operations directly. + +package main + +import ( + "go/ast" + "go/parser" + "go/token" + "os" + "path/filepath" +) + +func parseFiles(fset *token.FileSet, filenames []string) (pkgs map[string]*ast.Package, first os.Error) { + pkgs = make(map[string]*ast.Package) + for _, filename := range filenames { + src, err := fs.ReadFile(filename) + if err != nil { + if first == nil { + first = err + } + continue + } + + file, err := parser.ParseFile(fset, filename, src, parser.ParseComments) + if err != nil { + if first == nil { + first = err + } + continue + } + + name := file.Name.Name + pkg, found := pkgs[name] + if !found { + // TODO(gri) Use NewPackage here; reconsider ParseFiles API. + pkg = &ast.Package{name, nil, nil, make(map[string]*ast.File)} + pkgs[name] = pkg + } + pkg.Files[filename] = file + } + return +} + + +func parseDir(fset *token.FileSet, path string, filter func(FileInfo) bool) (map[string]*ast.Package, os.Error) { + list, err := fs.ReadDir(path) + if err != nil { + return nil, err + } + + filenames := make([]string, len(list)) + i := 0 + for _, d := range list { + if filter == nil || filter(d) { + filenames[i] = filepath.Join(path, d.Name()) + i++ + } + } + filenames = filenames[0:i] + + return parseFiles(fset, filenames) +} diff --git a/src/cmd/godoc/utils.go b/src/cmd/godoc/utils.go index 593b51ce0..e2637ab3d 100644 --- a/src/cmd/godoc/utils.go +++ b/src/cmd/godoc/utils.go @@ -44,6 +44,10 @@ func (v *RWValue) get() (interface{}, int64) { } +// TODO(gri) For now, using os.Getwd() is ok here since the functionality +// based on this code is not invoked for the appengine version, +// but this is fragile. Determine what the right thing to do is, +// here (possibly have some Getwd-equivalent in FileSystem). var cwd, _ = os.Getwd() // ignore errors // canonicalizePaths takes a list of (directory/file) paths and returns @@ -76,7 +80,7 @@ func canonicalizePaths(list []string, filter func(path string) bool) []string { list = list[0:i] // sort the list and remove duplicate entries - sort.SortStrings(list) + sort.Strings(list) i = 0 prev := "" for _, path := range list { @@ -95,6 +99,7 @@ func canonicalizePaths(list []string, filter func(path string) bool) []string { // atomically renames that file to the file named by filename. // func writeFileAtomically(filename string, data []byte) os.Error { + // TODO(gri) this won't work on appengine f, err := ioutil.TempFile(filepath.Split(filename)) if err != nil { return err @@ -155,7 +160,7 @@ func isTextFile(filename string) bool { // the extension is not known; read an initial chunk // of the file and check if it looks like text - f, err := os.Open(filename) + f, err := fs.Open(filename) if err != nil { return false } diff --git a/src/cmd/gofix/Makefile b/src/cmd/gofix/Makefile index c4127d93e..7ce21e8aa 100644 --- a/src/cmd/gofix/Makefile +++ b/src/cmd/gofix/Makefile @@ -6,15 +6,22 @@ include ../../Make.inc TARG=gofix GOFILES=\ + filepath.go\ fix.go\ - netdial.go\ - main.go\ - osopen.go\ httpfinalurl.go\ + httpfs.go\ + httpheaders.go\ httpserver.go\ + main.go\ + netdial.go\ + oserrorstring.go\ + osopen.go\ procattr.go\ reflect.go\ signal.go\ + sorthelpers.go\ + sortslice.go\ + stringssplit.go\ typecheck.go\ include ../../Make.cmd diff --git a/src/cmd/gofix/filepath.go b/src/cmd/gofix/filepath.go new file mode 100644 index 000000000..1d0ad6879 --- /dev/null +++ b/src/cmd/gofix/filepath.go @@ -0,0 +1,53 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "go/ast" +) + +func init() { + register(fix{ + "filepath", + filepathFunc, + `Adapt code from filepath.[List]SeparatorString to string(filepath.[List]Separator). + +http://codereview.appspot.com/4527090 +`, + }) +} + +func filepathFunc(f *ast.File) (fixed bool) { + if !imports(f, "path/filepath") { + return + } + + walk(f, func(n interface{}) { + e, ok := n.(*ast.Expr) + if !ok { + return + } + + var ident string + switch { + case isPkgDot(*e, "filepath", "SeparatorString"): + ident = "filepath.Separator" + case isPkgDot(*e, "filepath", "ListSeparatorString"): + ident = "filepath.ListSeparator" + default: + return + } + + // string(filepath.[List]Separator) + *e = &ast.CallExpr{ + Fun: ast.NewIdent("string"), + Args: []ast.Expr{ast.NewIdent(ident)}, + } + + fixed = true + }) + + return +} diff --git a/src/cmd/gofix/filepath_test.go b/src/cmd/gofix/filepath_test.go new file mode 100644 index 000000000..d170c3ae3 --- /dev/null +++ b/src/cmd/gofix/filepath_test.go @@ -0,0 +1,33 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func init() { + addTestCases(filepathTests) +} + +var filepathTests = []testCase{ + { + Name: "filepath.0", + In: `package main + +import ( + "path/filepath" +) + +var _ = filepath.SeparatorString +var _ = filepath.ListSeparatorString +`, + Out: `package main + +import ( + "path/filepath" +) + +var _ = string(filepath.Separator) +var _ = string(filepath.ListSeparator) +`, + }, +} diff --git a/src/cmd/gofix/httpfinalurl.go b/src/cmd/gofix/httpfinalurl.go index 53642b22f..9e6cbf6bc 100644 --- a/src/cmd/gofix/httpfinalurl.go +++ b/src/cmd/gofix/httpfinalurl.go @@ -13,7 +13,7 @@ var httpFinalURLFix = fix{ httpfinalurl, `Adapt http Get calls to not have a finalURL result parameter. - http://codereview.appspot.com/4535056/ +http://codereview.appspot.com/4535056/ `, } diff --git a/src/cmd/gofix/httpfs.go b/src/cmd/gofix/httpfs.go new file mode 100644 index 000000000..7f2765680 --- /dev/null +++ b/src/cmd/gofix/httpfs.go @@ -0,0 +1,63 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "go/ast" + "go/token" +) + +var httpFileSystemFix = fix{ + "httpfs", + httpfs, + `Adapt http FileServer to take a FileSystem. + +http://codereview.appspot.com/4629047 http FileSystem interface +`, +} + +func init() { + register(httpFileSystemFix) +} + +func httpfs(f *ast.File) bool { + if !imports(f, "http") { + return false + } + + fixed := false + walk(f, func(n interface{}) { + call, ok := n.(*ast.CallExpr) + if !ok || !isPkgDot(call.Fun, "http", "FileServer") { + return + } + if len(call.Args) != 2 { + return + } + dir, prefix := call.Args[0], call.Args[1] + call.Args = []ast.Expr{&ast.CallExpr{ + Fun: &ast.SelectorExpr{ast.NewIdent("http"), ast.NewIdent("Dir")}, + Args: []ast.Expr{dir}, + }} + wrapInStripHandler := true + if prefixLit, ok := prefix.(*ast.BasicLit); ok { + if prefixLit.Kind == token.STRING && (prefixLit.Value == `"/"` || prefixLit.Value == `""`) { + wrapInStripHandler = false + } + } + if wrapInStripHandler { + call.Fun.(*ast.SelectorExpr).Sel = ast.NewIdent("StripPrefix") + call.Args = []ast.Expr{ + prefix, + &ast.CallExpr{ + Fun: &ast.SelectorExpr{ast.NewIdent("http"), ast.NewIdent("FileServer")}, + Args: call.Args, + }, + } + } + fixed = true + }) + return fixed +} diff --git a/src/cmd/gofix/httpfs_test.go b/src/cmd/gofix/httpfs_test.go new file mode 100644 index 000000000..d1804e93b --- /dev/null +++ b/src/cmd/gofix/httpfs_test.go @@ -0,0 +1,47 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func init() { + addTestCases(httpFileSystemTests) +} + +var httpFileSystemTests = []testCase{ + { + Name: "httpfs.0", + In: `package httpfs + +import ( + "http" +) + +func f() { + _ = http.FileServer("/var/www/foo", "/") + _ = http.FileServer("/var/www/foo", "") + _ = http.FileServer("/var/www/foo/bar", "/bar") + s := "/foo" + _ = http.FileServer(s, "/") + prefix := "/p" + _ = http.FileServer(s, prefix) +} +`, + Out: `package httpfs + +import ( + "http" +) + +func f() { + _ = http.FileServer(http.Dir("/var/www/foo")) + _ = http.FileServer(http.Dir("/var/www/foo")) + _ = http.StripPrefix("/bar", http.FileServer(http.Dir("/var/www/foo/bar"))) + s := "/foo" + _ = http.FileServer(http.Dir(s)) + prefix := "/p" + _ = http.StripPrefix(prefix, http.FileServer(http.Dir(s))) +} +`, + }, +} diff --git a/src/cmd/gofix/httpheaders.go b/src/cmd/gofix/httpheaders.go new file mode 100644 index 000000000..8a9080e8e --- /dev/null +++ b/src/cmd/gofix/httpheaders.go @@ -0,0 +1,66 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "go/ast" +) + +var httpHeadersFix = fix{ + "httpheaders", + httpheaders, + `Rename http Referer, UserAgent, Cookie, SetCookie, which are now methods. + +http://codereview.appspot.com/4620049/ +`, +} + +func init() { + register(httpHeadersFix) +} + +func httpheaders(f *ast.File) bool { + if !imports(f, "http") { + return false + } + + called := make(map[ast.Node]bool) + walk(f, func(ni interface{}) { + switch n := ni.(type) { + case *ast.CallExpr: + called[n.Fun] = true + } + }) + + fixed := false + typeof := typecheck(headerTypeConfig, f) + walk(f, func(ni interface{}) { + switch n := ni.(type) { + case *ast.SelectorExpr: + if called[n] { + break + } + if t := typeof[n.X]; t != "*http.Request" && t != "*http.Response" { + break + } + switch n.Sel.Name { + case "Referer", "UserAgent": + n.Sel.Name += "()" + fixed = true + case "Cookie": + n.Sel.Name = "Cookies()" + fixed = true + } + } + }) + return fixed +} + +var headerTypeConfig = &TypeConfig{ + Type: map[string]*Type{ + "*http.Request": &Type{}, + "*http.Response": &Type{}, + }, +} diff --git a/src/cmd/gofix/httpheaders_test.go b/src/cmd/gofix/httpheaders_test.go new file mode 100644 index 000000000..cc82b5893 --- /dev/null +++ b/src/cmd/gofix/httpheaders_test.go @@ -0,0 +1,73 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func init() { + addTestCases(httpHeadersTests) +} + +var httpHeadersTests = []testCase{ + { + Name: "httpheaders.0", + In: `package headertest + +import ( + "http" +) + +type Other struct { + Referer string + UserAgent string + Cookie []*http.Cookie +} + +func f(req *http.Request, res *http.Response, other *Other) { + _ = req.Referer + _ = req.UserAgent + _ = req.Cookie + + _ = res.Cookie + + _ = other.Referer + _ = other.UserAgent + _ = other.Cookie + + _ = req.Referer() + _ = req.UserAgent() + _ = req.Cookies() + _ = res.Cookies() +} +`, + Out: `package headertest + +import ( + "http" +) + +type Other struct { + Referer string + UserAgent string + Cookie []*http.Cookie +} + +func f(req *http.Request, res *http.Response, other *Other) { + _ = req.Referer() + _ = req.UserAgent() + _ = req.Cookies() + + _ = res.Cookies() + + _ = other.Referer + _ = other.UserAgent + _ = other.Cookie + + _ = req.Referer() + _ = req.UserAgent() + _ = req.Cookies() + _ = res.Cookies() +} +`, + }, +} diff --git a/src/cmd/gofix/main.go b/src/cmd/gofix/main.go index 1b091c18a..e7e7013c5 100644 --- a/src/cmd/gofix/main.go +++ b/src/cmd/gofix/main.go @@ -53,7 +53,7 @@ func main() { if *allowedRewrites != "" { allowed = make(map[string]bool) - for _, f := range strings.Split(*allowedRewrites, ",", -1) { + for _, f := range strings.Split(*allowedRewrites, ",") { allowed[f] = true } } @@ -123,7 +123,7 @@ func processFile(filename string, useStdin bool) os.Error { newFile := file fixed := false for _, fix := range fixes { - if allowed != nil && !allowed[fix.desc] { + if allowed != nil && !allowed[fix.name] { continue } if fix.f(newFile) { diff --git a/src/cmd/gofix/oserrorstring.go b/src/cmd/gofix/oserrorstring.go new file mode 100644 index 000000000..5e61ab952 --- /dev/null +++ b/src/cmd/gofix/oserrorstring.go @@ -0,0 +1,75 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "go/ast" +) + +var oserrorstringFix = fix{ + "oserrorstring", + oserrorstring, + `Replace os.ErrorString() conversions with calls to os.NewError(). + +http://codereview.appspot.com/4607052 +`, +} + +func init() { + register(oserrorstringFix) +} + +func oserrorstring(f *ast.File) bool { + if !imports(f, "os") { + return false + } + + fixed := false + walk(f, func(n interface{}) { + // The conversion os.ErrorString(x) looks like a call + // of os.ErrorString with one argument. + if call := callExpr(n, "os", "ErrorString"); call != nil { + // os.ErrorString(args) -> os.NewError(args) + call.Fun.(*ast.SelectorExpr).Sel.Name = "NewError" + // os.ErrorString(args) -> os.NewError(args) + call.Fun.(*ast.SelectorExpr).Sel.Name = "NewError" + fixed = true + return + } + + // Remove os.Error type from variable declarations initialized + // with an os.NewError. + // (An *ast.ValueSpec may also be used in a const declaration + // but those won't be initialized with a call to os.NewError.) + if spec, ok := n.(*ast.ValueSpec); ok && + len(spec.Names) == 1 && + isPkgDot(spec.Type, "os", "Error") && + len(spec.Values) == 1 && + callExpr(spec.Values[0], "os", "NewError") != nil { + // var name os.Error = os.NewError(x) -> + // var name = os.NewError(x) + spec.Type = nil + fixed = true + return + } + + // Other occurrences of os.ErrorString are not fixed + // but they are rare. + + }) + return fixed +} + + +// callExpr returns the call expression if x is a call to pkg.name with one argument; +// otherwise it returns nil. +func callExpr(x interface{}, pkg, name string) *ast.CallExpr { + if call, ok := x.(*ast.CallExpr); ok && + len(call.Args) == 1 && + isPkgDot(call.Fun, pkg, name) { + return call + } + return nil +} diff --git a/src/cmd/gofix/oserrorstring_test.go b/src/cmd/gofix/oserrorstring_test.go new file mode 100644 index 000000000..070d9222b --- /dev/null +++ b/src/cmd/gofix/oserrorstring_test.go @@ -0,0 +1,57 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func init() { + addTestCases(oserrorstringTests) +} + +var oserrorstringTests = []testCase{ + { + Name: "oserrorstring.0", + In: `package main + +import "os" + +var _ = os.ErrorString("foo") +var _ os.Error = os.ErrorString("bar1") +var _ os.Error = os.NewError("bar2") +var _ os.Error = MyError("bal") // don't rewrite this one + +var ( + _ = os.ErrorString("foo") + _ os.Error = os.ErrorString("bar1") + _ os.Error = os.NewError("bar2") + _ os.Error = MyError("bal") // don't rewrite this one +) + +func _() (err os.Error) { + err = os.ErrorString("foo") + return os.ErrorString("foo") +} +`, + Out: `package main + +import "os" + +var _ = os.NewError("foo") +var _ = os.NewError("bar1") +var _ = os.NewError("bar2") +var _ os.Error = MyError("bal") // don't rewrite this one + +var ( + _ = os.NewError("foo") + _ = os.NewError("bar1") + _ = os.NewError("bar2") + _ os.Error = MyError("bal") // don't rewrite this one +) + +func _() (err os.Error) { + err = os.NewError("foo") + return os.NewError("foo") +} +`, + }, +} diff --git a/src/cmd/gofix/osopen.go b/src/cmd/gofix/osopen.go index 8eb5d0655..56147c390 100644 --- a/src/cmd/gofix/osopen.go +++ b/src/cmd/gofix/osopen.go @@ -13,7 +13,7 @@ var osopenFix = fix{ osopen, `Adapt os.Open calls to new, easier API and rename O_CREAT O_CREATE. - http://codereview.appspot.com/4357052 +http://codereview.appspot.com/4357052 `, } diff --git a/src/cmd/gofix/sorthelpers.go b/src/cmd/gofix/sorthelpers.go new file mode 100644 index 000000000..4d0bee6e7 --- /dev/null +++ b/src/cmd/gofix/sorthelpers.go @@ -0,0 +1,47 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "go/ast" +) + +func init() { + register(fix{ + "sorthelpers", + sorthelpers, + `Adapt code from sort.Sort[Ints|Float64s|Strings] to sort.[Ints|Float64s|Strings]. +`, + }) +} + + +func sorthelpers(f *ast.File) (fixed bool) { + if !imports(f, "sort") { + return + } + + walk(f, func(n interface{}) { + s, ok := n.(*ast.SelectorExpr) + if !ok || !isTopName(s.X, "sort") { + return + } + + switch s.Sel.String() { + case "SortFloat64s": + s.Sel.Name = "Float64s" + case "SortInts": + s.Sel.Name = "Ints" + case "SortStrings": + s.Sel.Name = "Strings" + default: + return + } + + fixed = true + }) + + return +} diff --git a/src/cmd/gofix/sorthelpers_test.go b/src/cmd/gofix/sorthelpers_test.go new file mode 100644 index 000000000..6c37858fd --- /dev/null +++ b/src/cmd/gofix/sorthelpers_test.go @@ -0,0 +1,45 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func init() { + addTestCases(sorthelpersTests) +} + +var sorthelpersTests = []testCase{ + { + Name: "sortslice.0", + In: `package main + +import ( + "sort" +) + +func main() { + var s []string + sort.SortStrings(s) + var i []ints + sort.SortInts(i) + var f []float64 + sort.SortFloat64s(f) +} +`, + Out: `package main + +import ( + "sort" +) + +func main() { + var s []string + sort.Strings(s) + var i []ints + sort.Ints(i) + var f []float64 + sort.Float64s(f) +} +`, + }, +} diff --git a/src/cmd/gofix/sortslice.go b/src/cmd/gofix/sortslice.go new file mode 100644 index 000000000..b9c108b5a --- /dev/null +++ b/src/cmd/gofix/sortslice.go @@ -0,0 +1,50 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "go/ast" +) + +func init() { + register(fix{ + "sortslice", + sortslice, + `Adapt code from sort.[Float64|Int|String]Array to sort.[Float64|Int|String]Slice. + +http://codereview.appspot.com/4602054 +http://codereview.appspot.com/4639041 +`, + }) +} + + +func sortslice(f *ast.File) (fixed bool) { + if !imports(f, "sort") { + return + } + + walk(f, func(n interface{}) { + s, ok := n.(*ast.SelectorExpr) + if !ok || !isTopName(s.X, "sort") { + return + } + + switch s.Sel.String() { + case "Float64Array": + s.Sel.Name = "Float64Slice" + case "IntArray": + s.Sel.Name = "IntSlice" + case "StringArray": + s.Sel.Name = "StringSlice" + default: + return + } + + fixed = true + }) + + return +} diff --git a/src/cmd/gofix/sortslice_test.go b/src/cmd/gofix/sortslice_test.go new file mode 100644 index 000000000..404feb26f --- /dev/null +++ b/src/cmd/gofix/sortslice_test.go @@ -0,0 +1,35 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func init() { + addTestCases(sortsliceTests) +} + +var sortsliceTests = []testCase{ + { + Name: "sortslice.0", + In: `package main + +import ( + "sort" +) + +var _ = sort.Float64Array +var _ = sort.IntArray +var _ = sort.StringArray +`, + Out: `package main + +import ( + "sort" +) + +var _ = sort.Float64Slice +var _ = sort.IntSlice +var _ = sort.StringSlice +`, + }, +} diff --git a/src/cmd/gofix/stringssplit.go b/src/cmd/gofix/stringssplit.go new file mode 100644 index 000000000..4a1fe93d3 --- /dev/null +++ b/src/cmd/gofix/stringssplit.go @@ -0,0 +1,71 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "go/ast" + "go/token" +) + +var stringssplitFix = fix{ + "stringssplit", + stringssplit, + `Restore strings.Split to its original meaning and add strings.SplitN. Bytes too. + +http://codereview.appspot.com/4661051 +`, +} + +func init() { + register(stringssplitFix) +} + +func stringssplit(f *ast.File) bool { + if !imports(f, "bytes") && !imports(f, "strings") { + return false + } + + fixed := false + walk(f, func(n interface{}) { + call, ok := n.(*ast.CallExpr) + // func Split(s, sep string, n int) []string + // func SplitAfter(s, sep string, n int) []string + if !ok || len(call.Args) != 3 { + return + } + // Is this our function? + switch { + case isPkgDot(call.Fun, "bytes", "Split"): + case isPkgDot(call.Fun, "bytes", "SplitAfter"): + case isPkgDot(call.Fun, "strings", "Split"): + case isPkgDot(call.Fun, "strings", "SplitAfter"): + default: + return + } + + sel := call.Fun.(*ast.SelectorExpr) + args := call.Args + fixed = true // We're committed. + + // Is the last argument -1? If so, drop the arg. + // (Actually we just look for a negative integer literal.) + // Otherwise, Split->SplitN and keep the arg. + final := args[2] + if unary, ok := final.(*ast.UnaryExpr); ok && unary.Op == token.SUB { + if lit, ok := unary.X.(*ast.BasicLit); ok { + // Is it an integer? If so, it's a negative integer and that's what we're after. + if lit.Kind == token.INT { + // drop the last arg. + call.Args = args[0:2] + return + } + } + } + + // If not, rename and keep the argument list. + sel.Sel.Name += "N" + }) + return fixed +} diff --git a/src/cmd/gofix/stringssplit_test.go b/src/cmd/gofix/stringssplit_test.go new file mode 100644 index 000000000..b925722af --- /dev/null +++ b/src/cmd/gofix/stringssplit_test.go @@ -0,0 +1,51 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func init() { + addTestCases(stringssplitTests) +} + +var stringssplitTests = []testCase{ + { + Name: "stringssplit.0", + In: `package main + +import ( + "bytes" + "strings" +) + +func f() { + bytes.Split(a, b, c) + bytes.Split(a, b, -1) + bytes.SplitAfter(a, b, c) + bytes.SplitAfter(a, b, -1) + strings.Split(a, b, c) + strings.Split(a, b, -1) + strings.SplitAfter(a, b, c) + strings.SplitAfter(a, b, -1) +} +`, + Out: `package main + +import ( + "bytes" + "strings" +) + +func f() { + bytes.SplitN(a, b, c) + bytes.Split(a, b) + bytes.SplitAfterN(a, b, c) + bytes.SplitAfter(a, b) + strings.SplitN(a, b, c) + strings.Split(a, b) + strings.SplitAfterN(a, b, c) + strings.SplitAfter(a, b) +} +`, + }, +} diff --git a/src/cmd/gofix/testdata/reflect.decoder.go.out b/src/cmd/gofix/testdata/reflect.decoder.go.out index 170eedb05..ece88ecbe 100644 --- a/src/cmd/gofix/testdata/reflect.decoder.go.out +++ b/src/cmd/gofix/testdata/reflect.decoder.go.out @@ -44,7 +44,7 @@ func NewDecoder(r io.Reader) *Decoder { func (dec *Decoder) recvType(id typeId) { // Have we already seen this type? That's an error if id < firstUserId || dec.wireType[id] != nil { - dec.err = os.ErrorString("gob: duplicate type received") + dec.err = os.NewError("gob: duplicate type received") return } @@ -143,7 +143,7 @@ func (dec *Decoder) decodeTypeSequence(isInterface bool) typeId { // will be absorbed by recvMessage.) if dec.buf.Len() > 0 { if !isInterface { - dec.err = os.ErrorString("extra data in buffer") + dec.err = os.NewError("extra data in buffer") break } dec.nextUint() @@ -165,7 +165,7 @@ func (dec *Decoder) Decode(e interface{}) os.Error { // If e represents a value as opposed to a pointer, the answer won't // get back to the caller. Make sure it's a pointer. if value.Type().Kind() != reflect.Ptr { - dec.err = os.ErrorString("gob: attempt to decode into a non-pointer") + dec.err = os.NewError("gob: attempt to decode into a non-pointer") return dec.err } return dec.DecodeValue(value) diff --git a/src/cmd/gofix/testdata/reflect.encoder.go.out b/src/cmd/gofix/testdata/reflect.encoder.go.out index 781ef6504..925d39301 100644 --- a/src/cmd/gofix/testdata/reflect.encoder.go.out +++ b/src/cmd/gofix/testdata/reflect.encoder.go.out @@ -50,7 +50,7 @@ func (enc *Encoder) popWriter() { } func (enc *Encoder) badType(rt reflect.Type) { - enc.setError(os.ErrorString("gob: can't encode type " + rt.String())) + enc.setError(os.NewError("gob: can't encode type " + rt.String())) } func (enc *Encoder) setError(err os.Error) { diff --git a/src/cmd/gofix/testdata/reflect.export.go.out b/src/cmd/gofix/testdata/reflect.export.go.out index 486a812e2..460edb40b 100644 --- a/src/cmd/gofix/testdata/reflect.export.go.out +++ b/src/cmd/gofix/testdata/reflect.export.go.out @@ -343,20 +343,20 @@ func (exp *Exporter) Sync(timeout int64) os.Error { func checkChan(chT interface{}, dir Dir) (reflect.Value, os.Error) { chanType := reflect.TypeOf(chT) if chanType.Kind() != reflect.Chan { - return reflect.Value{}, os.ErrorString("not a channel") + return reflect.Value{}, os.NewError("not a channel") } if dir != Send && dir != Recv { - return reflect.Value{}, os.ErrorString("unknown channel direction") + return reflect.Value{}, os.NewError("unknown channel direction") } switch chanType.ChanDir() { case reflect.BothDir: case reflect.SendDir: if dir != Recv { - return reflect.Value{}, os.ErrorString("to import/export with Send, must provide <-chan") + return reflect.Value{}, os.NewError("to import/export with Send, must provide <-chan") } case reflect.RecvDir: if dir != Send { - return reflect.Value{}, os.ErrorString("to import/export with Recv, must provide chan<-") + return reflect.Value{}, os.NewError("to import/export with Recv, must provide chan<-") } } return reflect.ValueOf(chT), nil @@ -376,7 +376,7 @@ func (exp *Exporter) Export(name string, chT interface{}, dir Dir) os.Error { defer exp.mu.Unlock() _, present := exp.names[name] if present { - return os.ErrorString("channel name already being exported:" + name) + return os.NewError("channel name already being exported:" + name) } exp.names[name] = &chanDir{ch, dir} return nil @@ -393,7 +393,7 @@ func (exp *Exporter) Hangup(name string) os.Error { // TODO drop all instances of channel from client sets exp.mu.Unlock() if !ok { - return os.ErrorString("netchan export: hangup: no such channel: " + name) + return os.NewError("netchan export: hangup: no such channel: " + name) } chDir.ch.Close() return nil diff --git a/src/cmd/gofix/testdata/reflect.print.go.out b/src/cmd/gofix/testdata/reflect.print.go.out index 079948cca..10379bd20 100644 --- a/src/cmd/gofix/testdata/reflect.print.go.out +++ b/src/cmd/gofix/testdata/reflect.print.go.out @@ -185,7 +185,7 @@ func Sprintf(format string, a ...interface{}) string { // Errorf formats according to a format specifier and returns the string // converted to an os.ErrorString, which satisfies the os.Error interface. func Errorf(format string, a ...interface{}) os.Error { - return os.ErrorString(Sprintf(format, a...)) + return os.NewError(Sprintf(format, a...)) } // These routines do not take a format string diff --git a/src/cmd/gofix/testdata/reflect.read.go.out b/src/cmd/gofix/testdata/reflect.read.go.out index 554b2a61b..a6b126744 100644 --- a/src/cmd/gofix/testdata/reflect.read.go.out +++ b/src/cmd/gofix/testdata/reflect.read.go.out @@ -244,7 +244,7 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { switch v := val; v.Kind() { default: - return os.ErrorString("unknown type " + v.Type().String()) + return os.NewError("unknown type " + v.Type().String()) case reflect.Slice: typ := v.Type() @@ -483,7 +483,7 @@ Loop: case reflect.Invalid: // Probably a comment, handled below default: - return os.ErrorString("cannot happen: unknown type " + t.Type().String()) + return os.NewError("cannot happen: unknown type " + t.Type().String()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if !getInt64() { return err diff --git a/src/cmd/gofix/testdata/reflect.scan.go.out b/src/cmd/gofix/testdata/reflect.scan.go.out index 42bc52c92..2b07115e9 100644 --- a/src/cmd/gofix/testdata/reflect.scan.go.out +++ b/src/cmd/gofix/testdata/reflect.scan.go.out @@ -167,7 +167,7 @@ type ssave struct { // satisfies io.Reader. It will never be called when used as // intended, so there is no need to make it actually work. func (s *ss) Read(buf []byte) (n int, err os.Error) { - return 0, os.ErrorString("ScanState's Read should not be called. Use ReadRune") + return 0, os.NewError("ScanState's Read should not be called. Use ReadRune") } func (s *ss) ReadRune() (rune int, size int, err os.Error) { @@ -240,7 +240,7 @@ func (s *ss) error(err os.Error) { } func (s *ss) errorString(err string) { - panic(scanError{os.ErrorString(err)}) + panic(scanError{os.NewError(err)}) } func (s *ss) Token(skipSpace bool, f func(int) bool) (tok []byte, err os.Error) { @@ -426,8 +426,8 @@ func (s *ss) typeError(field interface{}, expected string) { s.errorString("expected field of type pointer to " + expected + "; found " + reflect.TypeOf(field).String()) } -var complexError = os.ErrorString("syntax error scanning complex number") -var boolError = os.ErrorString("syntax error scanning boolean") +var complexError = os.NewError("syntax error scanning complex number") +var boolError = os.NewError("syntax error scanning boolean") // consume reads the next rune in the input and reports whether it is in the ok string. // If accept is true, it puts the character into the input token. diff --git a/src/cmd/gofix/testdata/reflect.template.go.in b/src/cmd/gofix/testdata/reflect.template.go.in index ba06de4e3..1f5a8128f 100644 --- a/src/cmd/gofix/testdata/reflect.template.go.in +++ b/src/cmd/gofix/testdata/reflect.template.go.in @@ -444,7 +444,7 @@ func (t *Template) newVariable(words []string) *variableElement { bar := strings.IndexRune(lastWord, '|') if bar >= 0 { words[len(words)-1] = lastWord[0:bar] - formatters = strings.Split(lastWord[bar+1:], "|", -1) + formatters = strings.Split(lastWord[bar+1:], "|") } // We could remember the function address here and avoid the lookup later, @@ -705,7 +705,7 @@ func (t *Template) findVar(st *state, s string) reflect.Value { if s == "@" { return indirectPtr(data, numStars) } - for _, elem := range strings.Split(s, ".", -1) { + for _, elem := range strings.Split(s, ".") { // Look up field; data must be a struct or map. data = t.lookup(st, data, elem) if data == nil { diff --git a/src/cmd/gofix/testdata/reflect.template.go.out b/src/cmd/gofix/testdata/reflect.template.go.out index c36288455..f2f56ef3c 100644 --- a/src/cmd/gofix/testdata/reflect.template.go.out +++ b/src/cmd/gofix/testdata/reflect.template.go.out @@ -444,7 +444,7 @@ func (t *Template) newVariable(words []string) *variableElement { bar := strings.IndexRune(lastWord, '|') if bar >= 0 { words[len(words)-1] = lastWord[0:bar] - formatters = strings.Split(lastWord[bar+1:], "|", -1) + formatters = strings.Split(lastWord[bar+1:], "|") } // We could remember the function address here and avoid the lookup later, @@ -705,7 +705,7 @@ func (t *Template) findVar(st *state, s string) reflect.Value { if s == "@" { return indirectPtr(data, numStars) } - for _, elem := range strings.Split(s, ".", -1) { + for _, elem := range strings.Split(s, ".") { // Look up field; data must be a struct or map. data = t.lookup(st, data, elem) if !data.IsValid() { diff --git a/src/cmd/gofix/testdata/reflect.type.go.out b/src/cmd/gofix/testdata/reflect.type.go.out index a39b074fe..9cd78296d 100644 --- a/src/cmd/gofix/testdata/reflect.type.go.out +++ b/src/cmd/gofix/testdata/reflect.type.go.out @@ -67,7 +67,7 @@ func validUserType(rt reflect.Type) (ut *userTypeInfo, err os.Error) { ut.base = pt.Elem() if ut.base == slowpoke { // ut.base lapped slowpoke // recursive pointer type. - return nil, os.ErrorString("can't represent recursive pointer type " + ut.base.String()) + return nil, os.NewError("can't represent recursive pointer type " + ut.base.String()) } if ut.indir%2 == 0 { slowpoke = slowpoke.Elem() @@ -524,7 +524,7 @@ func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os. return st, nil default: - return nil, os.ErrorString("gob NewTypeObject can't handle type: " + rt.String()) + return nil, os.NewError("gob NewTypeObject can't handle type: " + rt.String()) } return nil, nil } diff --git a/src/cmd/gofmt/gofmt_test.go b/src/cmd/gofmt/gofmt_test.go index a72530307..70700554b 100644 --- a/src/cmd/gofmt/gofmt_test.go +++ b/src/cmd/gofmt/gofmt_test.go @@ -20,8 +20,8 @@ func runTest(t *testing.T, dirname, in, out, flags string) { // process flags *simplifyAST = false *rewriteRule = "" - for _, flag := range strings.Split(flags, " ", -1) { - elts := strings.Split(flag, "=", 2) + for _, flag := range strings.Split(flags, " ") { + elts := strings.SplitN(flag, "=", 2) name := elts[0] value := "" if len(elts) == 2 { diff --git a/src/cmd/gofmt/rewrite.go b/src/cmd/gofmt/rewrite.go index 4c24282f3..f7f1fe824 100644 --- a/src/cmd/gofmt/rewrite.go +++ b/src/cmd/gofmt/rewrite.go @@ -22,7 +22,7 @@ func initRewrite() { rewrite = nil // disable any previous rewrite return } - f := strings.Split(*rewriteRule, "->", -1) + f := strings.Split(*rewriteRule, "->") if len(f) != 2 { fmt.Fprintf(os.Stderr, "rewrite rule must be of the form 'pattern -> replacement'\n") os.Exit(2) diff --git a/src/cmd/goinstall/Makefile b/src/cmd/goinstall/Makefile index 202797cd5..f61354f39 100644 --- a/src/cmd/goinstall/Makefile +++ b/src/cmd/goinstall/Makefile @@ -9,22 +9,5 @@ GOFILES=\ download.go\ main.go\ make.go\ - parse.go\ - path.go\ - syslist.go\ - -CLEANFILES+=syslist.go include ../../Make.cmd - -syslist.go: - echo '// Generated automatically by make.' >$@ - echo 'package main' >>$@ - echo 'const goosList = "$(GOOS_LIST)"' >>$@ - echo 'const goarchList = "$(GOARCH_LIST)"' >>$@ - -test: - gotest - -testshort: - gotest -test.short diff --git a/src/cmd/goinstall/doc.go b/src/cmd/goinstall/doc.go index 13c37d0a2..a5df7b3bd 100644 --- a/src/cmd/goinstall/doc.go +++ b/src/cmd/goinstall/doc.go @@ -5,7 +5,8 @@ /* Goinstall is an experiment in automatic package installation. It installs packages, possibly downloading them from the internet. -It maintains a list of public Go packages at http://godashboard.appspot.com/package. +It maintains a list of public Go packages at +http://godashboard.appspot.com/package. Usage: goinstall [flags] importpath... @@ -15,7 +16,9 @@ Flags and default settings: -a=false install all previously installed packages -clean=false clean the package directory before installing -dashboard=true tally public packages on godashboard.appspot.com + -install=true build and install the package and its dependencies -log=true log installed packages to $GOROOT/goinstall.log for use by -a + -nuke=false remove the target object and clean before installing -u=false update already-downloaded packages -v=false verbose operation @@ -39,9 +42,22 @@ Another common idiom is to use to update, recompile, and reinstall all goinstalled packages. The source code for a package with import path foo/bar is expected -to be in the directory $GOROOT/src/pkg/foo/bar/. If the import -path refers to a code hosting site, goinstall will download the code -if necessary. The recognized code hosting sites are: +to be in the directory $GOROOT/src/pkg/foo/bar/ or $GOPATH/src/foo/bar/. +See "The GOPATH Environment Variable" for more about GOPATH. + +By default, goinstall prints output only when it encounters an error. +The -v flag causes goinstall to print information about packages +being considered and installed. + +Goinstall ignores Makefiles. + + +Remote Repositories + +If a package import path refers to a remote repository, goinstall will +download the code if necessary. + +Goinstall recognizes packages from a few common code hosting sites: BitBucket (Mercurial) @@ -61,7 +77,7 @@ if necessary. The recognized code hosting sites are: import "project.googlecode.com/svn/trunk" import "project.googlecode.com/svn/trunk/sub/directory" - Launchpad + Launchpad (Bazaar) import "launchpad.net/project" import "launchpad.net/project/series" @@ -70,7 +86,6 @@ if necessary. The recognized code hosting sites are: import "launchpad.net/~user/project/branch" import "launchpad.net/~user/project/branch/sub/directory" - If the destination directory (e.g., $GOROOT/src/pkg/bitbucket.org/user/project) already exists and contains an appropriate checkout, goinstall will not attempt to fetch updates. The -u flag changes this behavior, @@ -82,23 +97,42 @@ named "release". If there is one, it uses that version of the code. Otherwise it uses the default version selected by the version control system, typically HEAD for git, tip for Mercurial. -After a successful download and installation of a publicly accessible -remote package, goinstall reports the installation to godashboard.appspot.com, -which increments a count associated with the package and the time -of its most recent installation. This mechanism powers the package list -at http://godashboard.appspot.com/package, allowing Go programmers -to learn about popular packages that might be worth looking at. +After a successful download and installation of one of these import paths, +goinstall reports the installation to godashboard.appspot.com, which +increments a count associated with the package and the time of its most +recent installation. This mechanism powers the package list at +http://godashboard.appspot.com/package, allowing Go programmers to learn about +popular packages that might be worth looking at. The -dashboard=false flag disables this reporting. -By default, goinstall prints output only when it encounters an error. -The -v flag causes goinstall to print information about packages -being considered and installed. +For code hosted on other servers, goinstall recognizes the general form + + repository.vcs/path + +as denoting the given repository, with or without the .vcs suffix, using +the named version control system, and then the path inside that repository. +The supported version control systems are: + + Bazaar .bzr + Git .git + Mercurial .hg + Subversion .svn + +For example, + + import "example.org/user/foo.hg" + +denotes the root directory of the Mercurial repository at example.org/user/foo +or foo.hg, and + + import "example.org/repo.git/foo/bar" + +denotes the foo/bar directory of the Git repository at example.com/repo or +repo.git. -Goinstall does not attempt to be a replacement for make. -Instead, it invokes "make install" after locating the package sources. -For local packages without a Makefile and all remote packages, -goinstall creates and uses a temporary Makefile constructed from -the import path and the list of Go files in the package. +When a version control system supports multiple protocols, goinstall tries each +in turn. +For example, for Git it tries git://, then https://, then http://. The GOPATH Environment Variable diff --git a/src/cmd/goinstall/download.go b/src/cmd/goinstall/download.go index 2edf85efd..da892a69d 100644 --- a/src/cmd/goinstall/download.go +++ b/src/cmd/goinstall/download.go @@ -7,6 +7,8 @@ package main import ( + "exec" + "fmt" "http" "os" "path/filepath" @@ -31,74 +33,10 @@ func maybeReportToDashboard(path string) { } } -var vcsPatterns = map[string]*regexp.Regexp{ - "googlecode": regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/(svn|hg))(/[a-z0-9A-Z_.\-/]*)?$`), - "github": regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), - "bitbucket": regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), - "launchpad": regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0-9A-Z_.\-/]+)?$`), -} - -// isRemote returns true if the provided package path -// matches one of the supported remote repositories. -func isRemote(pkg string) bool { - for _, r := range vcsPatterns { - if r.MatchString(pkg) { - return true - } - } - return false -} - -// download checks out or updates pkg from the remote server. -func download(pkg, srcDir string) os.Error { - if strings.Contains(pkg, "..") { - return os.ErrorString("invalid path (contains ..)") - } - if m := vcsPatterns["bitbucket"].FindStringSubmatch(pkg); m != nil { - if err := vcsCheckout(&hg, srcDir, m[1], "http://"+m[1], m[1]); err != nil { - return err - } - return nil - } - if m := vcsPatterns["googlecode"].FindStringSubmatch(pkg); m != nil { - var v *vcs - switch m[2] { - case "hg": - v = &hg - case "svn": - v = &svn - default: - // regexp only allows hg, svn to get through - panic("missing case in download: " + pkg) - } - if err := vcsCheckout(v, srcDir, m[1], "https://"+m[1], m[1]); err != nil { - return err - } - return nil - } - if m := vcsPatterns["github"].FindStringSubmatch(pkg); m != nil { - if strings.HasSuffix(m[1], ".git") { - return os.ErrorString("repository " + pkg + " should not have .git suffix") - } - if err := vcsCheckout(&git, srcDir, m[1], "http://"+m[1]+".git", m[1]); err != nil { - return err - } - return nil - } - if m := vcsPatterns["launchpad"].FindStringSubmatch(pkg); m != nil { - // Either lp.net/<project>[/<series>[/<path>]] - // or lp.net/~<user or team>/<project>/<branch>[/<path>] - if err := vcsCheckout(&bzr, srcDir, m[1], "https://"+m[1], m[1]); err != nil { - return err - } - return nil - } - return os.ErrorString("unknown repository: " + pkg) -} - // a vcs represents a version control system // like Mercurial, Git, or Subversion. type vcs struct { + name string cmd string metadir string checkout string @@ -110,9 +48,20 @@ type vcs struct { log string logLimitFlag string logReleaseFlag string + check string + protocols []string + suffix string + defaultHosts []host +} + +type host struct { + pattern *regexp.Regexp + protocol string + suffix string } var hg = vcs{ + name: "Mercurial", cmd: "hg", metadir: ".hg", checkout: "checkout", @@ -123,9 +72,17 @@ var hg = vcs{ log: "log", logLimitFlag: "-l1", logReleaseFlag: "-rrelease", + check: "identify", + protocols: []string{"https", "http"}, + suffix: ".hg", + defaultHosts: []host{ + {regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/hg)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""}, + {regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http", ""}, + }, } var git = vcs{ + name: "Git", cmd: "git", metadir: ".git", checkout: "checkout", @@ -136,9 +93,16 @@ var git = vcs{ log: "show-ref", logLimitFlag: "", logReleaseFlag: "release", + check: "ls-remote", + protocols: []string{"git", "https", "http"}, + suffix: ".git", + defaultHosts: []host{ + {regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http", ".git"}, + }, } var svn = vcs{ + name: "Subversion", cmd: "svn", metadir: ".svn", checkout: "checkout", @@ -148,9 +112,16 @@ var svn = vcs{ log: "log", logLimitFlag: "-l1", logReleaseFlag: "release", + check: "info", + protocols: []string{"https", "http", "svn"}, + suffix: ".svn", + defaultHosts: []host{ + {regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/svn)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""}, + }, } var bzr = vcs{ + name: "Bazaar", cmd: "bzr", metadir: ".bzr", checkout: "update", @@ -162,6 +133,116 @@ var bzr = vcs{ log: "log", logLimitFlag: "-l1", logReleaseFlag: "-rrelease", + check: "info", + protocols: []string{"https", "http", "bzr"}, + suffix: ".bzr", + defaultHosts: []host{ + {regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0-9A-Z_.\-/]+)?$`), "https", ""}, + }, +} + +var vcsList = []*vcs{&git, &hg, &bzr, &svn} + +type vcsMatch struct { + *vcs + prefix, repo string +} + +// findHostedRepo checks whether pkg is located at one of +// the supported code hosting sites and, if so, returns a match. +func findHostedRepo(pkg string) (*vcsMatch, os.Error) { + for _, v := range vcsList { + for _, host := range v.defaultHosts { + if hm := host.pattern.FindStringSubmatch(pkg); hm != nil { + if host.suffix != "" && strings.HasSuffix(hm[1], host.suffix) { + return nil, os.NewError("repository " + pkg + " should not have " + v.suffix + " suffix") + } + repo := host.protocol + "://" + hm[1] + host.suffix + return &vcsMatch{v, hm[1], repo}, nil + } + } + } + return nil, nil +} + +// findAnyRepo looks for a vcs suffix in pkg (.git, etc) and returns a match. +func findAnyRepo(pkg string) (*vcsMatch, os.Error) { + for _, v := range vcsList { + i := strings.Index(pkg+"/", v.suffix+"/") + if i < 0 { + continue + } + if !strings.Contains(pkg[:i], "/") { + continue // don't match vcs suffix in the host name + } + if m := v.find(pkg[:i]); m != nil { + return m, nil + } + return nil, fmt.Errorf("couldn't find %s repository", v.name) + } + return nil, nil +} + +func (v *vcs) find(pkg string) *vcsMatch { + for _, proto := range v.protocols { + for _, suffix := range []string{"", v.suffix} { + repo := proto + "://" + pkg + suffix + out, err := exec.Command(v.cmd, v.check, repo).CombinedOutput() + if err == nil { + printf("find %s: found %s\n", pkg, repo) + return &vcsMatch{v, pkg + v.suffix, repo} + } + printf("find %s: %s %s %s: %v\n%s\n", pkg, v.cmd, v.check, repo, err, out) + } + } + return nil +} + +// isRemote returns true if the first part of the package name looks like a +// hostname - i.e. contains at least one '.' and the last part is at least 2 +// characters. +func isRemote(pkg string) bool { + parts := strings.SplitN(pkg, "/", 2) + if len(parts) != 2 { + return false + } + parts = strings.Split(parts[0], ".") + if len(parts) < 2 || len(parts[len(parts)-1]) < 2 { + return false + } + return true +} + +// download checks out or updates pkg from the remote server. +func download(pkg, srcDir string) (dashReport bool, err os.Error) { + if strings.Contains(pkg, "..") { + err = os.NewError("invalid path (contains ..)") + return + } + m, err := findHostedRepo(pkg) + if err != nil { + return + } + if m != nil { + dashReport = true // only report public code hosting sites + } else { + m, err = findAnyRepo(pkg) + if err != nil { + return + } + } + if m == nil { + err = os.NewError("cannot download: " + pkg) + return + } + installed, err := m.checkoutRepo(srcDir, m.prefix, m.repo) + if err != nil { + return + } + if !installed { + dashReport = false + } + return } // Try to detect if a "release" tag exists. If it does, update @@ -180,47 +261,46 @@ func (v *vcs) updateRepo(dst string) os.Error { return nil } -// vcsCheckout checks out repo into dst using vcs. +// checkoutRepo checks out repo into dst using vcs. // It tries to check out (or update, if the dst already // exists and -u was specified on the command line) // the repository at tag/branch "release". If there is no // such tag or branch, it falls back to the repository tip. -func vcsCheckout(vcs *vcs, srcDir, pkgprefix, repo, dashpath string) os.Error { +func (vcs *vcs) checkoutRepo(srcDir, pkgprefix, repo string) (installed bool, err os.Error) { dst := filepath.Join(srcDir, filepath.FromSlash(pkgprefix)) dir, err := os.Stat(filepath.Join(dst, vcs.metadir)) if err == nil && !dir.IsDirectory() { - return os.ErrorString("not a directory: " + dst) + err = os.NewError("not a directory: " + dst) + return } if err != nil { parent, _ := filepath.Split(dst) - if err := os.MkdirAll(parent, 0777); err != nil { - return err + if err = os.MkdirAll(parent, 0777); err != nil { + return } - if err := run(string(filepath.Separator), nil, vcs.cmd, vcs.clone, repo, dst); err != nil { - return err + if err = run(string(filepath.Separator), nil, vcs.cmd, vcs.clone, repo, dst); err != nil { + return } - if err := vcs.updateRepo(dst); err != nil { - return err + if err = vcs.updateRepo(dst); err != nil { + return } - // success on first installation - report - maybeReportToDashboard(dashpath) + installed = true } else if *update { // Retrieve new revisions from the remote branch, if the VCS // supports this operation independently (e.g. svn doesn't) if vcs.pull != "" { if vcs.pullForceFlag != "" { - if err := run(dst, nil, vcs.cmd, vcs.pull, vcs.pullForceFlag); err != nil { - return err + if err = run(dst, nil, vcs.cmd, vcs.pull, vcs.pullForceFlag); err != nil { + return } - } else if err := run(dst, nil, vcs.cmd, vcs.pull); err != nil { - return err + } else if err = run(dst, nil, vcs.cmd, vcs.pull); err != nil { + return } } - // Update to release or latest revision - if err := vcs.updateRepo(dst); err != nil { - return err + if err = vcs.updateRepo(dst); err != nil { + return } } - return nil + return } diff --git a/src/cmd/goinstall/main.go b/src/cmd/goinstall/main.go index 721e719d2..5cdf0f18e 100644 --- a/src/cmd/goinstall/main.go +++ b/src/cmd/goinstall/main.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Experimental Go package installer; see doc.go. - package main import ( @@ -11,6 +9,7 @@ import ( "exec" "flag" "fmt" + "go/build" "go/token" "io/ioutil" "os" @@ -39,7 +38,10 @@ var ( reportToDashboard = flag.Bool("dashboard", true, "report public packages at "+dashboardURL) logPkgs = flag.Bool("log", true, "log installed packages to $GOROOT/goinstall.log for use by -a") update = flag.Bool("u", false, "update already-downloaded packages") + doInstall = flag.Bool("install", true, "build and install") clean = flag.Bool("clean", false, "clean the package directory before installing") + nuke = flag.Bool("nuke", false, "clean the package directory and target before installing") + useMake = flag.Bool("make", true, "use make to build and install") verbose = flag.Bool("v", false, "verbose") ) @@ -56,7 +58,7 @@ func logf(format string, args ...interface{}) { fmt.Fprintf(os.Stderr, format, args...) } -func vlogf(format string, args ...interface{}) { +func printf(format string, args ...interface{}) { if *verbose { logf(format, args...) } @@ -160,66 +162,96 @@ func install(pkg, parent string) { fmt.Fprintf(os.Stderr, "\t%s\n", pkg) os.Exit(2) } - visit[pkg] = visiting parents[pkg] = parent - - vlogf("%s: visit\n", pkg) + visit[pkg] = visiting + defer func() { + visit[pkg] = done + }() // Check whether package is local or remote. // If remote, download or update it. - proot, pkg, err := findPackageRoot(pkg) + tree, pkg, err := build.FindTree(pkg) // Don't build the standard library. - if err == nil && proot.goroot && isStandardPath(pkg) { + if err == nil && tree.Goroot && isStandardPath(pkg) { if parent == "" { errorf("%s: can not goinstall the standard library\n", pkg) } else { - vlogf("%s: skipping standard library\n", pkg) + printf("%s: skipping standard library\n", pkg) } - visit[pkg] = done return } // Download remote packages if not found or forced with -u flag. remote := isRemote(pkg) - if remote && (err == ErrPackageNotFound || (err == nil && *update)) { - vlogf("%s: download\n", pkg) - err = download(pkg, proot.srcDir()) + dashReport := false + if remote && (err == build.ErrNotFound || (err == nil && *update)) { + printf("%s: download\n", pkg) + dashReport, err = download(pkg, tree.SrcDir()) } if err != nil { errorf("%s: %v\n", pkg, err) - visit[pkg] = done return } - dir := filepath.Join(proot.srcDir(), pkg) + dir := filepath.Join(tree.SrcDir(), pkg) // Install prerequisites. - dirInfo, err := scanDir(dir, parent == "") + dirInfo, err := build.ScanDir(dir, parent == "") if err != nil { errorf("%s: %v\n", pkg, err) - visit[pkg] = done return } - if len(dirInfo.goFiles) == 0 { + if len(dirInfo.GoFiles)+len(dirInfo.CgoFiles) == 0 { errorf("%s: package has no files\n", pkg) - visit[pkg] = done return } - for _, p := range dirInfo.imports { + for _, p := range dirInfo.Imports { if p != "C" { install(p, pkg) } } + if errors { + return + } // Install this package. - if !errors { - isCmd := dirInfo.pkgName == "main" - if err := domake(dir, pkg, proot, isCmd); err != nil { - errorf("installing: %v\n", err) - } else if remote && *logPkgs { - // mark package as installed in $GOROOT/goinstall.log - logPackage(pkg) + if *useMake { + err := domake(dir, pkg, tree, dirInfo.IsCommand()) + if err != nil { + errorf("%s: install: %v\n", pkg, err) + return + } + } else { + script, err := build.Build(tree, pkg, dirInfo) + if err != nil { + errorf("%s: install: %v\n", pkg, err) + return + } + if *nuke { + printf("%s: nuke\n", pkg) + script.Nuke() + } else if *clean { + printf("%s: clean\n", pkg) + script.Clean() } + if *doInstall { + if script.Stale() { + printf("%s: install\n", pkg) + if err := script.Run(); err != nil { + errorf("%s: install: %v\n", pkg, err) + return + } + } else { + printf("%s: up-to-date\n", pkg) + } + } + } + if dashReport { + maybeReportToDashboard(pkg) + } + if remote { + // mark package as installed in $GOROOT/goinstall.log + logPackage(pkg) } - visit[pkg] = done + return } @@ -249,7 +281,7 @@ func genRun(dir string, stdin []byte, arg []string, quiet bool) os.Error { cmd := exec.Command(arg[0], arg[1:]...) cmd.Stdin = bytes.NewBuffer(stdin) cmd.Dir = dir - vlogf("%s: %s %s\n", dir, cmd.Path, strings.Join(arg[1:], " ")) + printf("%s: %s %s\n", dir, cmd.Path, strings.Join(arg[1:], " ")) out, err := cmd.CombinedOutput() if err != nil { if !quiet || *verbose { @@ -260,7 +292,7 @@ func genRun(dir string, stdin []byte, arg []string, quiet bool) os.Error { os.Stderr.Write(out) fmt.Fprintf(os.Stderr, "--- %s\n", err) } - return os.ErrorString("running " + arg[0] + ": " + err.String()) + return os.NewError("running " + arg[0] + ": " + err.String()) } return nil } diff --git a/src/cmd/goinstall/make.go b/src/cmd/goinstall/make.go index 0c44481d7..0fd9b02a8 100644 --- a/src/cmd/goinstall/make.go +++ b/src/cmd/goinstall/make.go @@ -1,4 +1,4 @@ -// Copyright 2010 The Go Authors. All rights reserved. +// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -8,21 +8,25 @@ package main import ( "bytes" + "go/build" "os" "path/filepath" + "strings" "template" ) // domake builds the package in dir. // domake generates a standard Makefile and passes it // to make on standard input. -func domake(dir, pkg string, root *pkgroot, isCmd bool) (err os.Error) { - makefile, err := makeMakefile(dir, pkg, root, isCmd) +func domake(dir, pkg string, tree *build.Tree, isCmd bool) (err os.Error) { + makefile, err := makeMakefile(dir, pkg, tree, isCmd) if err != nil { return err } cmd := []string{"bash", "gomake", "-f-"} - if *clean { + if *nuke { + cmd = append(cmd, "nuke") + } else if *clean { cmd = append(cmd, "clean") } cmd = append(cmd, "install") @@ -32,46 +36,46 @@ func domake(dir, pkg string, root *pkgroot, isCmd bool) (err os.Error) { // makeMakefile computes the standard Makefile for the directory dir // installing as package pkg. It includes all *.go files in the directory // except those in package main and those ending in _test.go. -func makeMakefile(dir, pkg string, root *pkgroot, isCmd bool) ([]byte, os.Error) { +func makeMakefile(dir, pkg string, tree *build.Tree, isCmd bool) ([]byte, os.Error) { if !safeName(pkg) { - return nil, os.ErrorString("unsafe name: " + pkg) + return nil, os.NewError("unsafe name: " + pkg) } targ := pkg - targDir := root.pkgDir() + targDir := tree.PkgDir() if isCmd { // use the last part of the package name for targ _, targ = filepath.Split(pkg) - targDir = root.binDir() + targDir = tree.BinDir() } - dirInfo, err := scanDir(dir, isCmd) + dirInfo, err := build.ScanDir(dir, isCmd) if err != nil { return nil, err } - cgoFiles := dirInfo.cgoFiles + cgoFiles := dirInfo.CgoFiles isCgo := make(map[string]bool, len(cgoFiles)) for _, file := range cgoFiles { if !safeName(file) { - return nil, os.ErrorString("bad name: " + file) + return nil, os.NewError("bad name: " + file) } isCgo[file] = true } - goFiles := make([]string, 0, len(dirInfo.goFiles)) - for _, file := range dirInfo.goFiles { + goFiles := make([]string, 0, len(dirInfo.GoFiles)) + for _, file := range dirInfo.GoFiles { if !safeName(file) { - return nil, os.ErrorString("unsafe name: " + file) + return nil, os.NewError("unsafe name: " + file) } if !isCgo[file] { goFiles = append(goFiles, file) } } - oFiles := make([]string, 0, len(dirInfo.cFiles)+len(dirInfo.sFiles)) - cgoOFiles := make([]string, 0, len(dirInfo.cFiles)) - for _, file := range dirInfo.cFiles { + oFiles := make([]string, 0, len(dirInfo.CFiles)+len(dirInfo.SFiles)) + cgoOFiles := make([]string, 0, len(dirInfo.CFiles)) + for _, file := range dirInfo.CFiles { if !safeName(file) { - return nil, os.ErrorString("unsafe name: " + file) + return nil, os.NewError("unsafe name: " + file) } // When cgo is in use, C files are compiled with gcc, // otherwise they're compiled with gc. @@ -82,13 +86,18 @@ func makeMakefile(dir, pkg string, root *pkgroot, isCmd bool) ([]byte, os.Error) } } - for _, file := range dirInfo.sFiles { + for _, file := range dirInfo.SFiles { if !safeName(file) { - return nil, os.ErrorString("unsafe name: " + file) + return nil, os.NewError("unsafe name: " + file) } oFiles = append(oFiles, file[:len(file)-2]+".$O") } + var imports []string + for _, t := range build.Path { + imports = append(imports, t.PkgDir()) + } + var buf bytes.Buffer md := makedata{targ, targDir, "pkg", goFiles, oFiles, cgoFiles, cgoOFiles, imports} if isCmd { @@ -106,6 +115,9 @@ func safeName(s string) bool { if s == "" { return false } + if strings.Contains(s, "..") { + return false + } for i := 0; i < len(s); i++ { if c := s[i]; c < 0x80 && bytes.IndexByte(safeBytes, c) < 0 { return false @@ -134,28 +146,28 @@ TARGDIR={TargDir} {.section GoFiles} GOFILES=\ -{.repeated section GoFiles} +{.repeated section @} {@}\ {.end} {.end} {.section OFiles} OFILES=\ -{.repeated section OFiles} +{.repeated section @} {@}\ {.end} {.end} {.section CgoFiles} CGOFILES=\ -{.repeated section CgoFiles} +{.repeated section @} {@}\ {.end} {.end} {.section CgoOFiles} CGO_OFILES=\ -{.repeated section CgoOFiles} +{.repeated section @} {@}\ {.end} diff --git a/src/cmd/goinstall/parse.go b/src/cmd/goinstall/parse.go deleted file mode 100644 index a4bb761f2..000000000 --- a/src/cmd/goinstall/parse.go +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Wrappers for Go parser. - -package main - -import ( - "go/ast" - "go/parser" - "log" - "os" - "path/filepath" - "strconv" - "strings" - "runtime" -) - - -type dirInfo struct { - goFiles []string // .go files within dir (including cgoFiles) - cgoFiles []string // .go files that import "C" - cFiles []string // .c files within dir - sFiles []string // .s files within dir - imports []string // All packages imported by goFiles - pkgName string // Name of package within dir -} - -// scanDir returns a structure with details about the Go content found -// in the given directory. The list of files will NOT contain the -// following entries: -// -// - Files in package main (unless allowMain is true) -// - Files ending in _test.go -// - Files starting with _ (temporary) -// - Files containing .cgo in their names -// -// The imports map keys are package paths imported by listed Go files, -// and the values are the Go files importing the respective package paths. -func scanDir(dir string, allowMain bool) (info *dirInfo, err os.Error) { - f, err := os.Open(dir) - if err != nil { - return nil, err - } - dirs, err := f.Readdir(-1) - f.Close() - if err != nil { - return nil, err - } - - goFiles := make([]string, 0, len(dirs)) - cgoFiles := make([]string, 0, len(dirs)) - cFiles := make([]string, 0, len(dirs)) - sFiles := make([]string, 0, len(dirs)) - importsm := make(map[string]bool) - pkgName := "" - for i := range dirs { - d := &dirs[i] - if strings.HasPrefix(d.Name, "_") || strings.Index(d.Name, ".cgo") != -1 { - continue - } - if !goodOSArch(d.Name) { - continue - } - - switch filepath.Ext(d.Name) { - case ".go": - if strings.HasSuffix(d.Name, "_test.go") { - continue - } - case ".c": - cFiles = append(cFiles, d.Name) - continue - case ".s": - sFiles = append(sFiles, d.Name) - continue - default: - continue - } - - filename := filepath.Join(dir, d.Name) - pf, err := parser.ParseFile(fset, filename, nil, parser.ImportsOnly) - if err != nil { - return nil, err - } - s := string(pf.Name.Name) - if s == "main" && !allowMain { - continue - } - if s == "documentation" { - continue - } - if pkgName == "" { - pkgName = s - } else if pkgName != s { - // Only if all files in the directory are in package main - // do we return pkgName=="main". - // A mix of main and another package reverts - // to the original (allowMain=false) behaviour. - if s == "main" || pkgName == "main" { - return scanDir(dir, false) - } - return nil, os.ErrorString("multiple package names in " + dir) - } - goFiles = append(goFiles, d.Name) - for _, decl := range pf.Decls { - for _, spec := range decl.(*ast.GenDecl).Specs { - quoted := string(spec.(*ast.ImportSpec).Path.Value) - unquoted, err := strconv.Unquote(quoted) - if err != nil { - log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted) - } - importsm[unquoted] = true - if unquoted == "C" { - cgoFiles = append(cgoFiles, d.Name) - } - } - } - } - imports := make([]string, len(importsm)) - i := 0 - for p := range importsm { - imports[i] = p - i++ - } - return &dirInfo{goFiles, cgoFiles, cFiles, sFiles, imports, pkgName}, nil -} - -// goodOSArch returns false if the filename contains a $GOOS or $GOARCH -// suffix which does not match the current system. -// The recognized filename formats are: -// -// name_$(GOOS).* -// name_$(GOARCH).* -// name_$(GOOS)_$(GOARCH).* -// -func goodOSArch(filename string) bool { - if dot := strings.Index(filename, "."); dot != -1 { - filename = filename[:dot] - } - l := strings.Split(filename, "_", -1) - n := len(l) - if n == 0 { - return true - } - if good, known := goodOS[l[n-1]]; known { - return good - } - if good, known := goodArch[l[n-1]]; known { - if !good || n < 2 { - return false - } - good, known = goodOS[l[n-2]] - return good || !known - } - return true -} - -var goodOS = make(map[string]bool) -var goodArch = make(map[string]bool) - -func init() { - goodOS = make(map[string]bool) - goodArch = make(map[string]bool) - for _, v := range strings.Fields(goosList) { - goodOS[v] = v == runtime.GOOS - } - for _, v := range strings.Fields(goarchList) { - goodArch[v] = v == runtime.GOARCH - } -} diff --git a/src/cmd/goinstall/path.go b/src/cmd/goinstall/path.go deleted file mode 100644 index b8c392931..000000000 --- a/src/cmd/goinstall/path.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "fmt" - "log" - "os" - "path/filepath" - "runtime" - "strings" -) - -var ( - gopath []*pkgroot - imports []string - defaultRoot *pkgroot // default root for remote packages -) - -// set up gopath: parse and validate GOROOT and GOPATH variables -func init() { - root := runtime.GOROOT() - p, err := newPkgroot(root) - if err != nil { - log.Fatalf("Invalid GOROOT %q: %v", root, err) - } - p.goroot = true - gopath = []*pkgroot{p} - - for _, p := range filepath.SplitList(os.Getenv("GOPATH")) { - if p == "" { - continue - } - r, err := newPkgroot(p) - if err != nil { - log.Printf("Invalid GOPATH %q: %v", p, err) - continue - } - gopath = append(gopath, r) - imports = append(imports, r.pkgDir()) - - // select first GOPATH entry as default - if defaultRoot == nil { - defaultRoot = r - } - } - - // use GOROOT if no valid GOPATH specified - if defaultRoot == nil { - defaultRoot = gopath[0] - } -} - -type pkgroot struct { - path string - goroot bool // TODO(adg): remove this once Go tree re-organized -} - -func newPkgroot(p string) (*pkgroot, os.Error) { - if !filepath.IsAbs(p) { - return nil, os.NewError("must be absolute") - } - ep, err := filepath.EvalSymlinks(p) - if err != nil { - return nil, err - } - return &pkgroot{path: ep}, nil -} - -func (r *pkgroot) srcDir() string { - if r.goroot { - return filepath.Join(r.path, "src", "pkg") - } - return filepath.Join(r.path, "src") -} - -func (r *pkgroot) pkgDir() string { - goos, goarch := runtime.GOOS, runtime.GOARCH - if e := os.Getenv("GOOS"); e != "" { - goos = e - } - if e := os.Getenv("GOARCH"); e != "" { - goarch = e - } - return filepath.Join(r.path, "pkg", goos+"_"+goarch) -} - -func (r *pkgroot) binDir() string { - return filepath.Join(r.path, "bin") -} - -func (r *pkgroot) hasSrcDir(name string) bool { - fi, err := os.Stat(filepath.Join(r.srcDir(), name)) - if err != nil { - return false - } - return fi.IsDirectory() -} - -func (r *pkgroot) hasPkg(name string) bool { - fi, err := os.Stat(filepath.Join(r.pkgDir(), name+".a")) - if err != nil { - return false - } - return fi.IsRegular() - // TODO(adg): check object version is consistent -} - - -var ErrPackageNotFound = os.NewError("package could not be found locally") - -// findPackageRoot takes an import or filesystem path and returns the -// root where the package source should be and the package import path. -func findPackageRoot(path string) (root *pkgroot, pkg string, err os.Error) { - if isLocalPath(path) { - if path, err = filepath.Abs(path); err != nil { - return - } - for _, r := range gopath { - rpath := r.srcDir() + string(filepath.Separator) - if !strings.HasPrefix(path, rpath) { - continue - } - root = r - pkg = path[len(rpath):] - return - } - err = fmt.Errorf("path %q not inside a GOPATH", path) - return - } - root = defaultRoot - pkg = path - for _, r := range gopath { - if r.hasSrcDir(path) { - root = r - return - } - } - err = ErrPackageNotFound - return -} - -// Is this a local path? /foo ./foo ../foo . .. -func isLocalPath(s string) bool { - const sep = string(filepath.Separator) - return strings.HasPrefix(s, sep) || strings.HasPrefix(s, "."+sep) || strings.HasPrefix(s, ".."+sep) || s == "." || s == ".." -} diff --git a/src/cmd/goinstall/syslist_test.go b/src/cmd/goinstall/syslist_test.go deleted file mode 100644 index 795cd293a..000000000 --- a/src/cmd/goinstall/syslist_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -package main - -import ( - "runtime" - "testing" -) - -var ( - thisOS = runtime.GOOS - thisArch = runtime.GOARCH - otherOS = anotherOS() - otherArch = anotherArch() -) - -func anotherOS() string { - if thisOS != "darwin" { - return "darwin" - } - return "linux" -} - -func anotherArch() string { - if thisArch != "amd64" { - return "amd64" - } - return "386" -} - -type GoodFileTest struct { - name string - result bool -} - -var tests = []GoodFileTest{ - {"file.go", true}, - {"file.c", true}, - {"file_foo.go", true}, - {"file_" + thisArch + ".go", true}, - {"file_" + otherArch + ".go", false}, - {"file_" + thisOS + ".go", true}, - {"file_" + otherOS + ".go", false}, - {"file_" + thisOS + "_" + thisArch + ".go", true}, - {"file_" + otherOS + "_" + thisArch + ".go", false}, - {"file_" + thisOS + "_" + otherArch + ".go", false}, - {"file_" + otherOS + "_" + otherArch + ".go", false}, - {"file_foo_" + thisArch + ".go", true}, - {"file_foo_" + otherArch + ".go", false}, - {"file_" + thisOS + ".c", true}, - {"file_" + otherOS + ".c", false}, -} - -func TestGoodOSArch(t *testing.T) { - for _, test := range tests { - if goodOSArch(test.name) != test.result { - t.Fatalf("goodOSArch(%q) != %v", test.name, test.result) - } - } -} diff --git a/src/cmd/gopack/Makefile b/src/cmd/gopack/Makefile index 859809562..91a8ac2df 100644 --- a/src/cmd/gopack/Makefile +++ b/src/cmd/gopack/Makefile @@ -9,7 +9,4 @@ TARG=gopack OFILES=\ ar.$O\ -LIB=\ - ../../../lib/libmach.a\ - include ../../Make.ccmd diff --git a/src/cmd/gotest/doc.go b/src/cmd/gotest/doc.go index 9dba390c1..5be06f817 100644 --- a/src/cmd/gotest/doc.go +++ b/src/cmd/gotest/doc.go @@ -53,7 +53,9 @@ The resulting test binary, called (for amd64) 6.out, has several flags. Usage: 6.out [-test.v] [-test.run pattern] [-test.bench pattern] \ [-test.cpuprofile=cpu.out] \ - [-test.memprofile=mem.out] [-test.memprofilerate=1] + [-test.memprofile=mem.out] [-test.memprofilerate=1] \ + [-test.timeout=10] [-test.short] \ + [-test.benchtime=3] [-test.cpu=1,2,3,4] The -test.v flag causes the tests to be logged as they run. The -test.run flag causes only those tests whose names match the regular @@ -93,6 +95,13 @@ The -test.timeout flag sets a timeout for the test in seconds. If the test runs for longer than that, it will panic, dumping a stack trace of all existing goroutines. +The -test.benchtime flag specifies the number of seconds to run each benchmark. +The default is one second. + +The -test.cpu flag specifies a list of GOMAXPROCS values for which +the tests or benchmarks are executed. The default is the current +value of GOMAXPROCS. + For convenience, each of these -test.X flags of the test binary is also available as the flag -X in gotest itself. Flags not listed here are unaffected. For instance, the command diff --git a/src/cmd/gotest/flag.go b/src/cmd/gotest/flag.go index 780c78b9c..c3a28f9a3 100644 --- a/src/cmd/gotest/flag.go +++ b/src/cmd/gotest/flag.go @@ -23,6 +23,8 @@ var usageMessage = `Usage of %s: // These flags can be passed with or without a "test." prefix: -v or -test.v. -bench="": passes -test.bench to test + -benchtime=1: passes -test.benchtime to test + -cpu="": passes -test.cpu to test -cpuprofile="": passes -test.cpuprofile to test -memprofile="": passes -test.memprofile to test -memprofilerate=0: passes -test.memprofilerate to test @@ -56,6 +58,8 @@ var flagDefn = []*flagSpec{ // passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v. &flagSpec{name: "bench", passToTest: true}, + &flagSpec{name: "benchtime", passToTest: true}, + &flagSpec{name: "cpu", passToTest: true}, &flagSpec{name: "cpuprofile", passToTest: true}, &flagSpec{name: "memprofile", passToTest: true}, &flagSpec{name: "memprofilerate", passToTest: true}, diff --git a/src/cmd/gotype/gotype.go b/src/cmd/gotype/gotype.go index b6a23ae5f..501ead443 100644 --- a/src/cmd/gotype/gotype.go +++ b/src/cmd/gotype/gotype.go @@ -114,7 +114,7 @@ func parseFiles(fset *token.FileSet, filenames []string) (files map[string]*ast. } if file := parse(fset, filename, src); file != nil { if files[filename] != nil { - report(os.ErrorString(fmt.Sprintf("%q: duplicate file", filename))) + report(os.NewError(fmt.Sprintf("%q: duplicate file", filename))) continue } files[filename] = file diff --git a/src/cmd/govet/Makefile b/src/cmd/govet/Makefile index 291b27197..f565b78f5 100644 --- a/src/cmd/govet/Makefile +++ b/src/cmd/govet/Makefile @@ -9,3 +9,6 @@ GOFILES=\ govet.go\ include ../../Make.cmd + +test testshort: $(TARG) + ../../../test/errchk $(TARG) -printfuncs='Warn:1,Warnf:1' govet.go diff --git a/src/cmd/govet/govet.go b/src/cmd/govet/govet.go index b811c61a2..5b24d2ff0 100644 --- a/src/cmd/govet/govet.go +++ b/src/cmd/govet/govet.go @@ -15,6 +15,7 @@ import ( "go/token" "os" "path/filepath" + "reflect" "strconv" "strings" "utf8" @@ -50,7 +51,7 @@ func main() { flag.Parse() if *printfuncs != "" { - for _, name := range strings.Split(*printfuncs, ",", -1) { + for _, name := range strings.Split(*printfuncs, ",") { if len(name) == 0 { flag.Usage() } @@ -59,7 +60,7 @@ func main() { var err os.Error skip, err = strconv.Atoi(name[colon+1:]) if err != nil { - error(`illegal format for "Func:N" argument %q; %s`, name, err) + errorf(`illegal format for "Func:N" argument %q; %s`, name, err) } name = name[:colon] } @@ -93,7 +94,7 @@ func doFile(name string, reader io.Reader) { fs := token.NewFileSet() parsedFile, err := parser.ParseFile(fs, name, reader, 0) if err != nil { - error("%s: %s", name, err) + errorf("%s: %s", name, err) return } file := &File{fs.File(parsedFile.Pos())} @@ -121,7 +122,7 @@ func walkDir(root string) { done := make(chan bool) go func() { for e := range errors { - error("walk error: %s", e) + errorf("walk error: %s", e) } done <- true }() @@ -132,7 +133,7 @@ func walkDir(root string) { // error formats the error to standard error, adding program // identification and a newline -func error(format string, args ...interface{}) { +func errorf(format string, args ...interface{}) { fmt.Fprintf(os.Stderr, "govet: "+format+"\n", args...) setExit(2) } @@ -185,15 +186,35 @@ func (f *File) checkFile(name string, file *ast.File) { // Visit implements the ast.Visitor interface. func (f *File) Visit(node ast.Node) ast.Visitor { - // TODO: could return nil for nodes that cannot contain a CallExpr - - // will shortcut traversal. Worthwhile? switch n := node.(type) { case *ast.CallExpr: f.checkCallExpr(n) + case *ast.Field: + f.checkFieldTag(n) } return f } +// checkField checks a struct field tag. +func (f *File) checkFieldTag(field *ast.Field) { + if field.Tag == nil { + return + } + + tag, err := strconv.Unquote(field.Tag.Value) + if err != nil { + f.Warnf(field.Pos(), "unable to read struct tag %s", field.Tag.Value) + return + } + + // Check tag for validity by appending + // new key:value to end and checking that + // the tag parsing code can find it. + if reflect.StructTag(tag+` _gofix:"_magic"`).Get("_gofix") != "_magic" { + f.Warnf(field.Pos(), "struct field tag %s not compatible with reflect.StructTag.Get", field.Tag.Value) + return + } +} // checkCallExpr checks a call expression. func (f *File) checkCallExpr(call *ast.CallExpr) { @@ -358,19 +379,24 @@ func (f *File) checkPrint(call *ast.CallExpr, name string, skip int) { } // This function never executes, but it serves as a simple test for the program. -// Test with govet -printfuncs="Bad:1,Badf:1,Warn:1,Warnf:1" govet.go +// Test with make test. func BadFunctionUsedInTests() { - fmt.Println() // niladic call - fmt.Println("%s", "hi") // % in call to Println - fmt.Printf("%s", "hi", 3) // wrong # percents - fmt.Printf("%s%%%d", "hi", 3) // right # percents - fmt.Printf("%.*d", 3, 3) // right # percents, with a * - fmt.Printf("%.*d", 3, 3, 3) // wrong # percents, with a * - printf("now is the time", "buddy") // no %s - Printf("now is the time", "buddy") // no %s + fmt.Println() // not an error + fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call" + fmt.Printf("%s", "hi", 3) // ERROR "wrong number of args in Printf call" + fmt.Printf("%s%%%d", "hi", 3) // correct + fmt.Printf("%.*d", 3, 3) // correct + fmt.Printf("%.*d", 3, 3, 3) // ERROR "wrong number of args in Printf call" + printf("now is the time", "buddy") // ERROR "no formatting directive" + Printf("now is the time", "buddy") // ERROR "no formatting directive" + Printf("hi") // ok f := new(File) - f.Warn(0, "%s", "hello", 3) // % in call to added function - f.Warnf(0, "%s", "hello", 3) // wrong # %s in call to added function + f.Warn(0, "%s", "hello", 3) // ERROR "possible formatting directive in Warn call" + f.Warnf(0, "%s", "hello", 3) // ERROR "wrong number of args in Warnf call" +} + +type BadTypeUsedInTests struct { + X int "hello" // ERROR "struct field tag" } // printf is used by the test. diff --git a/src/cmd/goyacc/goyacc.go b/src/cmd/goyacc/goyacc.go index 220c99492..543f8b1e8 100644 --- a/src/cmd/goyacc/goyacc.go +++ b/src/cmd/goyacc/goyacc.go @@ -2834,7 +2834,7 @@ func others() { // copy yaccpar fmt.Fprintf(ftable, "\n//line yaccpar:1\n") - parts := strings.Split(yaccpar, prefix+"run()", 2) + parts := strings.SplitN(yaccpar, prefix+"run()", 2) fmt.Fprintf(ftable, "%v", parts[0]) ftable.Write(fcode.Bytes()) fmt.Fprintf(ftable, "%v", parts[1]) diff --git a/src/cmd/hgpatch/main.go b/src/cmd/hgpatch/main.go index 8ee3422e2..4f7aec22b 100644 --- a/src/cmd/hgpatch/main.go +++ b/src/cmd/hgpatch/main.go @@ -176,7 +176,7 @@ func main() { list[i] = f i++ } - sort.SortStrings(list) + sort.Strings(list) for _, f := range list { fmt.Printf("%s\n", f) } @@ -282,7 +282,7 @@ func hgModified() ([]string, os.Error) { if err != nil { return nil, err } - return strings.Split(strings.TrimSpace(out), "\n", -1), nil + return strings.Split(strings.TrimSpace(out), "\n"), nil } // hgAdd adds name to the repository. @@ -329,15 +329,14 @@ var lookPathCache = make(map[string]string) // It provides input on standard input to the command. func run(argv []string, input []byte) (out string, err os.Error) { if len(argv) < 1 { - err = os.EINVAL - goto Error + return "", &runError{dup(argv), os.EINVAL} } prog, ok := lookPathCache[argv[0]] if !ok { prog, err = exec.LookPath(argv[0]) if err != nil { - goto Error + return "", &runError{dup(argv), err} } lookPathCache[argv[0]] = prog } @@ -347,13 +346,10 @@ func run(argv []string, input []byte) (out string, err os.Error) { cmd.Stdin = bytes.NewBuffer(input) } bs, err := cmd.CombinedOutput() - if err == nil { - return string(bs), nil + if err != nil { + return "", &runError{dup(argv), err} } - -Error: - err = &runError{dup(argv), err} - return + return string(bs), nil } // A runError represents an error that occurred while running a command. diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 3f3faade0..f1132fc8b 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -249,7 +249,7 @@ dynrelocsym(Sym *s) return; for(r=s->r; r<s->r+s->nr; r++) { targ = r->sym; - if(r->sym->plt == -2) { // make dynimport JMP table for PE object files. + if(r->sym->plt == -2 && r->sym->got != -2) { // make dynimport JMP table for PE object files. targ->plt = rel->size; r->sym = rel; r->add = targ->plt; @@ -278,6 +278,10 @@ dynreloc(void) { Sym *s; + // -d supresses dynamic loader format, so we may as well not + // compute these sections or mark their symbols as reachable. + if(debug['d'] && HEADTYPE != Hwindows) + return; if(debug['v']) Bprint(&bso, "%5.2f reloc\n", cputime()); Bflush(&bso); @@ -482,13 +486,13 @@ codeblk(int32 addr, int32 size) q = sym->p; while(n >= 16) { - Bprint(&bso, "%.6ux\t%-20.16I\n", addr, q); + Bprint(&bso, "%.6ux\t%-20.16I\n", addr, q); addr += 16; q += 16; n -= 16; } if(n > 0) - Bprint(&bso, "%.6ux\t%-20.*I\n", addr, n, q); + Bprint(&bso, "%.6ux\t%-20.*I\n", addr, (int)n, q); addr += n; continue; } @@ -502,7 +506,7 @@ codeblk(int32 addr, int32 size) Bprint(&bso, "%.6ux\t", p->pc); q = sym->p + p->pc - sym->value; n = epc - p->pc; - Bprint(&bso, "%-20.*I | %P\n", n, q, p); + Bprint(&bso, "%-20.*I | %P\n", (int)n, q, p); addr += n; } } @@ -543,7 +547,7 @@ datblk(int32 addr, int32 size) Bprint(&bso, "%-20s %.8ux| 00 ...\n", "(pre-pad)", addr); addr = sym->value; } - Bprint(&bso, "%-20s %.8ux|", sym->name, addr); + Bprint(&bso, "%-20s %.8ux|", sym->name, (uint)addr); p = sym->p; ep = p + sym->np; while(p < ep) @@ -555,8 +559,8 @@ datblk(int32 addr, int32 size) } if(addr < eaddr) - Bprint(&bso, "%-20s %.8ux| 00 ...\n", "(post-pad)", addr); - Bprint(&bso, "%-20s %.8ux|\n", "", eaddr); + Bprint(&bso, "%-20s %.8ux| 00 ...\n", "(post-pad)", (uint)addr); + Bprint(&bso, "%-20s %.8ux|\n", "", (uint)eaddr); } void @@ -781,18 +785,38 @@ dodata(void) */ /* read-only data */ - sect = addsection(&segtext, ".rodata", 06); + sect = addsection(&segtext, ".rodata", 04); sect->vaddr = 0; datsize = 0; s = datap; - for(; s != nil && s->type < SDATA; s = s->next) { + for(; s != nil && s->type < SSYMTAB; s = s->next) { s->type = SRODATA; t = rnd(s->size, PtrSize); s->value = datsize; datsize += t; } sect->len = datsize - sect->vaddr; - + + /* gosymtab */ + sect = addsection(&segtext, ".gosymtab", 04); + sect->vaddr = datsize; + for(; s != nil && s->type < SPCLNTAB; s = s->next) { + s->type = SRODATA; + s->value = datsize; + datsize += s->size; + } + sect->len = datsize - sect->vaddr; + + /* gopclntab */ + sect = addsection(&segtext, ".gopclntab", 04); + sect->vaddr = datsize; + for(; s != nil && s->type < SDATA; s = s->next) { + s->type = SRODATA; + s->value = datsize; + datsize += s->size; + } + sect->len = datsize - sect->vaddr; + /* data */ datsize = 0; sect = addsection(&segdata, ".data", 06); @@ -808,9 +832,9 @@ dodata(void) t = rnd(t, PtrSize); else if(t > 2) t = rnd(t, 4); - if(t & 1) + if(t & 1) { ; - else if(t & 2) + } else if(t & 2) datsize = rnd(datsize, 2); else if(t & 4) datsize = rnd(datsize, 4); @@ -834,9 +858,9 @@ dodata(void) t = rnd(t, PtrSize); else if(t > 2) t = rnd(t, 4); - if(t & 1) + if(t & 1) { ; - else if(t & 2) + } else if(t & 2) datsize = rnd(datsize, 2); else if(t & 4) datsize = rnd(datsize, 4); @@ -886,7 +910,7 @@ textaddress(void) void address(void) { - Section *s, *text, *data, *rodata; + Section *s, *text, *data, *rodata, *symtab, *pclntab; Sym *sym, *sub; uvlong va; @@ -917,7 +941,9 @@ address(void) segdata.filelen = segdata.sect->len; // assume .data is first text = segtext.sect; - rodata = segtext.sect->next; + rodata = text->next; + symtab = rodata->next; + pclntab = symtab->next; data = segdata.sect; for(sym = datap; sym != nil; sym = sym->next) { @@ -934,12 +960,11 @@ address(void) xdefine("etext", STEXT, text->vaddr + text->len); xdefine("rodata", SRODATA, rodata->vaddr); xdefine("erodata", SRODATA, rodata->vaddr + rodata->len); + xdefine("symtab", SRODATA, symtab->vaddr); + xdefine("esymtab", SRODATA, symtab->vaddr + symtab->len); + xdefine("pclntab", SRODATA, pclntab->vaddr); + xdefine("epclntab", SRODATA, pclntab->vaddr + pclntab->len); xdefine("data", SBSS, data->vaddr); xdefine("edata", SBSS, data->vaddr + data->len); xdefine("end", SBSS, segdata.vaddr + segdata.len); - - sym = lookup("pclntab", 0); - xdefine("epclntab", SRODATA, sym->value + sym->size); - sym = lookup("symtab", 0); - xdefine("esymtab", SRODATA, sym->value + sym->size); } diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c index 50b42183e..1c10dc796 100644 --- a/src/cmd/ld/dwarf.c +++ b/src/cmd/ld/dwarf.c @@ -1822,7 +1822,7 @@ flushunit(DWDie *dwinfo, vlong pc, vlong unitstart, int32 header_length) seek(cout, unitstart, 0); LPUT(here - unitstart - sizeof(int32)); // unit_length WPUT(3); // dwarf version - LPUT(header_length); // header lenght starting here + LPUT(header_length); // header length starting here cflush(); seek(cout, here, 0); } diff --git a/src/cmd/ld/elf.h b/src/cmd/ld/elf.h index d1370d28b..c63df2241 100644 --- a/src/cmd/ld/elf.h +++ b/src/cmd/ld/elf.h @@ -110,7 +110,6 @@ typedef struct { #define ELFOSABI_OPENVMS 13 /* Open VMS */ #define ELFOSABI_NSK 14 /* HP Non-Stop Kernel */ #define ELFOSABI_ARM 97 /* ARM */ -#define ELFOSABI_NACL 123 /* Native Client */ #define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ #define ELFOSABI_SYSV ELFOSABI_NONE /* symbol used in old spec */ @@ -262,8 +261,8 @@ typedef struct { /* Values for d_tag. */ #define DT_NULL 0 /* Terminating entry. */ -#define DT_NEEDED 1 /* String table offset of a needed shared - library. */ +/* String table offset of a needed shared library. */ +#define DT_NEEDED 1 #define DT_PLTRELSZ 2 /* Total size in bytes of PLT relocations. */ #define DT_PLTGOT 3 /* Processor-dependent address. */ #define DT_HASH 4 /* Address of symbol hash table. */ @@ -276,8 +275,8 @@ typedef struct { #define DT_SYMENT 11 /* Size of each symbol table entry. */ #define DT_INIT 12 /* Address of initialization function. */ #define DT_FINI 13 /* Address of finalization function. */ -#define DT_SONAME 14 /* String table offset of shared object - name. */ +/* String table offset of shared object name. */ +#define DT_SONAME 14 #define DT_RPATH 15 /* String table offset of library path. [sup] */ #define DT_SYMBOLIC 16 /* Indicates "symbolic" linking. [sup] */ #define DT_REL 17 /* Address of ElfNN_Rel relocations. */ @@ -285,30 +284,29 @@ typedef struct { #define DT_RELENT 19 /* Size of each ElfNN_Rel relocation. */ #define DT_PLTREL 20 /* Type of relocation used for PLT. */ #define DT_DEBUG 21 /* Reserved (not used). */ -#define DT_TEXTREL 22 /* Indicates there may be relocations in - non-writable segments. [sup] */ +/* Indicates there may be relocations in non-writable segments. [sup] */ +#define DT_TEXTREL 22 #define DT_JMPREL 23 /* Address of PLT relocations. */ #define DT_BIND_NOW 24 /* [sup] */ -#define DT_INIT_ARRAY 25 /* Address of the array of pointers to - initialization functions */ -#define DT_FINI_ARRAY 26 /* Address of the array of pointers to - termination functions */ -#define DT_INIT_ARRAYSZ 27 /* Size in bytes of the array of - initialization functions. */ -#define DT_FINI_ARRAYSZ 28 /* Size in bytes of the array of - terminationfunctions. */ -#define DT_RUNPATH 29 /* String table offset of a null-terminated - library search path string. */ +/* Address of the array of pointers to initialization functions */ +#define DT_INIT_ARRAY 25 +/* Address of the array of pointers to termination functions */ +#define DT_FINI_ARRAY 26 +/* Size in bytes of the array of initialization functions. */ +#define DT_INIT_ARRAYSZ 27 +/* Size in bytes of the array of terminationfunctions. */ +#define DT_FINI_ARRAYSZ 28 +/* String table offset of a null-terminated library search path string. */ +#define DT_RUNPATH 29 #define DT_FLAGS 30 /* Object specific flag values. */ -#define DT_ENCODING 32 /* Values greater than or equal to DT_ENCODING - and less than DT_LOOS follow the rules for - the interpretation of the d_un union - as follows: even == 'd_ptr', even == 'd_val' - or none */ -#define DT_PREINIT_ARRAY 32 /* Address of the array of pointers to - pre-initialization functions. */ -#define DT_PREINIT_ARRAYSZ 33 /* Size in bytes of the array of - pre-initialization functions. */ +/* Values greater than or equal to DT_ENCODING and less than + DT_LOOS follow the rules for the interpretation of the d_un + union as follows: even == 'd_ptr', even == 'd_val' or none */ +#define DT_ENCODING 32 +/* Address of the array of pointers to pre-initialization functions. */ +#define DT_PREINIT_ARRAY 32 +/* Size in bytes of the array of pre-initialization functions. */ +#define DT_PREINIT_ARRAYSZ 33 #define DT_LOOS 0x6000000d /* First OS-specific */ #define DT_HIOS 0x6ffff000 /* Last OS-specific */ #define DT_LOPROC 0x70000000 /* First processor-specific type. */ @@ -319,19 +317,19 @@ typedef struct { #define DT_VERSYM 0x6ffffff0 /* Values for DT_FLAGS */ -#define DF_ORIGIN 0x0001 /* Indicates that the object being loaded may - make reference to the $ORIGIN substitution - string */ +/* Indicates that the object being loaded may make reference to + the $ORIGIN substitution string */ +#define DF_ORIGIN 0x0001 #define DF_SYMBOLIC 0x0002 /* Indicates "symbolic" linking. */ -#define DF_TEXTREL 0x0004 /* Indicates there may be relocations in - non-writable segments. */ -#define DF_BIND_NOW 0x0008 /* Indicates that the dynamic linker should - process all relocations for the object - containing this entry before transferring - control to the program. */ -#define DF_STATIC_TLS 0x0010 /* Indicates that the shared object or - executable contains code using a static - thread-local storage scheme. */ +/* Indicates there may be relocations in non-writable segments. */ +#define DF_TEXTREL 0x0004 +/* Indicates that the dynamic linker should process all + relocations for the object containing this entry before + transferring control to the program. */ +#define DF_BIND_NOW 0x0008 +/* Indicates that the shared object or executable contains code + using a static thread-local storage scheme. */ +#define DF_STATIC_TLS 0x0010 /* Values for n_type. Used in core files. */ #define NT_PRSTATUS 1 /* Process status. */ diff --git a/src/cmd/ld/ldelf.c b/src/cmd/ld/ldelf.c index d61020e49..8334e988e 100644 --- a/src/cmd/ld/ldelf.c +++ b/src/cmd/ld/ldelf.c @@ -328,15 +328,16 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) Reloc *r, *rp; Sym *s; + USED(pkg); if(debug['v']) Bprint(&bso, "%5.2f ldelf %s\n", cputime(), pn); version++; base = Boffset(f); - if(Bread(f, &hdrbuf, sizeof hdrbuf) != sizeof hdrbuf) + if(Bread(f, hdrbuf, sizeof hdrbuf) != sizeof hdrbuf) goto bad; - hdr = (ElfHdrBytes*)&hdrbuf; + hdr = (ElfHdrBytes*)hdrbuf; if(memcmp(hdr->ident, ElfMagic, 4) != 0) goto bad; switch(hdr->ident[5]) { @@ -518,7 +519,7 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn) name = smprint("%s(%s)", pn, sect->name); s = lookup(name, version); free(name); - switch(sect->flags&(ElfSectFlagAlloc|ElfSectFlagWrite|ElfSectFlagExec)) { + switch((int)sect->flags&(ElfSectFlagAlloc|ElfSectFlagWrite|ElfSectFlagExec)) { default: werrstr("unexpected flags for ELF section %s", sect->name); goto bad; diff --git a/src/cmd/ld/ldmacho.c b/src/cmd/ld/ldmacho.c index bbb21d51a..abbc3b3cd 100644 --- a/src/cmd/ld/ldmacho.c +++ b/src/cmd/ld/ldmacho.c @@ -440,6 +440,7 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn) Reloc *r, *rp; char *name; + USED(pkg); version++; base = Boffset(f); if(Bread(f, hdr, sizeof hdr) != sizeof hdr) diff --git a/src/cmd/ld/ldpe.c b/src/cmd/ld/ldpe.c index d6aa267c4..98c866fee 100644 --- a/src/cmd/ld/ldpe.c +++ b/src/cmd/ld/ldpe.c @@ -125,10 +125,13 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) Sym *s; Reloc *r, *rp; PeSym *sym; - + + USED(len); + USED(pkg); if(debug['v']) Bprint(&bso, "%5.2f ldpe %s\n", cputime(), pn); + sect = nil; version++; base = Boffset(f); @@ -304,6 +307,8 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn) diag("%s: %s sectnum <0!", pn, s->name, sym->sectnum); } + if(sect == nil) + return; s->sub = sect->sym->sub; sect->sym->sub = s; s->type = sect->sym->type | SSUB; @@ -366,12 +371,11 @@ readsym(PeObj *obj, int i, PeSym **y) sym = &obj->pesym[i]; *y = sym; - s = nil; name = sym->name; if(sym->sclass == IMAGE_SYM_CLASS_STATIC && sym->value == 0) // section name = obj->sect[sym->sectnum-1].sym->name; - if(strncmp(sym->name, "__imp__", 6) == 0) + if(strncmp(sym->name, "__imp__", 7) == 0) name = &sym->name[7]; // __imp__Name => Name else if(sym->name[0] == '_') name = &sym->name[1]; // _Name => Name @@ -403,6 +407,8 @@ readsym(PeObj *obj, int i, PeSym **y) if(s != nil && s->type == 0 && !(sym->sclass == IMAGE_SYM_CLASS_STATIC && sym->value == 0)) s->type = SXREF; + if(strncmp(sym->name, "__imp__", 7) == 0) + s->got = -2; // flag for __imp__ sym->sym = s; return 0; diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 04ee790a4..77a62f5de 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -956,7 +956,7 @@ pclntab(void) uchar *bp; sym = lookup("pclntab", 0); - sym->type = SRODATA; + sym->type = SPCLNTAB; sym->reachable = 1; if(debug['s']) return; diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h index dfd18fbff..347987195 100644 --- a/src/cmd/ld/lib.h +++ b/src/cmd/ld/lib.h @@ -40,6 +40,8 @@ enum SSTRING, SGOSTRING, SRODATA, + SSYMTAB, + SPCLNTAB, SDATA, SMACHO, /* Mach-O __nl_symbol_ptr */ SMACHOGOT, @@ -260,10 +262,8 @@ enum { Hipaq, // ipaq Hdarwin, // Apple Mach-O Hlinux, // Linux ELF - Hnacl, // Google Native Client Hfreebsd, // FreeBSD ELF Hwindows, // MS Windows PE - Htiny // tiny (os image) }; typedef struct Header Header; diff --git a/src/cmd/ld/pe.c b/src/cmd/ld/pe.c index 91e15d343..9ac0a50d8 100644 --- a/src/cmd/ld/pe.c +++ b/src/cmd/ld/pe.c @@ -5,8 +5,6 @@ // PE (Portable Executable) file writing // http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx -#include <time.h> - #include "l.h" #include "../ld/lib.h" #include "../ld/pe.h" @@ -150,7 +148,7 @@ pewrite(void) ewrite(cout, &oh64, sizeof oh64); else ewrite(cout, &oh, sizeof oh); - ewrite(cout, &sh, nsect * sizeof sh[0]); + ewrite(cout, sh, nsect * sizeof sh[0]); } static void @@ -175,7 +173,7 @@ initdynimport(void) Sym *s, *dynamic; dr = nil; - + m = nil; for(s = allsym; s != S; s = s->allsym) { if(!s->reachable || !s->dynimpname || s->dynexport) continue; diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c index e3093b2aa..60e146b35 100644 --- a/src/cmd/ld/symtab.c +++ b/src/cmd/ld/symtab.c @@ -90,6 +90,7 @@ putelfsym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) { int bind, type, shndx, off; + USED(go); switch(t) { default: return; @@ -127,6 +128,10 @@ putplan9sym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) { int i; + USED(go); + USED(ver); + USED(size); + USED(x); switch(t) { case 'T': case 'L': @@ -252,6 +257,7 @@ putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ) int i, f, l; Reloc *rel; + USED(size); if(t == 'f') name++; l = 4; @@ -280,7 +286,6 @@ putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ) } scput(0); scput(0); - i++; } else { for(i=0; name[i]; i++) @@ -311,9 +316,9 @@ putsymb(Sym *s, char *name, int t, vlong v, vlong size, int ver, Sym *typ) return; } if(ver) - Bprint(&bso, "%c %.8llux %s<%d> %s\n", t, v, s, ver, typ ? typ->name : ""); + Bprint(&bso, "%c %.8llux %s<%d> %s\n", t, v, s->name, ver, typ ? typ->name : ""); else - Bprint(&bso, "%c %.8llux %s %s\n", t, v, s, typ ? typ->name : ""); + Bprint(&bso, "%c %.8llux %s %s\n", t, v, s->name, typ ? typ->name : ""); } } @@ -346,7 +351,7 @@ symtab(void) s->reachable = 1; symt = lookup("symtab", 0); - symt->type = SRODATA; + symt->type = SSYMTAB; symt->size = 0; symt->reachable = 1; @@ -367,5 +372,7 @@ symtab(void) } } + if(debug['s']) + return; genasmsym(putsymb); } diff --git a/src/cmd/nm/Makefile b/src/cmd/nm/Makefile index 383dbd973..81bc348de 100644 --- a/src/cmd/nm/Makefile +++ b/src/cmd/nm/Makefile @@ -12,7 +12,4 @@ TARG=6nm OFILES=\ nm.$O\ -LIB=\ - ../../../lib/libmach.a\ - include ../../Make.ccmd diff --git a/src/cmd/prof/Makefile b/src/cmd/prof/Makefile index e643f267c..8a1a2f308 100644 --- a/src/cmd/prof/Makefile +++ b/src/cmd/prof/Makefile @@ -13,9 +13,6 @@ TARG=6prof OFILES=\ main.$O\ -LIB=\ - ../../../lib/libmach.a\ - NOINSTALL=1 include ../../Make.ccmd diff --git a/src/env.bash b/src/env.bash index ca3ecebe8..f83012a26 100644 --- a/src/env.bash +++ b/src/env.bash @@ -39,13 +39,55 @@ if [ ! -d "$GOBIN" -a "$GOBIN" != "$GOROOT/bin" ]; then fi export OLDPATH=$PATH -export PATH="$GOBIN":/bin:/usr/bin:$PATH +export PATH="$GOBIN":$PATH MAKE=make if ! make --version 2>/dev/null | grep 'GNU Make' >/dev/null; then MAKE=gmake fi +PROGS=" + ar + awk + bash + bison + chmod + cp + cut + echo + egrep + gcc + grep + ls + mkdir + mv + pwd + rm + sed + sort + tee + touch + tr + true + uname + uniq +" + +for i in $PROGS; do + if ! which $i >/dev/null 2>&1; then + echo "Cannot find '$i' on search path." 1>&2 + echo "See http://golang.org/doc/install.html#ctools" 1>&2 + exit 1 + fi +done + +if bison --version 2>&1 | grep 'bison++' >/dev/null 2>&1; then + echo "Your system's 'bison' is bison++." + echo "Go needs the original bison instead." 1>&2 + echo "See http://golang.org/doc/install.html#ctools" 1>&2 + exit 1 +fi + # Tried to use . <($MAKE ...) here, but it cannot set environment # variables in the version of bash that ships with OS X. Amazing. eval $($MAKE --no-print-directory -f Make.inc go-env | egrep 'GOARCH|GOOS|GOHOSTARCH|GOHOSTOS|GO_ENV') diff --git a/src/lib9/Makefile b/src/lib9/Makefile index d222e2f53..28c97c9b4 100644 --- a/src/lib9/Makefile +++ b/src/lib9/Makefile @@ -116,5 +116,6 @@ GOROOT_FINAL?=$(GOROOT) $(HOST_CC) -c $(HOST_CFLAGS) $< goos.$O: goos.c - $(HOST_CC) -c $(HOST_CFLAGS) -DGOOS='"$(GOOS)"' -DGOARCH='"$(GOARCH)"' -DGOROOT='"$(GOROOT_FINAL)"' -DGOVERSION='"'"$$(../version.bash)"'"' $< + GOVERSION=`../version.bash` && \ + $(HOST_CC) -c $(HOST_CFLAGS) -DGOOS='"$(GOOS)"' -DGOARCH='"$(GOARCH)"' -DGOROOT='"$(GOROOT_FINAL)"' -DGOVERSION='"'"$$GOVERSION"'"' $< diff --git a/src/libmach/8db.c b/src/libmach/8db.c index 5a195baf8..5b3de69a5 100644 --- a/src/libmach/8db.c +++ b/src/libmach/8db.c @@ -1017,14 +1017,56 @@ static Optable optabDB[8+64] = [0x03] = { 0,0, "FMOVLP F0,%e" }, [0x05] = { 0,0, "FMOVX %e,F0" }, [0x07] = { 0,0, "FMOVXP F0,%e" }, -[0x08] = { 0,0, "FCMOVCC %f,F0" }, -[0x09] = { 0,0, "FCMOVNE %f,F0" }, -[0x0a] = { 0,0, "FCMOVHI %f,F0" }, -[0x0b] = { 0,0, "FCMOVNU %f,F0" }, -[0x0d] = { 0,0, "FUCOMI F0,%f" }, -[0x0e] = { 0,0, "FCOMI F0,%f" }, +[0x08] = { 0,0, "FCMOVCC F0,F0" }, /* Mod R/M = 11xx xxxx*/ +[0x09] = { 0,0, "FCMOVCC F1,F0" }, +[0x0a] = { 0,0, "FCMOVCC F2,F0" }, +[0x0b] = { 0,0, "FCMOVCC F3,F0" }, +[0x0c] = { 0,0, "FCMOVCC F4,F0" }, +[0x0d] = { 0,0, "FCMOVCC F5,F0" }, +[0x0e] = { 0,0, "FCMOVCC F6,F0" }, +[0x0f] = { 0,0, "FCMOVCC F7,F0" }, +[0x10] = { 0,0, "FCMOVNE F0,F0" }, +[0x11] = { 0,0, "FCMOVNE F1,F0" }, +[0x12] = { 0,0, "FCMOVNE F2,F0" }, +[0x13] = { 0,0, "FCMOVNE F3,F0" }, +[0x14] = { 0,0, "FCMOVNE F4,F0" }, +[0x15] = { 0,0, "FCMOVNE F5,F0" }, +[0x16] = { 0,0, "FCMOVNE F6,F0" }, +[0x17] = { 0,0, "FCMOVNE F7,F0" }, +[0x18] = { 0,0, "FCMOVHI F0,F0" }, +[0x19] = { 0,0, "FCMOVHI F1,F0" }, +[0x1a] = { 0,0, "FCMOVHI F2,F0" }, +[0x1b] = { 0,0, "FCMOVHI F3,F0" }, +[0x1c] = { 0,0, "FCMOVHI F4,F0" }, +[0x1d] = { 0,0, "FCMOVHI F5,F0" }, +[0x1e] = { 0,0, "FCMOVHI F6,F0" }, +[0x1f] = { 0,0, "FCMOVHI F7,F0" }, +[0x20] = { 0,0, "FCMOVNU F0,F0" }, +[0x21] = { 0,0, "FCMOVNU F1,F0" }, +[0x22] = { 0,0, "FCMOVNU F2,F0" }, +[0x23] = { 0,0, "FCMOVNU F3,F0" }, +[0x24] = { 0,0, "FCMOVNU F4,F0" }, +[0x25] = { 0,0, "FCMOVNU F5,F0" }, +[0x26] = { 0,0, "FCMOVNU F6,F0" }, +[0x27] = { 0,0, "FCMOVNU F7,F0" }, [0x2a] = { 0,0, "FCLEX" }, [0x2b] = { 0,0, "FINIT" }, +[0x30] = { 0,0, "FUCOMI F0,F0" }, +[0x31] = { 0,0, "FUCOMI F1,F0" }, +[0x32] = { 0,0, "FUCOMI F2,F0" }, +[0x33] = { 0,0, "FUCOMI F3,F0" }, +[0x34] = { 0,0, "FUCOMI F4,F0" }, +[0x35] = { 0,0, "FUCOMI F5,F0" }, +[0x36] = { 0,0, "FUCOMI F6,F0" }, +[0x37] = { 0,0, "FUCOMI F7,F0" }, +[0x38] = { 0,0, "FCOMI F0,F0" }, +[0x39] = { 0,0, "FCOMI F1,F0" }, +[0x3a] = { 0,0, "FCOMI F2,F0" }, +[0x3b] = { 0,0, "FCOMI F3,F0" }, +[0x3c] = { 0,0, "FCOMI F4,F0" }, +[0x3d] = { 0,0, "FCOMI F5,F0" }, +[0x3e] = { 0,0, "FCOMI F6,F0" }, +[0x3f] = { 0,0, "FCOMI F7,F0" }, }; static Optable optabDC[8+8] = diff --git a/src/libmach/darwin.c b/src/libmach/darwin.c index c443a4fba..63abde313 100644 --- a/src/libmach/darwin.c +++ b/src/libmach/darwin.c @@ -222,12 +222,21 @@ addpid(int pid, int force) // The excthread reads that port and signals // us if we are waiting on that thread. pthread_t p; + int err; excport = mach_reply_port(); pthread_mutex_init(&mu, nil); pthread_cond_init(&cond, nil); - pthread_create(&p, nil, excthread, nil); - pthread_create(&p, nil, waitthread, (void*)(uintptr)pid); + err = pthread_create(&p, nil, excthread, nil); + if (err != 0) { + fprint(2, "pthread_create failed: %s\n", strerror(err)); + abort(); + } + err = pthread_create(&p, nil, waitthread, (void*)(uintptr)pid); + if (err != 0) { + fprint(2, "pthread_create failed: %s\n", strerror(err)); + abort(); + } first = 0; } diff --git a/src/libmach/linux.c b/src/libmach/linux.c index 30b4da240..6ce18957f 100644 --- a/src/libmach/linux.c +++ b/src/libmach/linux.c @@ -238,8 +238,7 @@ fixup: PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXEC | - PTRACE_O_TRACEVFORKDONE | - PTRACE_O_TRACEEXIT; + PTRACE_O_TRACEVFORKDONE; if(ptrace(PTRACE_SETOPTIONS, tid, 0, (void*)flags) < 0) { fprint(2, "ptrace PTRACE_SETOPTIONS %d: %r\n", tid); return nil; @@ -358,6 +357,12 @@ wait1(int nohang) break; case PTRACE_EVENT_EXIT: + // We won't see this unless we set PTRACE_O_TRACEEXIT. + // The debuggers assume that a read or write on a Map + // will fail for a thread that has exited. This event + // breaks that assumption. It's not a big deal: we + // only lose the ability to see the register state at + // the time of exit. if(trace) fprint(2, "tid %d: exiting %#x\n", tid, status); t->state = Exiting; @@ -755,13 +760,19 @@ static int ptracerw(int type, int xtype, int isr, int pid, uvlong addr, void *v, uint n) { int i; - uintptr u; + uintptr u, a; uchar buf[sizeof(uintptr)]; for(i=0; i<n; i+=sizeof(uintptr)){ + // Tread carefully here. On recent versions of glibc, + // ptrace is a variadic function which means the third + // argument will be pushed onto the stack as a uvlong. + // This is fine on amd64 but will not work for 386. + // We must convert addr to a uintptr. + a = addr+i; if(isr){ errno = 0; - u = ptrace(type, pid, addr+i, 0); + u = ptrace(type, pid, a, 0); if(errno) goto ptraceerr; if(n-i >= sizeof(uintptr)) @@ -775,14 +786,14 @@ ptracerw(int type, int xtype, int isr, int pid, uvlong addr, void *v, uint n) u = *(uintptr*)((char*)v+i); else{ errno = 0; - u = ptrace(xtype, pid, addr+i, 0); + u = ptrace(xtype, pid, a, 0); if(errno) return -1; memmove(buf, &u, sizeof u); memmove(buf, (char*)v+i, n-i); memmove(&u, buf, sizeof u); } - if(ptrace(type, pid, addr+i, u) < 0) + if(ptrace(type, pid, a, u) < 0) goto ptraceerr; } } diff --git a/src/pkg/Makefile b/src/pkg/Makefile index 2d6b3d014..7338399c2 100644 --- a/src/pkg/Makefile +++ b/src/pkg/Makefile @@ -45,6 +45,7 @@ DIRS=\ crypto/ocsp\ crypto/openpgp\ crypto/openpgp/armor\ + crypto/openpgp/elgamal\ crypto/openpgp/error\ crypto/openpgp/packet\ crypto/openpgp/s2k\ @@ -61,6 +62,7 @@ DIRS=\ crypto/x509\ crypto/x509/pkix\ crypto/xtea\ + csv\ debug/dwarf\ debug/macho\ debug/elf\ @@ -80,6 +82,8 @@ DIRS=\ exp/eval\ exp/gui\ exp/gui/x11\ + exp/regexp/syntax\ + exp/template\ expvar\ flag\ fmt\ @@ -181,6 +185,36 @@ DIRS+=\ endif +ifeq ($(GOOS),plan9) +NOPLAN9BUILD=\ + crypto/tls\ + debug/proc\ + exp/gui/x11\ + expvar\ + http\ + http/cgi\ + http/fcgi\ + http/httptest\ + http/pprof\ + http/spdy\ + mail\ + mime/multipart\ + net\ + net/dict\ + net/textproto\ + netchan\ + os/signal\ + rpc\ + rpc/jsonrpc\ + smtp\ + syslog\ + websocket\ + ../cmd/godoc\ + ../cmd/goinstall\ + +DIRS:=$(filter-out $(NOPLAN9BUILD),$(DIRS)) +endif + NOTEST+=\ crypto\ crypto/openpgp/error\ @@ -206,8 +240,8 @@ NOTEST+=\ ../cmd/cgo\ ../cmd/ebnflint\ ../cmd/godoc\ + ../cmd/goinstall\ ../cmd/gotest\ - ../cmd/govet\ ../cmd/goyacc\ ../cmd/hgpatch\ diff --git a/src/pkg/archive/tar/reader.go b/src/pkg/archive/tar/reader.go index ad06b6dac..45d95c3df 100644 --- a/src/pkg/archive/tar/reader.go +++ b/src/pkg/archive/tar/reader.go @@ -16,7 +16,7 @@ import ( ) var ( - HeaderError os.Error = os.ErrorString("invalid tar header") + HeaderError = os.NewError("invalid tar header") ) // A Reader provides sequential access to the contents of a tar archive. diff --git a/src/pkg/asn1/asn1.go b/src/pkg/asn1/asn1.go index 2650ef2a2..655772931 100644 --- a/src/pkg/asn1/asn1.go +++ b/src/pkg/asn1/asn1.go @@ -149,7 +149,7 @@ func (b BitString) RightAlign() []byte { return a } -// parseBitString parses an ASN.1 bit string from the given byte array and returns it. +// parseBitString parses an ASN.1 bit string from the given byte slice and returns it. func parseBitString(bytes []byte) (ret BitString, err os.Error) { if len(bytes) == 0 { err = SyntaxError{"zero length BIT STRING"} @@ -227,7 +227,7 @@ type Enumerated int type Flag bool // parseBase128Int parses a base-128 encoded int from the given offset in the -// given byte array. It returns the value and the new offset. +// given byte slice. It returns the value and the new offset. func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err os.Error) { offset = initOffset for shifted := 0; offset < len(bytes); shifted++ { @@ -259,7 +259,7 @@ func parseUTCTime(bytes []byte) (ret *time.Time, err os.Error) { return } -// parseGeneralizedTime parses the GeneralizedTime from the given byte array +// parseGeneralizedTime parses the GeneralizedTime from the given byte slice // and returns the resulting time. func parseGeneralizedTime(bytes []byte) (ret *time.Time, err os.Error) { return time.Parse("20060102150405Z0700", string(bytes)) @@ -300,7 +300,7 @@ func isPrintable(b byte) bool { // IA5String // parseIA5String parses a ASN.1 IA5String (ASCII string) from the given -// byte array and returns it. +// byte slice and returns it. func parseIA5String(bytes []byte) (ret string, err os.Error) { for _, b := range bytes { if b >= 0x80 { @@ -315,11 +315,19 @@ func parseIA5String(bytes []byte) (ret string, err os.Error) { // T61String // parseT61String parses a ASN.1 T61String (8-bit clean string) from the given -// byte array and returns it. +// byte slice and returns it. func parseT61String(bytes []byte) (ret string, err os.Error) { return string(bytes), nil } +// UTF8String + +// parseUTF8String parses a ASN.1 UTF8String (raw UTF-8) from the given byte +// array and returns it. +func parseUTF8String(bytes []byte) (ret string, err os.Error) { + return string(bytes), nil +} + // A RawValue represents an undecoded ASN.1 object. type RawValue struct { Class, Tag int @@ -336,7 +344,7 @@ type RawContent []byte // Tagging // parseTagAndLength parses an ASN.1 tag and length pair from the given offset -// into a byte array. It returns the parsed data and the new offset. SET and +// into a byte slice. It returns the parsed data and the new offset. SET and // SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we // don't distinguish between ordered and unordered objects in this code. func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset int, err os.Error) { @@ -393,7 +401,7 @@ func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset i } // parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse -// a number of ASN.1 values from the given byte array and returns them as a +// a number of ASN.1 values from the given byte slice and returns them as a // slice of Go values of the given type. func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type) (ret reflect.Value, err os.Error) { expectedTag, compoundType, ok := getUniversalType(elemType) @@ -456,7 +464,7 @@ func invalidLength(offset, length, sliceLength int) bool { return offset+length < offset || offset+length > sliceLength } -// parseField is the main parsing function. Given a byte array and an offset +// parseField is the main parsing function. Given a byte slice and an offset // into the array, it will try to parse a suitable ASN.1 value out and store it // in the given Value. func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParameters) (offset int, err os.Error) { @@ -573,16 +581,15 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam } } - // Special case for strings: PrintableString and IA5String both map to - // the Go type string. getUniversalType returns the tag for - // PrintableString when it sees a string so, if we see an IA5String on - // the wire, we change the universal type to match. - if universalTag == tagPrintableString && t.tag == tagIA5String { - universalTag = tagIA5String - } - // Likewise for GeneralString - if universalTag == tagPrintableString && t.tag == tagGeneralString { - universalTag = tagGeneralString + // Special case for strings: all the ASN.1 string types map to the Go + // type string. getUniversalType returns the tag for PrintableString + // when it sees a string, so if we see a different string type on the + // wire, we change the universal type to match. + if universalTag == tagPrintableString { + switch t.tag { + case tagIA5String, tagGeneralString, tagT61String, tagUTF8String: + universalTag = t.tag + } } // Special case for time: UTCTime and GeneralizedTime both map to the @@ -707,7 +714,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam if i == 0 && field.Type == rawContentsType { continue } - innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag)) + innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag.Get("asn1"))) if err != nil { return } @@ -738,6 +745,8 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam v, err = parseIA5String(innerBytes) case tagT61String: v, err = parseT61String(innerBytes) + case tagUTF8String: + v, err = parseUTF8String(innerBytes) case tagGeneralString: // GeneralString is specified in ISO-2022/ECMA-35, // A brief review suggests that it includes structures diff --git a/src/pkg/asn1/asn1_test.go b/src/pkg/asn1/asn1_test.go index 463dbe026..3c9478618 100644 --- a/src/pkg/asn1/asn1_test.go +++ b/src/pkg/asn1/asn1_test.go @@ -299,11 +299,11 @@ type TestObjectIdentifierStruct struct { } type TestContextSpecificTags struct { - A int "tag:1" + A int `asn1:"tag:1"` } type TestContextSpecificTags2 struct { - A int "explicit,tag:1" + A int `asn1:"explicit,tag:1"` B int } @@ -353,7 +353,7 @@ type Certificate struct { } type TBSCertificate struct { - Version int "optional,explicit,default:0,tag:0" + Version int `asn1:"optional,explicit,default:0,tag:0"` SerialNumber RawValue SignatureAlgorithm AlgorithmIdentifier Issuer RDNSequence diff --git a/src/pkg/asn1/common.go b/src/pkg/asn1/common.go index 9db887e25..01f4f7b6e 100644 --- a/src/pkg/asn1/common.go +++ b/src/pkg/asn1/common.go @@ -25,6 +25,7 @@ const ( tagOctetString = 4 tagOID = 6 tagEnum = 10 + tagUTF8String = 12 tagSequence = 16 tagSet = 17 tagPrintableString = 19 @@ -83,7 +84,7 @@ type fieldParameters struct { // parseFieldParameters will parse it into a fieldParameters structure, // ignoring unknown parts of the string. func parseFieldParameters(str string) (ret fieldParameters) { - for _, part := range strings.Split(str, ",", -1) { + for _, part := range strings.Split(str, ",") { switch { case part == "optional": ret.optional = true diff --git a/src/pkg/asn1/marshal.go b/src/pkg/asn1/marshal.go index 7212c91ef..d7eb63bf8 100644 --- a/src/pkg/asn1/marshal.go +++ b/src/pkg/asn1/marshal.go @@ -413,7 +413,7 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter for i := startingField; i < t.NumField(); i++ { var pre *forkableWriter pre, out = out.fork() - err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag)) + err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag.Get("asn1"))) if err != nil { return } diff --git a/src/pkg/asn1/marshal_test.go b/src/pkg/asn1/marshal_test.go index a9517634d..03df5f1e1 100644 --- a/src/pkg/asn1/marshal_test.go +++ b/src/pkg/asn1/marshal_test.go @@ -30,23 +30,23 @@ type rawContentsStruct struct { } type implicitTagTest struct { - A int "implicit,tag:5" + A int `asn1:"implicit,tag:5"` } type explicitTagTest struct { - A int "explicit,tag:5" + A int `asn1:"explicit,tag:5"` } type ia5StringTest struct { - A string "ia5" + A string `asn1:"ia5"` } type printableStringTest struct { - A string "printable" + A string `asn1:"printable"` } type optionalRawValueTest struct { - A RawValue "optional" + A RawValue `asn1:"optional"` } type testSET []int diff --git a/src/pkg/big/int.go b/src/pkg/big/int.go index 22bdf8d2f..0948919cd 100755 --- a/src/pkg/big/int.go +++ b/src/pkg/big/int.go @@ -368,11 +368,60 @@ func (x *Int) Format(s fmt.State, ch int) { format = "0X%s" } } - if x.neg { - format = "-" + format + t := fmt.Sprintf(format, x.abs.string(cs)) + + // insert spaces in hexadecimal formats if needed + if len(t) > 0 && s.Flag(' ') && (ch == 'x' || ch == 'X') { + spaces := (len(t)+1)/2 - 1 + spaced := make([]byte, len(t)+spaces) + var i, j int + spaced[i] = t[j] + i++ + j++ + if len(t)&1 == 0 { + spaced[i] = t[j] + i++ + j++ + } + for j < len(t) { + spaced[i] = ' ' + i++ + spaced[i] = t[j] + i++ + j++ + spaced[i] = t[j] + i++ + j++ + } + t = string(spaced) + } + + // determine sign prefix + prefix := "" + switch { + case x.neg: + prefix = "-" + case s.Flag('+'): + prefix = "+" + case s.Flag(' ') && ch != 'x' && ch != 'X': + prefix = " " + } + + // fill to minimum width and prepend sign prefix + if width, ok := s.Width(); ok && len(t)+len(prefix) < width { + if s.Flag('0') { + t = fmt.Sprintf("%s%0*d%s", prefix, width-len(t)-len(prefix), 0, t) + } else { + if s.Flag('-') { + width = -width + } + t = fmt.Sprintf("%*s", width, prefix+t) + } + } else if prefix != "" { + t = prefix + t } - fmt.Fprintf(s, format, x.abs.string(cs)) + fmt.Fprint(s, t) } @@ -417,6 +466,7 @@ func (z *Int) scan(r io.RuneScanner, base int) (*Int, int, os.Error) { // the scanned number. It accepts the formats 'b' (binary), 'o' (octal), // 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal). func (z *Int) Scan(s fmt.ScanState, ch int) os.Error { + s.SkipSpace() // skip leading space characters base := 0 switch ch { case 'b': @@ -430,7 +480,7 @@ func (z *Int) Scan(s fmt.ScanState, ch int) os.Error { case 's', 'v': // let scan determine the base default: - return os.ErrorString("Int.Scan: invalid verb") + return os.NewError("Int.Scan: invalid verb") } _, _, err := z.scan(s, base) return err @@ -585,7 +635,7 @@ func ProbablyPrime(z *Int, n int) bool { } -// Rand sets z to a pseudo-random number in [0, n) and returns z. +// Rand sets z to a pseudo-random number in [0, n) and returns z. func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int { z.neg = false if n.neg == true || len(n.abs) == 0 { @@ -834,11 +884,11 @@ func (z *Int) GobEncode() ([]byte, os.Error) { // GobDecode implements the gob.GobDecoder interface. func (z *Int) GobDecode(buf []byte) os.Error { if len(buf) == 0 { - return os.ErrorString("Int.GobDecode: no data") + return os.NewError("Int.GobDecode: no data") } b := buf[0] if b>>1 != intGobVersion { - return os.ErrorString(fmt.Sprintf("Int.GobDecode: encoding version %d not supported", b>>1)) + return os.NewError(fmt.Sprintf("Int.GobDecode: encoding version %d not supported", b>>1)) } z.neg = b&1 != 0 z.abs = z.abs.setBytes(buf[1:]) diff --git a/src/pkg/big/int_test.go b/src/pkg/big/int_test.go index 58a55030d..7f33c9522 100755 --- a/src/pkg/big/int_test.go +++ b/src/pkg/big/int_test.go @@ -376,6 +376,35 @@ var formatTests = []struct { {"-10", "%#X", "-0XA"}, {"10", "%#y", "%!y(big.Int=10)"}, {"-10", "%#y", "%!y(big.Int=-10)"}, + + {"1234", "%d", "1234"}, + {"1234", "%3d", "1234"}, + {"1234", "%4d", "1234"}, + {"-1234", "%d", "-1234"}, + {"1234", "% 5d", " 1234"}, + {"1234", "%+5d", "+1234"}, + {"1234", "%-5d", "1234 "}, + {"1234", "%x", "4d2"}, + {"1234", "%X", "4D2"}, + {"1234", "% x", "4 d2"}, + {"-1234", "%3x", "-4d2"}, + {"-1234", "%4x", "-4d2"}, + {"-1234", "%5x", " -4d2"}, + {"-1234", "%-5x", "-4d2 "}, + {"-1234", "% x", "-4 d2"}, + {"1234", "%03d", "1234"}, + {"1234", "%04d", "1234"}, + {"1234", "%05d", "01234"}, + {"1234", "%06d", "001234"}, + {"-1234", "%06d", "-01234"}, + {"1234", "%+06d", "+01234"}, + {"1234", "% 06d", " 01234"}, + {"1234", "%-6d", "1234 "}, + {"1234", "%-06d", "001234"}, + {"-1234", "%-06d", "-01234"}, + {"10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", // 10**100 + "% x", + "12 49 ad 25 94 c3 7c eb 0b 27 84 c4 ce 0b f3 8a ce 40 8e 21 1a 7c aa b2 43 08 a8 2e 8f 10 00 00 00 00 00 00 00 00 00 00 00 00"}, } @@ -391,7 +420,7 @@ func TestFormat(t *testing.T) { } output := fmt.Sprintf(test.format, x) if output != test.output { - t.Errorf("#%d got %s; want %s", i, output, test.output) + t.Errorf("#%d got %q; want %q", i, output, test.output) } } } diff --git a/src/pkg/big/nat.go b/src/pkg/big/nat.go index 734568e06..6755832be 100755 --- a/src/pkg/big/nat.go +++ b/src/pkg/big/nat.go @@ -644,7 +644,7 @@ func hexValue(ch int) Word { func (z nat) scan(r io.RuneScanner, base int) (nat, int, os.Error) { // reject illegal bases if base < 0 || base == 1 || MaxBase < base { - return z, 0, os.ErrorString("illegal number base") + return z, 0, os.NewError("illegal number base") } // one char look-ahead @@ -721,7 +721,7 @@ func (z nat) scan(r io.RuneScanner, base int) (nat, int, os.Error) { return z, 10, nil case base != 0 || b != 8: // there was neither a mantissa digit nor the octal prefix 0 - return z, int(b), os.ErrorString("syntax error scanning number") + return z, int(b), os.NewError("syntax error scanning number") } return z.norm(), int(b), nil diff --git a/src/pkg/big/rat.go b/src/pkg/big/rat.go index 1fbf8c459..b61cbb966 100644 --- a/src/pkg/big/rat.go +++ b/src/pkg/big/rat.go @@ -227,10 +227,10 @@ func (z *Rat) Scan(s fmt.ScanState, ch int) os.Error { return err } if strings.IndexRune("efgEFGv", ch) < 0 { - return os.ErrorString("Rat.Scan: invalid verb") + return os.NewError("Rat.Scan: invalid verb") } if _, ok := z.SetString(string(tok)); !ok { - return os.ErrorString("Rat.Scan: invalid syntax") + return os.NewError("Rat.Scan: invalid syntax") } return nil } @@ -368,7 +368,7 @@ func (z *Rat) GobEncode() ([]byte, os.Error) { n := i - j if int(uint32(n)) != n { // this should never happen - return nil, os.ErrorString("Rat.GobEncode: numerator too large") + return nil, os.NewError("Rat.GobEncode: numerator too large") } binary.BigEndian.PutUint32(buf[j-4:j], uint32(n)) j -= 1 + 4 @@ -384,11 +384,11 @@ func (z *Rat) GobEncode() ([]byte, os.Error) { // GobDecode implements the gob.GobDecoder interface. func (z *Rat) GobDecode(buf []byte) os.Error { if len(buf) == 0 { - return os.ErrorString("Rat.GobDecode: no data") + return os.NewError("Rat.GobDecode: no data") } b := buf[0] if b>>1 != ratGobVersion { - return os.ErrorString(fmt.Sprintf("Rat.GobDecode: encoding version %d not supported", b>>1)) + return os.NewError(fmt.Sprintf("Rat.GobDecode: encoding version %d not supported", b>>1)) } const j = 1 + 4 i := j + binary.BigEndian.Uint32(buf[j-4:j]) diff --git a/src/pkg/bufio/bufio.go b/src/pkg/bufio/bufio.go index eaae8bb42..cb2667b28 100644 --- a/src/pkg/bufio/bufio.go +++ b/src/pkg/bufio/bufio.go @@ -22,9 +22,11 @@ const ( // Errors introduced by this package. type Error struct { - os.ErrorString + ErrorString string } +func (err *Error) String() string { return err.ErrorString } + var ( ErrInvalidUnreadByte os.Error = &Error{"bufio: invalid use of UnreadByte"} ErrInvalidUnreadRune os.Error = &Error{"bufio: invalid use of UnreadRune"} @@ -101,6 +103,12 @@ func (b *Reader) fill() { } } +func (b *Reader) readErr() os.Error { + err := b.err + b.err = nil + return err +} + // Peek returns the next n bytes without advancing the reader. The bytes stop // being valid at the next read call. If Peek returns fewer than n bytes, it // also returns an error explaining why the read is short. The error is @@ -119,7 +127,7 @@ func (b *Reader) Peek(n int) ([]byte, os.Error) { if m > n { m = n } - err := b.err + err := b.readErr() if m < n && err == nil { err = ErrBufferFull } @@ -134,11 +142,11 @@ func (b *Reader) Peek(n int) ([]byte, os.Error) { func (b *Reader) Read(p []byte) (n int, err os.Error) { n = len(p) if n == 0 { - return 0, b.err + return 0, b.readErr() } if b.w == b.r { if b.err != nil { - return 0, b.err + return 0, b.readErr() } if len(p) >= len(b.buf) { // Large read, empty buffer. @@ -148,11 +156,11 @@ func (b *Reader) Read(p []byte) (n int, err os.Error) { b.lastByte = int(p[n-1]) b.lastRuneSize = -1 } - return n, b.err + return n, b.readErr() } b.fill() if b.w == b.r { - return 0, b.err + return 0, b.readErr() } } @@ -172,7 +180,7 @@ func (b *Reader) ReadByte() (c byte, err os.Error) { b.lastRuneSize = -1 for b.w == b.r { if b.err != nil { - return 0, b.err + return 0, b.readErr() } b.fill() } @@ -208,7 +216,7 @@ func (b *Reader) ReadRune() (rune int, size int, err os.Error) { } b.lastRuneSize = -1 if b.r == b.w { - return 0, 0, b.err + return 0, 0, b.readErr() } rune, size = int(b.buf[b.r]), 1 if rune >= 0x80 { @@ -260,7 +268,7 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err os.Error) { if b.err != nil { line := b.buf[b.r:b.w] b.r = b.w - return line, b.err + return line, b.readErr() } n := b.Buffered() diff --git a/src/pkg/bufio/bufio_test.go b/src/pkg/bufio/bufio_test.go index 123adac29..5709213c8 100644 --- a/src/pkg/bufio/bufio_test.go +++ b/src/pkg/bufio/bufio_test.go @@ -53,11 +53,12 @@ func readBytes(buf *Reader) string { if e == os.EOF { break } - if e != nil { + if e == nil { + b[nb] = c + nb++ + } else if e != iotest.ErrTimeout { panic("Data: " + e.String()) } - b[nb] = c - nb++ } return string(b[0:nb]) } @@ -86,6 +87,7 @@ var readMakers = []readMaker{ {"byte", iotest.OneByteReader}, {"half", iotest.HalfReader}, {"data+err", iotest.DataErrReader}, + {"timeout", iotest.TimeoutReader}, } // Call ReadString (which ends up calling everything else) @@ -97,7 +99,7 @@ func readLines(b *Reader) string { if e == os.EOF { break } - if e != nil { + if e != nil && e != iotest.ErrTimeout { panic("GetLines: " + e.String()) } s += s1 diff --git a/src/pkg/bytes/buffer.go b/src/pkg/bytes/buffer.go index 1acd4e05c..5de86105d 100644 --- a/src/pkg/bytes/buffer.go +++ b/src/pkg/bytes/buffer.go @@ -280,7 +280,7 @@ func (b *Buffer) ReadRune() (r int, size int, err os.Error) { // from any read operation.) func (b *Buffer) UnreadRune() os.Error { if b.lastRead != opReadRune { - return os.ErrorString("bytes.Buffer: UnreadRune: previous operation was not ReadRune") + return os.NewError("bytes.Buffer: UnreadRune: previous operation was not ReadRune") } b.lastRead = opInvalid if b.off > 0 { @@ -295,7 +295,7 @@ func (b *Buffer) UnreadRune() os.Error { // returns an error. func (b *Buffer) UnreadByte() os.Error { if b.lastRead != opReadRune && b.lastRead != opRead { - return os.ErrorString("bytes.Buffer: UnreadByte: previous operation was not a read") + return os.NewError("bytes.Buffer: UnreadByte: previous operation was not a read") } b.lastRead = opInvalid if b.off > 0 { diff --git a/src/pkg/bytes/bytes.go b/src/pkg/bytes/bytes.go index 0f9ac9863..3cec60f96 100644 --- a/src/pkg/bytes/bytes.go +++ b/src/pkg/bytes/bytes.go @@ -212,26 +212,40 @@ func genSplit(s, sep []byte, sepSave, n int) [][]byte { return a[0 : na+1] } -// Split slices s into subslices separated by sep and returns a slice of +// SplitN slices s into subslices separated by sep and returns a slice of // the subslices between those separators. -// If sep is empty, Split splits after each UTF-8 sequence. +// If sep is empty, SplitN splits after each UTF-8 sequence. // The count determines the number of subslices to return: // n > 0: at most n subslices; the last subslice will be the unsplit remainder. // n == 0: the result is nil (zero subslices) // n < 0: all subslices -func Split(s, sep []byte, n int) [][]byte { return genSplit(s, sep, 0, n) } +func SplitN(s, sep []byte, n int) [][]byte { return genSplit(s, sep, 0, n) } -// SplitAfter slices s into subslices after each instance of sep and +// SplitAfterN slices s into subslices after each instance of sep and // returns a slice of those subslices. -// If sep is empty, Split splits after each UTF-8 sequence. +// If sep is empty, SplitAfterN splits after each UTF-8 sequence. // The count determines the number of subslices to return: // n > 0: at most n subslices; the last subslice will be the unsplit remainder. // n == 0: the result is nil (zero subslices) // n < 0: all subslices -func SplitAfter(s, sep []byte, n int) [][]byte { +func SplitAfterN(s, sep []byte, n int) [][]byte { return genSplit(s, sep, len(sep), n) } +// Split slices s into all subslices separated by sep and returns a slice of +// the subslices between those separators. +// If sep is empty, Split splits after each UTF-8 sequence. +// It is equivalent to SplitN with a count of -1. +func Split(s, sep []byte) [][]byte { return genSplit(s, sep, 0, -1) } + +// SplitAfter slices s into all subslices after each instance of sep and +// returns a slice of those subslices. +// If sep is empty, SplitAfter splits after each UTF-8 sequence. +// It is equivalent to SplitAfterN with a count of -1. +func SplitAfter(s, sep []byte) [][]byte { + return genSplit(s, sep, len(sep), -1) +} + // Fields splits the array s around each instance of one or more consecutive white space // characters, returning a slice of subarrays of s or an empty list if s contains only white space. func Fields(s []byte) [][]byte { diff --git a/src/pkg/bytes/bytes_test.go b/src/pkg/bytes/bytes_test.go index 4ce291a4f..753935309 100644 --- a/src/pkg/bytes/bytes_test.go +++ b/src/pkg/bytes/bytes_test.go @@ -6,6 +6,7 @@ package bytes_test import ( . "bytes" + "reflect" "testing" "unicode" "utf8" @@ -315,7 +316,7 @@ var explodetests = []ExplodeTest{ func TestExplode(t *testing.T) { for _, tt := range explodetests { - a := Split([]byte(tt.s), nil, tt.n) + a := SplitN([]byte(tt.s), nil, tt.n) result := arrayOfString(a) if !eq(result, tt.a) { t.Errorf(`Explode("%s", %d) = %v; want %v`, tt.s, tt.n, result, tt.a) @@ -354,7 +355,7 @@ var splittests = []SplitTest{ func TestSplit(t *testing.T) { for _, tt := range splittests { - a := Split([]byte(tt.s), []byte(tt.sep), tt.n) + a := SplitN([]byte(tt.s), []byte(tt.sep), tt.n) result := arrayOfString(a) if !eq(result, tt.a) { t.Errorf(`Split(%q, %q, %d) = %v; want %v`, tt.s, tt.sep, tt.n, result, tt.a) @@ -367,6 +368,12 @@ func TestSplit(t *testing.T) { if string(s) != tt.s { t.Errorf(`Join(Split(%q, %q, %d), %q) = %q`, tt.s, tt.sep, tt.n, tt.sep, s) } + if tt.n < 0 { + b := Split([]byte(tt.s), []byte(tt.sep)) + if !reflect.DeepEqual(a, b) { + t.Errorf("Split disagrees withSplitN(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, b, a) + } + } } } @@ -388,7 +395,7 @@ var splitaftertests = []SplitTest{ func TestSplitAfter(t *testing.T) { for _, tt := range splitaftertests { - a := SplitAfter([]byte(tt.s), []byte(tt.sep), tt.n) + a := SplitAfterN([]byte(tt.s), []byte(tt.sep), tt.n) result := arrayOfString(a) if !eq(result, tt.a) { t.Errorf(`Split(%q, %q, %d) = %v; want %v`, tt.s, tt.sep, tt.n, result, tt.a) @@ -398,6 +405,12 @@ func TestSplitAfter(t *testing.T) { if string(s) != tt.s { t.Errorf(`Join(Split(%q, %q, %d), %q) = %q`, tt.s, tt.sep, tt.n, tt.sep, s) } + if tt.n < 0 { + b := SplitAfter([]byte(tt.s), []byte(tt.sep)) + if !reflect.DeepEqual(a, b) { + t.Errorf("SplitAfter disagrees withSplitAfterN(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, b, a) + } + } } } diff --git a/src/pkg/compress/gzip/gunzip.go b/src/pkg/compress/gzip/gunzip.go index b0ddc81d2..6ac9293d7 100644 --- a/src/pkg/compress/gzip/gunzip.go +++ b/src/pkg/compress/gzip/gunzip.go @@ -36,8 +36,8 @@ func makeReader(r io.Reader) flate.Reader { return bufio.NewReader(r) } -var HeaderError os.Error = os.ErrorString("invalid gzip header") -var ChecksumError os.Error = os.ErrorString("gzip checksum error") +var HeaderError = os.NewError("invalid gzip header") +var ChecksumError = os.NewError("gzip checksum error") // The gzip file stores a header giving metadata about the compressed file. // That header is exposed as the fields of the Compressor and Decompressor structs. diff --git a/src/pkg/compress/lzw/reader_test.go b/src/pkg/compress/lzw/reader_test.go index 72121a6b5..f8042b0d1 100644 --- a/src/pkg/compress/lzw/reader_test.go +++ b/src/pkg/compress/lzw/reader_test.go @@ -84,7 +84,7 @@ var lzwTests = []lzwTest{ func TestReader(t *testing.T) { b := bytes.NewBuffer(nil) for _, tt := range lzwTests { - d := strings.Split(tt.desc, ";", -1) + d := strings.Split(tt.desc, ";") var order Order switch d[1] { case "LSB": diff --git a/src/pkg/compress/zlib/reader.go b/src/pkg/compress/zlib/reader.go index 8a3ef1580..78dabdf4d 100644 --- a/src/pkg/compress/zlib/reader.go +++ b/src/pkg/compress/zlib/reader.go @@ -34,9 +34,9 @@ import ( const zlibDeflate = 8 -var ChecksumError os.Error = os.ErrorString("zlib checksum error") -var HeaderError os.Error = os.ErrorString("invalid zlib header") -var DictionaryError os.Error = os.ErrorString("invalid zlib dictionary") +var ChecksumError = os.NewError("zlib checksum error") +var HeaderError = os.NewError("invalid zlib header") +var DictionaryError = os.NewError("invalid zlib dictionary") type reader struct { r flate.Reader diff --git a/src/pkg/crypto/aes/cipher.go b/src/pkg/crypto/aes/cipher.go index 3a9d02318..73223531e 100644 --- a/src/pkg/crypto/aes/cipher.go +++ b/src/pkg/crypto/aes/cipher.go @@ -45,14 +45,14 @@ func NewCipher(key []byte) (*Cipher, os.Error) { // BlockSize returns the AES block size, 16 bytes. // It is necessary to satisfy the Cipher interface in the -// package "crypto/block". +// package "crypto/cipher". func (c *Cipher) BlockSize() int { return BlockSize } // Encrypt encrypts the 16-byte buffer src using the key k // and stores the result in dst. // Note that for amounts of data larger than a block, // it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/block/cbc.go). +// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). func (c *Cipher) Encrypt(dst, src []byte) { encryptBlock(c.enc, dst, src) } // Decrypt decrypts the 16-byte buffer src using the key k diff --git a/src/pkg/crypto/blowfish/cipher.go b/src/pkg/crypto/blowfish/cipher.go index f3c5175ac..6c37dfe94 100644 --- a/src/pkg/crypto/blowfish/cipher.go +++ b/src/pkg/crypto/blowfish/cipher.go @@ -42,14 +42,14 @@ func NewCipher(key []byte) (*Cipher, os.Error) { // BlockSize returns the Blowfish block size, 8 bytes. // It is necessary to satisfy the Cipher interface in the -// package "crypto/block". +// package "crypto/cipher". func (c *Cipher) BlockSize() int { return BlockSize } // Encrypt encrypts the 8-byte buffer src using the key k // and stores the result in dst. // Note that for amounts of data larger than a block, // it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/block/cbc.go). +// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). func (c *Cipher) Encrypt(dst, src []byte) { l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7]) diff --git a/src/pkg/crypto/cast5/cast5.go b/src/pkg/crypto/cast5/cast5.go index cb62e3132..e9d4a24e2 100644 --- a/src/pkg/crypto/cast5/cast5.go +++ b/src/pkg/crypto/cast5/cast5.go @@ -20,7 +20,7 @@ type Cipher struct { func NewCipher(key []byte) (c *Cipher, err os.Error) { if len(key) != KeySize { - return nil, os.ErrorString("CAST5: keys must be 16 bytes") + return nil, os.NewError("CAST5: keys must be 16 bytes") } c = new(Cipher) diff --git a/src/pkg/crypto/dsa/dsa.go b/src/pkg/crypto/dsa/dsa.go index f0af8bb42..a5f96fe94 100644 --- a/src/pkg/crypto/dsa/dsa.go +++ b/src/pkg/crypto/dsa/dsa.go @@ -79,7 +79,7 @@ func GenerateParameters(params *Parameters, rand io.Reader, sizes ParameterSizes L = 3072 N = 256 default: - return os.ErrorString("crypto/dsa: invalid ParameterSizes") + return os.NewError("crypto/dsa: invalid ParameterSizes") } qBytes := make([]byte, N/8) @@ -158,7 +158,7 @@ GeneratePrimes: // PrivateKey must already be valid (see GenerateParameters). func GenerateKey(priv *PrivateKey, rand io.Reader) os.Error { if priv.P == nil || priv.Q == nil || priv.G == nil { - return os.ErrorString("crypto/dsa: parameters not set up before generating key") + return os.NewError("crypto/dsa: parameters not set up before generating key") } x := new(big.Int) diff --git a/src/pkg/crypto/ocsp/ocsp.go b/src/pkg/crypto/ocsp/ocsp.go index 57dbe7d2d..e725bded8 100644 --- a/src/pkg/crypto/ocsp/ocsp.go +++ b/src/pkg/crypto/ocsp/ocsp.go @@ -43,7 +43,7 @@ type certID struct { type responseASN1 struct { Status asn1.Enumerated - Response responseBytes "explicit,tag:0" + Response responseBytes `asn1:"explicit,tag:0"` } type responseBytes struct { @@ -55,30 +55,30 @@ type basicResponse struct { TBSResponseData responseData SignatureAlgorithm pkix.AlgorithmIdentifier Signature asn1.BitString - Certificates []asn1.RawValue "explicit,tag:0,optional" + Certificates []asn1.RawValue `asn1:"explicit,tag:0,optional"` } type responseData struct { Raw asn1.RawContent - Version int "optional,default:1,explicit,tag:0" - RequestorName pkix.RDNSequence "optional,explicit,tag:1" - KeyHash []byte "optional,explicit,tag:2" + Version int `asn1:"optional,default:1,explicit,tag:0"` + RequestorName pkix.RDNSequence `asn1:"optional,explicit,tag:1"` + KeyHash []byte `asn1:"optional,explicit,tag:2"` ProducedAt *time.Time Responses []singleResponse } type singleResponse struct { CertID certID - Good asn1.Flag "explicit,tag:0,optional" - Revoked revokedInfo "explicit,tag:1,optional" - Unknown asn1.Flag "explicit,tag:2,optional" + Good asn1.Flag `asn1:"explicit,tag:0,optional"` + Revoked revokedInfo `asn1:"explicit,tag:1,optional"` + Unknown asn1.Flag `asn1:"explicit,tag:2,optional"` ThisUpdate *time.Time - NextUpdate *time.Time "explicit,tag:0,optional" + NextUpdate *time.Time `asn1:"explicit,tag:0,optional"` } type revokedInfo struct { RevocationTime *time.Time - Reason int "explicit,tag:0,optional" + Reason int `asn1:"explicit,tag:0,optional"` } // This is the exposed reflection of the internal OCSP structures. diff --git a/src/pkg/crypto/openpgp/elgamal/Makefile b/src/pkg/crypto/openpgp/elgamal/Makefile new file mode 100644 index 000000000..f730255f8 --- /dev/null +++ b/src/pkg/crypto/openpgp/elgamal/Makefile @@ -0,0 +1,11 @@ +# Copyright 2011 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../../../Make.inc + +TARG=crypto/openpgp/elgamal +GOFILES=\ + elgamal.go\ + +include ../../../../Make.pkg diff --git a/src/pkg/crypto/openpgp/elgamal/elgamal.go b/src/pkg/crypto/openpgp/elgamal/elgamal.go new file mode 100644 index 000000000..99a6e3e1f --- /dev/null +++ b/src/pkg/crypto/openpgp/elgamal/elgamal.go @@ -0,0 +1,122 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package elgamal implements ElGamal encryption, suitable for OpenPGP, +// as specified in "A Public-Key Cryptosystem and a Signature Scheme Based on +// Discrete Logarithms," IEEE Transactions on Information Theory, v. IT-31, +// n. 4, 1985, pp. 469-472. +// +// This form of ElGamal embeds PKCS#1 v1.5 padding, which may make it +// unsuitable for other protocols. RSA should be used in preference in any +// case. +package elgamal + +import ( + "big" + "crypto/rand" + "crypto/subtle" + "io" + "os" +) + +// PublicKey represents an ElGamal public key. +type PublicKey struct { + G, P, Y *big.Int +} + +// PrivateKey represents an ElGamal private key. +type PrivateKey struct { + PublicKey + X *big.Int +} + +// Encrypt encrypts the given message to the given public key. The result is a +// pair of integers. Errors can result from reading random, or because msg is +// too large to be encrypted to the public key. +func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err os.Error) { + pLen := (pub.P.BitLen() + 7) / 8 + if len(msg) > pLen-11 { + err = os.NewError("elgamal: message too long") + return + } + + // EM = 0x02 || PS || 0x00 || M + em := make([]byte, pLen-1) + em[0] = 2 + ps, mm := em[1:len(em)-len(msg)-1], em[len(em)-len(msg):] + err = nonZeroRandomBytes(ps, random) + if err != nil { + return + } + em[len(em)-len(msg)-1] = 0 + copy(mm, msg) + + m := new(big.Int).SetBytes(em) + + k, err := rand.Int(random, pub.P) + if err != nil { + return + } + + c1 = new(big.Int).Exp(pub.G, k, pub.P) + s := new(big.Int).Exp(pub.Y, k, pub.P) + c2 = s.Mul(s, m) + c2.Mod(c2, pub.P) + + return +} + +// Decrypt takes two integers, resulting from an ElGamal encryption, and +// returns the plaintext of the message. An error can result only if the +// ciphertext is invalid. Users should keep in mind that this is a padding +// oracle and thus, if exposed to an adaptive chosen ciphertext attack, can +// be used to break the cryptosystem. See ``Chosen Ciphertext Attacks +// Against Protocols Based on the RSA Encryption Standard PKCS #1'', Daniel +// Bleichenbacher, Advances in Cryptology (Crypto '98), +func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err os.Error) { + s := new(big.Int).Exp(c1, priv.X, priv.P) + s.ModInverse(s, priv.P) + s.Mul(s, c2) + s.Mod(s, priv.P) + em := s.Bytes() + + firstByteIsTwo := subtle.ConstantTimeByteEq(em[0], 2) + + // The remainder of the plaintext must be a string of non-zero random + // octets, followed by a 0, followed by the message. + // lookingForIndex: 1 iff we are still looking for the zero. + // index: the offset of the first zero byte. + var lookingForIndex, index int + lookingForIndex = 1 + + for i := 1; i < len(em); i++ { + equals0 := subtle.ConstantTimeByteEq(em[i], 0) + index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index) + lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex) + } + + if firstByteIsTwo != 1 || lookingForIndex != 0 || index < 9 { + return nil, os.NewError("elgamal: decryption error") + } + return em[index+1:], nil +} + +// nonZeroRandomBytes fills the given slice with non-zero random octets. +func nonZeroRandomBytes(s []byte, rand io.Reader) (err os.Error) { + _, err = io.ReadFull(rand, s) + if err != nil { + return + } + + for i := 0; i < len(s); i++ { + for s[i] == 0 { + _, err = io.ReadFull(rand, s[i:i+1]) + if err != nil { + return + } + } + } + + return +} diff --git a/src/pkg/crypto/openpgp/elgamal/elgamal_test.go b/src/pkg/crypto/openpgp/elgamal/elgamal_test.go new file mode 100644 index 000000000..101121aa6 --- /dev/null +++ b/src/pkg/crypto/openpgp/elgamal/elgamal_test.go @@ -0,0 +1,49 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package elgamal + +import ( + "big" + "bytes" + "crypto/rand" + "testing" +) + +// This is the 1024-bit MODP group from RFC 5114, section 2.1: +const primeHex = "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C69A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C013ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD7098488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708DF1FB2BC2E4A4371" + +const generatorHex = "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507FD6406CFF14266D31266FEA1E5C41564B777E690F5504F213160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28AD662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24855E6EEB22B3B2E5" + +func fromHex(hex string) *big.Int { + n, ok := new(big.Int).SetString(hex, 16) + if !ok { + panic("failed to parse hex number") + } + return n +} + +func TestEncryptDecrypt(t *testing.T) { + priv := &PrivateKey{ + PublicKey: PublicKey{ + G: fromHex(generatorHex), + P: fromHex(primeHex), + }, + X: fromHex("42"), + } + priv.Y = new(big.Int).Exp(priv.G, priv.X, priv.P) + + message := []byte("hello world") + c1, c2, err := Encrypt(rand.Reader, &priv.PublicKey, message) + if err != nil { + t.Errorf("error encrypting: %s", err) + } + message2, err := Decrypt(priv, c1, c2) + if err != nil { + t.Errorf("error decrypting: %s", err) + } + if !bytes.Equal(message2, message) { + t.Errorf("decryption failed, got: %x, want: %x", message2, message) + } +} diff --git a/src/pkg/crypto/openpgp/keys.go b/src/pkg/crypto/openpgp/keys.go index 2acb7e612..c70fb7927 100644 --- a/src/pkg/crypto/openpgp/keys.go +++ b/src/pkg/crypto/openpgp/keys.go @@ -12,6 +12,7 @@ import ( "crypto/rsa" "io" "os" + "time" ) // PublicKeyType is the armor type for a PGP public key. @@ -64,6 +65,78 @@ type KeyRing interface { DecryptionKeys() []Key } +// primaryIdentity returns the Identity marked as primary or the first identity +// if none are so marked. +func (e *Entity) primaryIdentity() *Identity { + var firstIdentity *Identity + for _, ident := range e.Identities { + if firstIdentity == nil { + firstIdentity = ident + } + if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId { + return ident + } + } + return firstIdentity +} + +// encryptionKey returns the best candidate Key for encrypting a message to the +// given Entity. +func (e *Entity) encryptionKey() Key { + candidateSubkey := -1 + + for i, subkey := range e.Subkeys { + if subkey.Sig.FlagsValid && subkey.Sig.FlagEncryptCommunications && subkey.PublicKey.PubKeyAlgo.CanEncrypt() { + candidateSubkey = i + break + } + } + + i := e.primaryIdentity() + + if e.PrimaryKey.PubKeyAlgo.CanEncrypt() { + // If we don't have any candidate subkeys for encryption and + // the primary key doesn't have any usage metadata then we + // assume that the primary key is ok. Or, if the primary key is + // marked as ok to encrypt to, then we can obviously use it. + if candidateSubkey == -1 && !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications && i.SelfSignature.FlagsValid { + return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature} + } + } + + if candidateSubkey != -1 { + subkey := e.Subkeys[candidateSubkey] + return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig} + } + + // This Entity appears to be signing only. + return Key{} +} + +// signingKey return the best candidate Key for signing a message with this +// Entity. +func (e *Entity) signingKey() Key { + candidateSubkey := -1 + + for i, subkey := range e.Subkeys { + if subkey.Sig.FlagsValid && subkey.Sig.FlagSign && subkey.PublicKey.PubKeyAlgo.CanSign() { + candidateSubkey = i + break + } + } + + i := e.primaryIdentity() + + // If we have no candidate subkey then we assume that it's ok to sign + // with the primary key. + if candidateSubkey == -1 || i.SelfSignature.FlagsValid && i.SelfSignature.FlagSign { + return Key{e, e.PrimaryKey, e.PrivateKey, i.SelfSignature} + } + + subkey := e.Subkeys[candidateSubkey] + return Key{e, subkey.PublicKey, subkey.PrivateKey, subkey.Sig} +} + // An EntityList contains one or more Entities. type EntityList []*Entity @@ -199,6 +272,10 @@ func readEntity(packets *packet.Reader) (*Entity, os.Error) { } } + if !e.PrimaryKey.PubKeyAlgo.CanSign() { + return nil, error.StructuralError("primary key cannot be used for signatures") + } + var current *Identity EachPacket: for { @@ -229,7 +306,7 @@ EachPacket: return nil, error.StructuralError("user ID packet not followed by self-signature") } - if sig.SigType == packet.SigTypePositiveCert && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId { + if (sig.SigType == packet.SigTypePositiveCert || sig.SigType == packet.SigTypeGenericCert) && sig.IssuerKeyId != nil && *sig.IssuerKeyId == e.PrimaryKey.KeyId { if err = e.PrimaryKey.VerifyUserIdSignature(pkt.Id, sig); err != nil { return nil, error.StructuralError("user ID self-signature invalid: " + err.String()) } @@ -400,3 +477,69 @@ func (e *Entity) SerializePrivate(w io.Writer) (err os.Error) { } return nil } + +// Serialize writes the public part of the given Entity to w. (No private +// key material will be output). +func (e *Entity) Serialize(w io.Writer) os.Error { + err := e.PrimaryKey.Serialize(w) + if err != nil { + return err + } + for _, ident := range e.Identities { + err = ident.UserId.Serialize(w) + if err != nil { + return err + } + err = ident.SelfSignature.Serialize(w) + if err != nil { + return err + } + for _, sig := range ident.Signatures { + err = sig.Serialize(w) + if err != nil { + return err + } + } + } + for _, subkey := range e.Subkeys { + err = subkey.PublicKey.Serialize(w) + if err != nil { + return err + } + err = subkey.Sig.Serialize(w) + if err != nil { + return err + } + } + return nil +} + +// SignIdentity adds a signature to e, from signer, attesting that identity is +// associated with e. The provided identity must already be an element of +// e.Identities and the private key of signer must have been decrypted if +// necessary. +func (e *Entity) SignIdentity(identity string, signer *Entity) os.Error { + if signer.PrivateKey == nil { + return error.InvalidArgumentError("signing Entity must have a private key") + } + if signer.PrivateKey.Encrypted { + return error.InvalidArgumentError("signing Entity's private key must be decrypted") + } + ident, ok := e.Identities[identity] + if !ok { + return error.InvalidArgumentError("given identity string not found in Entity") + } + + sig := &packet.Signature{ + SigType: packet.SigTypeGenericCert, + PubKeyAlgo: signer.PrivateKey.PubKeyAlgo, + Hash: crypto.SHA256, + CreationTime: uint32(time.Seconds()), + IssuerKeyId: &signer.PrivateKey.KeyId, + } + if err := sig.SignKey(e.PrimaryKey, signer.PrivateKey); err != nil { + return err + } + ident.Signatures = append(ident.Signatures, sig) + return nil +} diff --git a/src/pkg/crypto/openpgp/packet/encrypted_key.go b/src/pkg/crypto/openpgp/packet/encrypted_key.go index b11a9b830..b4730cbc9 100644 --- a/src/pkg/crypto/openpgp/packet/encrypted_key.go +++ b/src/pkg/crypto/openpgp/packet/encrypted_key.go @@ -5,6 +5,8 @@ package packet import ( + "big" + "crypto/openpgp/elgamal" "crypto/openpgp/error" "crypto/rand" "crypto/rsa" @@ -14,14 +16,17 @@ import ( "strconv" ) +const encryptedKeyVersion = 3 + // EncryptedKey represents a public-key encrypted session key. See RFC 4880, // section 5.1. type EncryptedKey struct { KeyId uint64 Algo PublicKeyAlgorithm - Encrypted []byte CipherFunc CipherFunction // only valid after a successful Decrypt Key []byte // only valid after a successful Decrypt + + encryptedMPI1, encryptedMPI2 []byte } func (e *EncryptedKey) parse(r io.Reader) (err os.Error) { @@ -30,37 +35,134 @@ func (e *EncryptedKey) parse(r io.Reader) (err os.Error) { if err != nil { return } - if buf[0] != 3 { + if buf[0] != encryptedKeyVersion { return error.UnsupportedError("unknown EncryptedKey version " + strconv.Itoa(int(buf[0]))) } e.KeyId = binary.BigEndian.Uint64(buf[1:9]) e.Algo = PublicKeyAlgorithm(buf[9]) - if e.Algo == PubKeyAlgoRSA || e.Algo == PubKeyAlgoRSAEncryptOnly { - e.Encrypted, _, err = readMPI(r) + switch e.Algo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + e.encryptedMPI1, _, err = readMPI(r) + case PubKeyAlgoElGamal: + e.encryptedMPI1, _, err = readMPI(r) + if err != nil { + return + } + e.encryptedMPI2, _, err = readMPI(r) } _, err = consumeAll(r) return } -// DecryptRSA decrypts an RSA encrypted session key with the given private key. -func (e *EncryptedKey) DecryptRSA(priv *rsa.PrivateKey) (err os.Error) { - if e.Algo != PubKeyAlgoRSA && e.Algo != PubKeyAlgoRSAEncryptOnly { - return error.InvalidArgumentError("EncryptedKey not RSA encrypted") +func checksumKeyMaterial(key []byte) uint16 { + var checksum uint16 + for _, v := range key { + checksum += uint16(v) } - b, err := rsa.DecryptPKCS1v15(rand.Reader, priv, e.Encrypted) + return checksum +} + +// Decrypt decrypts an encrypted session key with the given private key. The +// private key must have been decrypted first. +func (e *EncryptedKey) Decrypt(priv *PrivateKey) os.Error { + var err os.Error + var b []byte + + // TODO(agl): use session key decryption routines here to avoid + // padding oracle attacks. + switch priv.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + b, err = rsa.DecryptPKCS1v15(rand.Reader, priv.PrivateKey.(*rsa.PrivateKey), e.encryptedMPI1) + case PubKeyAlgoElGamal: + c1 := new(big.Int).SetBytes(e.encryptedMPI1) + c2 := new(big.Int).SetBytes(e.encryptedMPI2) + b, err = elgamal.Decrypt(priv.PrivateKey.(*elgamal.PrivateKey), c1, c2) + default: + err = error.InvalidArgumentError("cannot decrypted encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo))) + } + if err != nil { - return + return err } + e.CipherFunc = CipherFunction(b[0]) e.Key = b[1 : len(b)-2] expectedChecksum := uint16(b[len(b)-2])<<8 | uint16(b[len(b)-1]) - var checksum uint16 - for _, v := range e.Key { - checksum += uint16(v) - } + checksum := checksumKeyMaterial(e.Key) if checksum != expectedChecksum { return error.StructuralError("EncryptedKey checksum incorrect") } - return + return nil +} + +// SerializeEncryptedKey serializes an encrypted key packet to w that contains +// key, encrypted to pub. +func SerializeEncryptedKey(w io.Writer, rand io.Reader, pub *PublicKey, cipherFunc CipherFunction, key []byte) os.Error { + var buf [10]byte + buf[0] = encryptedKeyVersion + binary.BigEndian.PutUint64(buf[1:9], pub.KeyId) + buf[9] = byte(pub.PubKeyAlgo) + + keyBlock := make([]byte, 1 /* cipher type */ +len(key)+2 /* checksum */ ) + keyBlock[0] = byte(cipherFunc) + copy(keyBlock[1:], key) + checksum := checksumKeyMaterial(key) + keyBlock[1+len(key)] = byte(checksum >> 8) + keyBlock[1+len(key)+1] = byte(checksum) + + switch pub.PubKeyAlgo { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly: + return serializeEncryptedKeyRSA(w, rand, buf, pub.PublicKey.(*rsa.PublicKey), keyBlock) + case PubKeyAlgoElGamal: + return serializeEncryptedKeyElGamal(w, rand, buf, pub.PublicKey.(*elgamal.PublicKey), keyBlock) + case PubKeyAlgoDSA, PubKeyAlgoRSASignOnly: + return error.InvalidArgumentError("cannot encrypt to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) + } + + return error.UnsupportedError("encrypting a key to public key of type " + strconv.Itoa(int(pub.PubKeyAlgo))) +} + +func serializeEncryptedKeyRSA(w io.Writer, rand io.Reader, header [10]byte, pub *rsa.PublicKey, keyBlock []byte) os.Error { + cipherText, err := rsa.EncryptPKCS1v15(rand, pub, keyBlock) + if err != nil { + return error.InvalidArgumentError("RSA encryption failed: " + err.String()) + } + + packetLen := 10 /* header length */ + 2 /* mpi size */ + len(cipherText) + + err = serializeHeader(w, packetTypeEncryptedKey, packetLen) + if err != nil { + return err + } + _, err = w.Write(header[:]) + if err != nil { + return err + } + return writeMPI(w, 8*uint16(len(cipherText)), cipherText) +} + +func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte, pub *elgamal.PublicKey, keyBlock []byte) os.Error { + c1, c2, err := elgamal.Encrypt(rand, pub, keyBlock) + if err != nil { + return error.InvalidArgumentError("ElGamal encryption failed: " + err.String()) + } + + packetLen := 10 /* header length */ + packetLen += 2 /* mpi size */ + (c1.BitLen()+7)/8 + packetLen += 2 /* mpi size */ + (c2.BitLen()+7)/8 + + err = serializeHeader(w, packetTypeEncryptedKey, packetLen) + if err != nil { + return err + } + _, err = w.Write(header[:]) + if err != nil { + return err + } + err = writeBig(w, c1) + if err != nil { + return err + } + return writeBig(w, c2) } diff --git a/src/pkg/crypto/openpgp/packet/encrypted_key_test.go b/src/pkg/crypto/openpgp/packet/encrypted_key_test.go index 755ae7a30..b0a14904a 100644 --- a/src/pkg/crypto/openpgp/packet/encrypted_key_test.go +++ b/src/pkg/crypto/openpgp/packet/encrypted_key_test.go @@ -6,6 +6,8 @@ package packet import ( "big" + "bytes" + "crypto/rand" "crypto/rsa" "fmt" "testing" @@ -19,7 +21,28 @@ func bigFromBase10(s string) *big.Int { return b } -func TestEncryptedKey(t *testing.T) { + +var encryptedKeyPub = rsa.PublicKey{ + E: 65537, + N: bigFromBase10("115804063926007623305902631768113868327816898845124614648849934718568541074358183759250136204762053879858102352159854352727097033322663029387610959884180306668628526686121021235757016368038585212410610742029286439607686208110250133174279811431933746643015923132833417396844716207301518956640020862630546868823"), +} + +var encryptedKeyRSAPriv = &rsa.PrivateKey{ + PublicKey: encryptedKeyPub, + D: bigFromBase10("32355588668219869544751561565313228297765464314098552250409557267371233892496951383426602439009993875125222579159850054973310859166139474359774543943714622292329487391199285040721944491839695981199720170366763547754915493640685849961780092241140181198779299712578774460837139360803883139311171713302987058393"), +} + +var encryptedKeyPriv = &PrivateKey{ + PublicKey: PublicKey{ + PubKeyAlgo: PubKeyAlgoRSA, + }, + PrivateKey: encryptedKeyRSAPriv, +} + +func TestDecryptingEncryptedKey(t *testing.T) { + const encryptedKeyHex = "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8" + const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b" + p, err := Read(readerFromHex(encryptedKeyHex)) if err != nil { t.Errorf("error from Read: %s", err) @@ -36,23 +59,63 @@ func TestEncryptedKey(t *testing.T) { return } - pub := rsa.PublicKey{ - E: 65537, - N: bigFromBase10("115804063926007623305902631768113868327816898845124614648849934718568541074358183759250136204762053879858102352159854352727097033322663029387610959884180306668628526686121021235757016368038585212410610742029286439607686208110250133174279811431933746643015923132833417396844716207301518956640020862630546868823"), + err = ek.Decrypt(encryptedKeyPriv) + if err != nil { + t.Errorf("error from Decrypt: %s", err) + return } - priv := &rsa.PrivateKey{ - PublicKey: pub, - D: bigFromBase10("32355588668219869544751561565313228297765464314098552250409557267371233892496951383426602439009993875125222579159850054973310859166139474359774543943714622292329487391199285040721944491839695981199720170366763547754915493640685849961780092241140181198779299712578774460837139360803883139311171713302987058393"), + if ek.CipherFunc != CipherAES256 { + t.Errorf("unexpected EncryptedKey contents: %#v", ek) + return + } + + keyHex := fmt.Sprintf("%x", ek.Key) + if keyHex != expectedKeyHex { + t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex) } +} - err = ek.DecryptRSA(priv) +func TestEncryptingEncryptedKey(t *testing.T) { + key := []byte{1, 2, 3, 4} + const expectedKeyHex = "01020304" + const keyId = 42 + + pub := &PublicKey{ + PublicKey: &encryptedKeyPub, + KeyId: keyId, + PubKeyAlgo: PubKeyAlgoRSAEncryptOnly, + } + + buf := new(bytes.Buffer) + err := SerializeEncryptedKey(buf, rand.Reader, pub, CipherAES128, key) + if err != nil { + t.Errorf("error writing encrypted key packet: %s", err) + } + + p, err := Read(buf) + if err != nil { + t.Errorf("error from Read: %s", err) + return + } + ek, ok := p.(*EncryptedKey) + if !ok { + t.Errorf("didn't parse an EncryptedKey, got %#v", p) + return + } + + if ek.KeyId != keyId || ek.Algo != PubKeyAlgoRSAEncryptOnly { + t.Errorf("unexpected EncryptedKey contents: %#v", ek) + return + } + + err = ek.Decrypt(encryptedKeyPriv) if err != nil { - t.Errorf("error from DecryptRSA: %s", err) + t.Errorf("error from Decrypt: %s", err) return } - if ek.CipherFunc != CipherAES256 { + if ek.CipherFunc != CipherAES128 { t.Errorf("unexpected EncryptedKey contents: %#v", ek) return } @@ -62,6 +125,3 @@ func TestEncryptedKey(t *testing.T) { t.Errorf("bad key, got %s want %x", keyHex, expectedKeyHex) } } - -const encryptedKeyHex = "c18c032a67d68660df41c70104005789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8" -const expectedKeyHex = "d930363f7e0308c333b9618617ea728963d8df993665ae7be1092d4926fd864b" diff --git a/src/pkg/crypto/openpgp/packet/one_pass_signature.go b/src/pkg/crypto/openpgp/packet/one_pass_signature.go index acbf58bbe..ca826e4f4 100644 --- a/src/pkg/crypto/openpgp/packet/one_pass_signature.go +++ b/src/pkg/crypto/openpgp/packet/one_pass_signature.go @@ -24,6 +24,8 @@ type OnePassSignature struct { IsLast bool } +const onePassSignatureVersion = 3 + func (ops *OnePassSignature) parse(r io.Reader) (err os.Error) { var buf [13]byte @@ -31,7 +33,7 @@ func (ops *OnePassSignature) parse(r io.Reader) (err os.Error) { if err != nil { return } - if buf[0] != 3 { + if buf[0] != onePassSignatureVersion { err = error.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0]))) } @@ -47,3 +49,26 @@ func (ops *OnePassSignature) parse(r io.Reader) (err os.Error) { ops.IsLast = buf[12] != 0 return } + +// Serialize marshals the given OnePassSignature to w. +func (ops *OnePassSignature) Serialize(w io.Writer) os.Error { + var buf [13]byte + buf[0] = onePassSignatureVersion + buf[1] = uint8(ops.SigType) + var ok bool + buf[2], ok = s2k.HashToHashId(ops.Hash) + if !ok { + return error.UnsupportedError("hash type: " + strconv.Itoa(int(ops.Hash))) + } + buf[3] = uint8(ops.PubKeyAlgo) + binary.BigEndian.PutUint64(buf[4:12], ops.KeyId) + if ops.IsLast { + buf[12] = 1 + } + + if err := serializeHeader(w, packetTypeOnePassSignature, len(buf)); err != nil { + return err + } + _, err := w.Write(buf[:]) + return err +} diff --git a/src/pkg/crypto/openpgp/packet/packet.go b/src/pkg/crypto/openpgp/packet/packet.go index 640a5b76f..1d7297e38 100644 --- a/src/pkg/crypto/openpgp/packet/packet.go +++ b/src/pkg/crypto/openpgp/packet/packet.go @@ -372,10 +372,30 @@ const ( PubKeyAlgoRSA PublicKeyAlgorithm = 1 PubKeyAlgoRSAEncryptOnly PublicKeyAlgorithm = 2 PubKeyAlgoRSASignOnly PublicKeyAlgorithm = 3 - PubKeyAlgoElgamal PublicKeyAlgorithm = 16 + PubKeyAlgoElGamal PublicKeyAlgorithm = 16 PubKeyAlgoDSA PublicKeyAlgorithm = 17 ) +// CanEncrypt returns true if it's possible to encrypt a message to a public +// key of the given type. +func (pka PublicKeyAlgorithm) CanEncrypt() bool { + switch pka { + case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoElGamal: + return true + } + return false +} + +// CanSign returns true if it's possible for a public key of the given type to +// sign a message. +func (pka PublicKeyAlgorithm) CanSign() bool { + switch pka { + case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA: + return true + } + return false +} + // CipherFunction represents the different block ciphers specified for OpenPGP. See // http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13 type CipherFunction uint8 @@ -387,8 +407,8 @@ const ( CipherAES256 CipherFunction = 9 ) -// keySize returns the key size, in bytes, of cipher. -func (cipher CipherFunction) keySize() int { +// KeySize returns the key size, in bytes, of cipher. +func (cipher CipherFunction) KeySize() int { switch cipher { case CipherCAST5: return cast5.KeySize diff --git a/src/pkg/crypto/openpgp/packet/private_key.go b/src/pkg/crypto/openpgp/packet/private_key.go index 92e7ee422..6f8133d98 100644 --- a/src/pkg/crypto/openpgp/packet/private_key.go +++ b/src/pkg/crypto/openpgp/packet/private_key.go @@ -9,6 +9,7 @@ import ( "bytes" "crypto/cipher" "crypto/dsa" + "crypto/openpgp/elgamal" "crypto/openpgp/error" "crypto/openpgp/s2k" "crypto/rsa" @@ -181,7 +182,7 @@ func (pk *PrivateKey) Decrypt(passphrase []byte) os.Error { return nil } - key := make([]byte, pk.cipher.keySize()) + key := make([]byte, pk.cipher.KeySize()) pk.s2k(key, passphrase) block := pk.cipher.new(key) cfb := cipher.NewCFBDecrypter(block, pk.iv) @@ -224,6 +225,8 @@ func (pk *PrivateKey) parsePrivateKey(data []byte) (err os.Error) { return pk.parseRSAPrivateKey(data) case PubKeyAlgoDSA: return pk.parseDSAPrivateKey(data) + case PubKeyAlgoElGamal: + return pk.parseElGamalPrivateKey(data) } panic("impossible") } @@ -277,3 +280,22 @@ func (pk *PrivateKey) parseDSAPrivateKey(data []byte) (err os.Error) { return nil } + +func (pk *PrivateKey) parseElGamalPrivateKey(data []byte) (err os.Error) { + pub := pk.PublicKey.PublicKey.(*elgamal.PublicKey) + priv := new(elgamal.PrivateKey) + priv.PublicKey = *pub + + buf := bytes.NewBuffer(data) + x, _, err := readMPI(buf) + if err != nil { + return + } + + priv.X = new(big.Int).SetBytes(x) + pk.PrivateKey = priv + pk.Encrypted = false + pk.encryptedData = nil + + return nil +} diff --git a/src/pkg/crypto/openpgp/packet/private_key_test.go b/src/pkg/crypto/openpgp/packet/private_key_test.go index e941cc735..60eebaa6b 100644 --- a/src/pkg/crypto/openpgp/packet/private_key_test.go +++ b/src/pkg/crypto/openpgp/packet/private_key_test.go @@ -8,30 +8,50 @@ import ( "testing" ) -func TestPrivateKeyRead(t *testing.T) { - packet, err := Read(readerFromHex(privKeyHex)) - if err != nil { - t.Error(err) - return - } - - privKey := packet.(*PrivateKey) - - if !privKey.Encrypted { - t.Error("private key isn't encrypted") - return - } - - err = privKey.Decrypt([]byte("testing")) - if err != nil { - t.Error(err) - return - } +var privateKeyTests = []struct { + privateKeyHex string + creationTime uint32 +}{ + { + privKeyRSAHex, + 0x4cc349a8, + }, + { + privKeyElGamalHex, + 0x4df9ee1a, + }, +} - if privKey.CreationTime != 0x4cc349a8 || privKey.Encrypted { - t.Errorf("failed to parse, got: %#v", privKey) +func TestPrivateKeyRead(t *testing.T) { + for i, test := range privateKeyTests { + packet, err := Read(readerFromHex(test.privateKeyHex)) + if err != nil { + t.Errorf("#%d: failed to parse: %s", i, err) + continue + } + + privKey := packet.(*PrivateKey) + + if !privKey.Encrypted { + t.Errorf("#%d: private key isn't encrypted", i) + continue + } + + err = privKey.Decrypt([]byte("testing")) + if err != nil { + t.Errorf("#%d: failed to decrypt: %s", i, err) + continue + } + + if privKey.CreationTime != test.creationTime || privKey.Encrypted { + t.Errorf("#%d: bad result, got: %#v", i, privKey) + } } } // Generated with `gpg --export-secret-keys "Test Key 2"` -const privKeyHex = "9501fe044cc349a8010400b70ca0010e98c090008d45d1ee8f9113bd5861fd57b88bacb7c68658747663f1e1a3b5a98f32fda6472373c024b97359cd2efc88ff60f77751adfbf6af5e615e6a1408cfad8bf0cea30b0d5f53aa27ad59089ba9b15b7ebc2777a25d7b436144027e3bcd203909f147d0e332b240cf63d3395f5dfe0df0a6c04e8655af7eacdf0011010001fe0303024a252e7d475fd445607de39a265472aa74a9320ba2dac395faa687e9e0336aeb7e9a7397e511b5afd9dc84557c80ac0f3d4d7bfec5ae16f20d41c8c84a04552a33870b930420e230e179564f6d19bb153145e76c33ae993886c388832b0fa042ddda7f133924f3854481533e0ede31d51278c0519b29abc3bf53da673e13e3e1214b52413d179d7f66deee35cac8eacb060f78379d70ef4af8607e68131ff529439668fc39c9ce6dfef8a5ac234d234802cbfb749a26107db26406213ae5c06d4673253a3cbee1fcbae58d6ab77e38d6e2c0e7c6317c48e054edadb5a40d0d48acb44643d998139a8a66bb820be1f3f80185bc777d14b5954b60effe2448a036d565c6bc0b915fcea518acdd20ab07bc1529f561c58cd044f723109b93f6fd99f876ff891d64306b5d08f48bab59f38695e9109c4dec34013ba3153488ce070268381ba923ee1eb77125b36afcb4347ec3478c8f2735b06ef17351d872e577fa95d0c397c88c71b59629a36aec" +const privKeyRSAHex = "9501fe044cc349a8010400b70ca0010e98c090008d45d1ee8f9113bd5861fd57b88bacb7c68658747663f1e1a3b5a98f32fda6472373c024b97359cd2efc88ff60f77751adfbf6af5e615e6a1408cfad8bf0cea30b0d5f53aa27ad59089ba9b15b7ebc2777a25d7b436144027e3bcd203909f147d0e332b240cf63d3395f5dfe0df0a6c04e8655af7eacdf0011010001fe0303024a252e7d475fd445607de39a265472aa74a9320ba2dac395faa687e9e0336aeb7e9a7397e511b5afd9dc84557c80ac0f3d4d7bfec5ae16f20d41c8c84a04552a33870b930420e230e179564f6d19bb153145e76c33ae993886c388832b0fa042ddda7f133924f3854481533e0ede31d51278c0519b29abc3bf53da673e13e3e1214b52413d179d7f66deee35cac8eacb060f78379d70ef4af8607e68131ff529439668fc39c9ce6dfef8a5ac234d234802cbfb749a26107db26406213ae5c06d4673253a3cbee1fcbae58d6ab77e38d6e2c0e7c6317c48e054edadb5a40d0d48acb44643d998139a8a66bb820be1f3f80185bc777d14b5954b60effe2448a036d565c6bc0b915fcea518acdd20ab07bc1529f561c58cd044f723109b93f6fd99f876ff891d64306b5d08f48bab59f38695e9109c4dec34013ba3153488ce070268381ba923ee1eb77125b36afcb4347ec3478c8f2735b06ef17351d872e577fa95d0c397c88c71b59629a36aec" + +// Generated by `gpg --export-secret-keys` followed by a manual extraction of +// the ElGamal subkey from the packets. +const privKeyElGamalHex = "9d0157044df9ee1a100400eb8e136a58ec39b582629cdadf830bc64e0a94ed8103ca8bb247b27b11b46d1d25297ef4bcc3071785ba0c0bedfe89eabc5287fcc0edf81ab5896c1c8e4b20d27d79813c7aede75320b33eaeeaa586edc00fd1036c10133e6ba0ff277245d0d59d04b2b3421b7244aca5f4a8d870c6f1c1fbff9e1c26699a860b9504f35ca1d700030503fd1ededd3b840795be6d9ccbe3c51ee42e2f39233c432b831ddd9c4e72b7025a819317e47bf94f9ee316d7273b05d5fcf2999c3a681f519b1234bbfa6d359b4752bd9c3f77d6b6456cde152464763414ca130f4e91d91041432f90620fec0e6d6b5116076c2985d5aeaae13be492b9b329efcaf7ee25120159a0a30cd976b42d7afe030302dae7eb80db744d4960c4df930d57e87fe81412eaace9f900e6c839817a614ddb75ba6603b9417c33ea7b6c93967dfa2bcff3fa3c74a5ce2c962db65b03aece14c96cbd0038fc" diff --git a/src/pkg/crypto/openpgp/packet/public_key.go b/src/pkg/crypto/openpgp/packet/public_key.go index 46d365b2a..e6b0ae5f3 100644 --- a/src/pkg/crypto/openpgp/packet/public_key.go +++ b/src/pkg/crypto/openpgp/packet/public_key.go @@ -7,6 +7,7 @@ package packet import ( "big" "crypto/dsa" + "crypto/openpgp/elgamal" "crypto/openpgp/error" "crypto/rsa" "crypto/sha1" @@ -69,6 +70,8 @@ func (pk *PublicKey) parse(r io.Reader) (err os.Error) { err = pk.parseRSA(r) case PubKeyAlgoDSA: err = pk.parseDSA(r) + case PubKeyAlgoElGamal: + err = pk.parseElGamal(r) default: err = error.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo))) } @@ -117,7 +120,7 @@ func (pk *PublicKey) parseRSA(r io.Reader) (err os.Error) { return } -// parseRSA parses DSA public key material from the given Reader. See RFC 4880, +// parseDSA parses DSA public key material from the given Reader. See RFC 4880, // section 5.5.2. func (pk *PublicKey) parseDSA(r io.Reader) (err os.Error) { pk.p.bytes, pk.p.bitLength, err = readMPI(r) @@ -146,6 +149,30 @@ func (pk *PublicKey) parseDSA(r io.Reader) (err os.Error) { return } +// parseElGamal parses ElGamal public key material from the given Reader. See +// RFC 4880, section 5.5.2. +func (pk *PublicKey) parseElGamal(r io.Reader) (err os.Error) { + pk.p.bytes, pk.p.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.g.bytes, pk.g.bitLength, err = readMPI(r) + if err != nil { + return + } + pk.y.bytes, pk.y.bitLength, err = readMPI(r) + if err != nil { + return + } + + elgamal := new(elgamal.PublicKey) + elgamal.P = new(big.Int).SetBytes(pk.p.bytes) + elgamal.G = new(big.Int).SetBytes(pk.g.bytes) + elgamal.Y = new(big.Int).SetBytes(pk.y.bytes) + pk.PublicKey = elgamal + return +} + // SerializeSignaturePrefix writes the prefix for this public key to the given Writer. // The prefix is used when calculating a signature over this public key. See // RFC 4880, section 5.2.4. @@ -160,6 +187,10 @@ func (pk *PublicKey) SerializeSignaturePrefix(h hash.Hash) { pLength += 2 + uint16(len(pk.q.bytes)) pLength += 2 + uint16(len(pk.g.bytes)) pLength += 2 + uint16(len(pk.y.bytes)) + case PubKeyAlgoElGamal: + pLength += 2 + uint16(len(pk.p.bytes)) + pLength += 2 + uint16(len(pk.g.bytes)) + pLength += 2 + uint16(len(pk.y.bytes)) default: panic("unknown public key algorithm") } @@ -180,9 +211,19 @@ func (pk *PublicKey) Serialize(w io.Writer) (err os.Error) { length += 2 + len(pk.q.bytes) length += 2 + len(pk.g.bytes) length += 2 + len(pk.y.bytes) + case PubKeyAlgoElGamal: + length += 2 + len(pk.p.bytes) + length += 2 + len(pk.g.bytes) + length += 2 + len(pk.y.bytes) + default: + panic("unknown public key algorithm") } - err = serializeHeader(w, packetTypePublicKey, length) + packetType := packetTypePublicKey + if pk.IsSubkey { + packetType = packetTypePublicSubkey + } + err = serializeHeader(w, packetType, length) if err != nil { return } @@ -210,13 +251,15 @@ func (pk *PublicKey) serializeWithoutHeaders(w io.Writer) (err os.Error) { return writeMPIs(w, pk.n, pk.e) case PubKeyAlgoDSA: return writeMPIs(w, pk.p, pk.q, pk.g, pk.y) + case PubKeyAlgoElGamal: + return writeMPIs(w, pk.p, pk.g, pk.y) } return error.InvalidArgumentError("bad public-key algorithm") } // CanSign returns true iff this public key can generate signatures func (pk *PublicKey) CanSign() bool { - return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElgamal + return pk.PubKeyAlgo != PubKeyAlgoRSAEncryptOnly && pk.PubKeyAlgo != PubKeyAlgoElGamal } // VerifySignature returns nil iff sig is a valid signature, made by this @@ -240,14 +283,14 @@ func (pk *PublicKey) VerifySignature(signed hash.Hash, sig *Signature) (err os.E switch pk.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey) - err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature) + err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes) if err != nil { return error.SignatureError("RSA verification failure") } return nil case PubKeyAlgoDSA: dsaPublicKey, _ := pk.PublicKey.(*dsa.PublicKey) - if !dsa.Verify(dsaPublicKey, hashBytes, sig.DSASigR, sig.DSASigS) { + if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.bytes), new(big.Int).SetBytes(sig.DSASigS.bytes)) { return error.SignatureError("DSA verification failure") } return nil diff --git a/src/pkg/crypto/openpgp/packet/signature.go b/src/pkg/crypto/openpgp/packet/signature.go index 3169bac1e..7577e2875 100644 --- a/src/pkg/crypto/openpgp/packet/signature.go +++ b/src/pkg/crypto/openpgp/packet/signature.go @@ -5,7 +5,6 @@ package packet import ( - "big" "crypto" "crypto/dsa" "crypto/openpgp/error" @@ -32,8 +31,11 @@ type Signature struct { HashTag [2]byte CreationTime uint32 // Unix epoch time - RSASignature []byte - DSASigR, DSASigS *big.Int + RSASignature parsedMPI + DSASigR, DSASigS parsedMPI + + // rawSubpackets contains the unparsed subpackets, in order. + rawSubpackets []outputSubpacket // The following are optional so are nil when not included in the // signature. @@ -128,14 +130,11 @@ func (sig *Signature) parse(r io.Reader) (err os.Error) { switch sig.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - sig.RSASignature, _, err = readMPI(r) + sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r) case PubKeyAlgoDSA: - var rBytes, sBytes []byte - rBytes, _, err = readMPI(r) - sig.DSASigR = new(big.Int).SetBytes(rBytes) + sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r) if err == nil { - sBytes, _, err = readMPI(r) - sig.DSASigS = new(big.Int).SetBytes(sBytes) + sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r) } default: panic("unreachable") @@ -177,7 +176,11 @@ const ( // parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1. func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (rest []byte, err os.Error) { // RFC 4880, section 5.2.3.1 - var length uint32 + var ( + length uint32 + packetType signatureSubpacketType + isCritical bool + ) switch { case subpacket[0] < 192: length = uint32(subpacket[0]) @@ -207,10 +210,11 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r err = error.StructuralError("zero length signature subpacket") return } - packetType := subpacket[0] & 0x7f - isCritial := subpacket[0]&0x80 == 0x80 + packetType = signatureSubpacketType(subpacket[0] & 0x7f) + isCritical = subpacket[0]&0x80 == 0x80 subpacket = subpacket[1:] - switch signatureSubpacketType(packetType) { + sig.rawSubpackets = append(sig.rawSubpackets, outputSubpacket{isHashed, packetType, isCritical, subpacket}) + switch packetType { case creationTimeSubpacket: if !isHashed { err = error.StructuralError("signature creation time in non-hashed area") @@ -309,7 +313,7 @@ func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (r } default: - if isCritial { + if isCritical { err = error.UnsupportedError("unknown critical signature subpacket type " + strconv.Itoa(int(packetType))) return } @@ -381,7 +385,6 @@ func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) { // buildHashSuffix constructs the HashSuffix member of sig in preparation for signing. func (sig *Signature) buildHashSuffix() (err os.Error) { - sig.outSubpackets = sig.buildSubpackets() hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true) var ok bool @@ -424,6 +427,7 @@ func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err os.Error) // the hash of the message to be signed and will be mutated by this function. // On success, the signature is stored in sig. Call Serialize to write it out. func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey) (err os.Error) { + sig.outSubpackets = sig.buildSubpackets() digest, err := sig.signPrepareHash(h) if err != nil { return @@ -431,9 +435,16 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey) (err os.Error) { switch priv.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - sig.RSASignature, err = rsa.SignPKCS1v15(rand.Reader, priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest) + sig.RSASignature.bytes, err = rsa.SignPKCS1v15(rand.Reader, priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest) + sig.RSASignature.bitLength = uint16(8 * len(sig.RSASignature.bytes)) case PubKeyAlgoDSA: - sig.DSASigR, sig.DSASigS, err = dsa.Sign(rand.Reader, priv.PrivateKey.(*dsa.PrivateKey), digest) + r, s, err := dsa.Sign(rand.Reader, priv.PrivateKey.(*dsa.PrivateKey), digest) + if err == nil { + sig.DSASigR.bytes = r.Bytes() + sig.DSASigR.bitLength = uint16(8 * len(sig.DSASigR.bytes)) + sig.DSASigS.bytes = s.Bytes() + sig.DSASigS.bitLength = uint16(8 * len(sig.DSASigS.bytes)) + } default: err = error.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo))) } @@ -464,17 +475,20 @@ func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey) os.Error { // Serialize marshals sig to w. SignRSA or SignDSA must have been called first. func (sig *Signature) Serialize(w io.Writer) (err os.Error) { - if sig.RSASignature == nil && sig.DSASigR == nil { + if len(sig.outSubpackets) == 0 { + sig.outSubpackets = sig.rawSubpackets + } + if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil { return error.InvalidArgumentError("Signature: need to call SignRSA or SignDSA before Serialize") } sigLength := 0 switch sig.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - sigLength = len(sig.RSASignature) + sigLength = 2 + len(sig.RSASignature.bytes) case PubKeyAlgoDSA: - sigLength = mpiLength(sig.DSASigR) - sigLength += mpiLength(sig.DSASigS) + sigLength = 2 + len(sig.DSASigR.bytes) + sigLength += 2 + len(sig.DSASigS.bytes) default: panic("impossible") } @@ -482,7 +496,7 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) { unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false) length := len(sig.HashSuffix) - 6 /* trailer not included */ + 2 /* length of unhashed subpackets */ + unhashedSubpacketsLen + - 2 /* hash tag */ + 2 /* length of signature MPI */ + sigLength + 2 /* hash tag */ + sigLength err = serializeHeader(w, packetTypeSignature, length) if err != nil { return @@ -509,12 +523,9 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) { switch sig.PubKeyAlgo { case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly: - err = writeMPI(w, 8*uint16(len(sig.RSASignature)), sig.RSASignature) + err = writeMPIs(w, sig.RSASignature) case PubKeyAlgoDSA: - err = writeBig(w, sig.DSASigR) - if err == nil { - err = writeBig(w, sig.DSASigS) - } + err = writeMPIs(w, sig.DSASigR, sig.DSASigS) default: panic("impossible") } @@ -525,6 +536,7 @@ func (sig *Signature) Serialize(w io.Writer) (err os.Error) { type outputSubpacket struct { hashed bool // true if this subpacket is in the hashed area. subpacketType signatureSubpacketType + isCritical bool contents []byte } @@ -534,12 +546,12 @@ func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) { creationTime[1] = byte(sig.CreationTime >> 16) creationTime[2] = byte(sig.CreationTime >> 8) creationTime[3] = byte(sig.CreationTime) - subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, creationTime}) + subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime}) if sig.IssuerKeyId != nil { keyId := make([]byte, 8) binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId) - subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, keyId}) + subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, false, keyId}) } return diff --git a/src/pkg/crypto/openpgp/packet/signature_test.go b/src/pkg/crypto/openpgp/packet/signature_test.go index 1305548b2..c1bbde8b0 100644 --- a/src/pkg/crypto/openpgp/packet/signature_test.go +++ b/src/pkg/crypto/openpgp/packet/signature_test.go @@ -12,9 +12,7 @@ import ( ) func TestSignatureRead(t *testing.T) { - signatureData, _ := hex.DecodeString(signatureDataHex) - buf := bytes.NewBuffer(signatureData) - packet, err := Read(buf) + packet, err := Read(readerFromHex(signatureDataHex)) if err != nil { t.Error(err) return @@ -25,4 +23,20 @@ func TestSignatureRead(t *testing.T) { } } -const signatureDataHex = "89011c04000102000605024cb45112000a0910ab105c91af38fb158f8d07ff5596ea368c5efe015bed6e78348c0f033c931d5f2ce5db54ce7f2a7e4b4ad64db758d65a7a71773edeab7ba2a9e0908e6a94a1175edd86c1d843279f045b021a6971a72702fcbd650efc393c5474d5b59a15f96d2eaad4c4c426797e0dcca2803ef41c6ff234d403eec38f31d610c344c06f2401c262f0993b2e66cad8a81ebc4322c723e0d4ba09fe917e8777658307ad8329adacba821420741009dfe87f007759f0982275d028a392c6ed983a0d846f890b36148c7358bdb8a516007fac760261ecd06076813831a36d0459075d1befa245ae7f7fb103d92ca759e9498fe60ef8078a39a3beda510deea251ea9f0a7f0df6ef42060f20780360686f3e400e" +func TestSignatureReserialize(t *testing.T) { + packet, _ := Read(readerFromHex(signatureDataHex)) + sig := packet.(*Signature) + out := new(bytes.Buffer) + err := sig.Serialize(out) + if err != nil { + t.Errorf("error reserializing: %s", err) + return + } + + expected, _ := hex.DecodeString(signatureDataHex) + if !bytes.Equal(expected, out.Bytes()) { + t.Errorf("output doesn't match input (got vs expected):\n%s\n%s", hex.Dump(out.Bytes()), hex.Dump(expected)) + } +} + +const signatureDataHex = "c2c05c04000102000605024cb45112000a0910ab105c91af38fb158f8d07ff5596ea368c5efe015bed6e78348c0f033c931d5f2ce5db54ce7f2a7e4b4ad64db758d65a7a71773edeab7ba2a9e0908e6a94a1175edd86c1d843279f045b021a6971a72702fcbd650efc393c5474d5b59a15f96d2eaad4c4c426797e0dcca2803ef41c6ff234d403eec38f31d610c344c06f2401c262f0993b2e66cad8a81ebc4322c723e0d4ba09fe917e8777658307ad8329adacba821420741009dfe87f007759f0982275d028a392c6ed983a0d846f890b36148c7358bdb8a516007fac760261ecd06076813831a36d0459075d1befa245ae7f7fb103d92ca759e9498fe60ef8078a39a3beda510deea251ea9f0a7f0df6ef42060f20780360686f3e400e" diff --git a/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go b/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go index 25d264acf..ad4f1d621 100644 --- a/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go +++ b/src/pkg/crypto/openpgp/packet/symmetric_key_encrypted.go @@ -42,7 +42,7 @@ func (ske *SymmetricKeyEncrypted) parse(r io.Reader) (err os.Error) { } ske.CipherFunc = CipherFunction(buf[1]) - if ske.CipherFunc.keySize() == 0 { + if ske.CipherFunc.KeySize() == 0 { return error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(buf[1]))) } @@ -78,7 +78,7 @@ func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) os.Error { return nil } - key := make([]byte, ske.CipherFunc.keySize()) + key := make([]byte, ske.CipherFunc.KeySize()) ske.s2k(key, passphrase) if len(ske.encryptedKey) == 0 { @@ -109,7 +109,7 @@ func (ske *SymmetricKeyEncrypted) Decrypt(passphrase []byte) os.Error { // given passphrase. The session key is returned and must be passed to // SerializeSymmetricallyEncrypted. func SerializeSymmetricKeyEncrypted(w io.Writer, rand io.Reader, passphrase []byte, cipherFunc CipherFunction) (key []byte, err os.Error) { - keySize := cipherFunc.keySize() + keySize := cipherFunc.KeySize() if keySize == 0 { return nil, error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(cipherFunc))) } diff --git a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go index 236c36774..e33c9f3a0 100644 --- a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go +++ b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted.go @@ -47,7 +47,7 @@ func (se *SymmetricallyEncrypted) parse(r io.Reader) os.Error { // packet can be read. An incorrect key can, with high probability, be detected // immediately and this will result in a KeyIncorrect error being returned. func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, os.Error) { - keySize := c.keySize() + keySize := c.KeySize() if keySize == 0 { return nil, error.UnsupportedError("unknown cipher: " + strconv.Itoa(int(c))) } @@ -255,7 +255,7 @@ func (c noOpCloser) Close() os.Error { // to w and returns a WriteCloser to which the to-be-encrypted packets can be // written. func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, key []byte) (contents io.WriteCloser, err os.Error) { - if c.keySize() != len(key) { + if c.KeySize() != len(key) { return nil, error.InvalidArgumentError("SymmetricallyEncrypted.Serialize: bad key length") } writeCloser := noOpCloser{w} diff --git a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go index ba5606e6c..1054fc2f9 100644 --- a/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go +++ b/src/pkg/crypto/openpgp/packet/symmetrically_encrypted_test.go @@ -81,7 +81,7 @@ const mdcPlaintextHex = "a302789c3b2d93c4e0eb9aba22283539b3203335af44a134afb800c func TestSerialize(t *testing.T) { buf := bytes.NewBuffer(nil) c := CipherAES128 - key := make([]byte, c.keySize()) + key := make([]byte, c.KeySize()) w, err := SerializeSymmetricallyEncrypted(buf, c, key) if err != nil { diff --git a/src/pkg/crypto/openpgp/read.go b/src/pkg/crypto/openpgp/read.go index 46fcde363..d95f613c6 100644 --- a/src/pkg/crypto/openpgp/read.go +++ b/src/pkg/crypto/openpgp/read.go @@ -10,7 +10,6 @@ import ( "crypto/openpgp/armor" "crypto/openpgp/error" "crypto/openpgp/packet" - "crypto/rsa" _ "crypto/sha256" "hash" "io" @@ -57,7 +56,6 @@ type MessageDetails struct { // been consumed. Once EOF has been seen, the following fields are // valid. (An authentication code failure is reported as a // SignatureError error when reading from UnverifiedBody.) - SignatureError os.Error // nil if the signature is good. Signature *packet.Signature // the signature packet itself. @@ -112,7 +110,10 @@ ParsePackets: case *packet.EncryptedKey: // This packet contains the decryption key encrypted to a public key. md.EncryptedToKeyIds = append(md.EncryptedToKeyIds, p.KeyId) - if p.Algo != packet.PubKeyAlgoRSA && p.Algo != packet.PubKeyAlgoRSAEncryptOnly { + switch p.Algo { + case packet.PubKeyAlgoRSA, packet.PubKeyAlgoRSAEncryptOnly, packet.PubKeyAlgoElGamal: + break + default: continue } var keys []Key @@ -155,7 +156,7 @@ FindKey: } if !pk.key.PrivateKey.Encrypted { if len(pk.encryptedKey.Key) == 0 { - pk.encryptedKey.DecryptRSA(pk.key.PrivateKey.PrivateKey.(*rsa.PrivateKey)) + pk.encryptedKey.Decrypt(pk.key.PrivateKey) } if len(pk.encryptedKey.Key) == 0 { continue @@ -249,11 +250,12 @@ FindLiteralData: md.IsSigned = true md.SignedByKeyId = p.KeyId keys := keyring.KeysById(p.KeyId) - for _, key := range keys { + for i, key := range keys { if key.SelfSignature.FlagsValid && !key.SelfSignature.FlagSign { continue } - md.SignedBy = &key + md.SignedBy = &keys[i] + break } case *packet.LiteralData: md.LiteralData = p diff --git a/src/pkg/crypto/openpgp/read_test.go b/src/pkg/crypto/openpgp/read_test.go index a040b5b1b..4dc290ef2 100644 --- a/src/pkg/crypto/openpgp/read_test.go +++ b/src/pkg/crypto/openpgp/read_test.go @@ -33,6 +33,29 @@ func TestReadKeyRing(t *testing.T) { } } +func TestRereadKeyRing(t *testing.T) { + kring, err := ReadKeyRing(readerFromHex(testKeys1And2Hex)) + if err != nil { + t.Errorf("error in initial parse: %s", err) + return + } + out := new(bytes.Buffer) + err = kring[0].Serialize(out) + if err != nil { + t.Errorf("error in serialization: %s", err) + return + } + kring, err = ReadKeyRing(out) + if err != nil { + t.Errorf("error in second parse: %s", err) + return + } + + if len(kring) != 1 || uint32(kring[0].PrimaryKey.KeyId) != 0xC20C31BB { + t.Errorf("bad keyring: %#v", kring) + } +} + func TestReadPrivateKeyRing(t *testing.T) { kring, err := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) if err != nil { @@ -102,49 +125,71 @@ func TestTextSignedMessage(t *testing.T) { checkSignedMessage(t, signedTextMessageHex, signedTextInput) } -func TestSignedEncryptedMessage(t *testing.T) { - expected := "Signed and encrypted message\n" - kring, _ := ReadKeyRing(readerFromHex(testKeys1And2PrivateHex)) - prompt := func(keys []Key, symmetric bool) ([]byte, os.Error) { - if symmetric { - t.Errorf("prompt: message was marked as symmetrically encrypted") - return nil, error.KeyIncorrectError - } +var signedEncryptedMessageTests = []struct { + keyRingHex string + messageHex string + signedByKeyId uint64 + encryptedToKeyId uint64 +}{ + { + testKeys1And2PrivateHex, + signedEncryptedMessageHex, + 0xa34d7e18c20c31bb, + 0x2a67d68660df41c7, + }, + { + dsaElGamalTestKeysHex, + signedEncryptedMessage2Hex, + 0x33af447ccd759b09, + 0xcf6a7abcd43e3673, + }, +} - if len(keys) == 0 { - t.Error("prompt: no keys requested") - return nil, error.KeyIncorrectError +func TestSignedEncryptedMessage(t *testing.T) { + for i, test := range signedEncryptedMessageTests { + expected := "Signed and encrypted message\n" + kring, _ := ReadKeyRing(readerFromHex(test.keyRingHex)) + prompt := func(keys []Key, symmetric bool) ([]byte, os.Error) { + if symmetric { + t.Errorf("prompt: message was marked as symmetrically encrypted") + return nil, error.KeyIncorrectError + } + + if len(keys) == 0 { + t.Error("prompt: no keys requested") + return nil, error.KeyIncorrectError + } + + err := keys[0].PrivateKey.Decrypt([]byte("passphrase")) + if err != nil { + t.Errorf("prompt: error decrypting key: %s", err) + return nil, error.KeyIncorrectError + } + + return nil, nil } - err := keys[0].PrivateKey.Decrypt([]byte("passphrase")) + md, err := ReadMessage(readerFromHex(test.messageHex), kring, prompt) if err != nil { - t.Errorf("prompt: error decrypting key: %s", err) - return nil, error.KeyIncorrectError + t.Errorf("#%d: error reading message: %s", i, err) + return } - return nil, nil - } - - md, err := ReadMessage(readerFromHex(signedEncryptedMessageHex), kring, prompt) - if err != nil { - t.Errorf("error reading message: %s", err) - return - } - - if !md.IsSigned || md.SignedByKeyId != 0xa34d7e18c20c31bb || md.SignedBy == nil || !md.IsEncrypted || md.IsSymmetricallyEncrypted || len(md.EncryptedToKeyIds) == 0 || md.EncryptedToKeyIds[0] != 0x2a67d68660df41c7 { - t.Errorf("bad MessageDetails: %#v", md) - } + if !md.IsSigned || md.SignedByKeyId != test.signedByKeyId || md.SignedBy == nil || !md.IsEncrypted || md.IsSymmetricallyEncrypted || len(md.EncryptedToKeyIds) == 0 || md.EncryptedToKeyIds[0] != test.encryptedToKeyId { + t.Errorf("#%d: bad MessageDetails: %#v", i, md) + } - contents, err := ioutil.ReadAll(md.UnverifiedBody) - if err != nil { - t.Errorf("error reading UnverifiedBody: %s", err) - } - if string(contents) != expected { - t.Errorf("bad UnverifiedBody got:%s want:%s", string(contents), expected) - } + contents, err := ioutil.ReadAll(md.UnverifiedBody) + if err != nil { + t.Errorf("#%d: error reading UnverifiedBody: %s", i, err) + } + if string(contents) != expected { + t.Errorf("#%d: bad UnverifiedBody got:%s want:%s", i, string(contents), expected) + } - if md.SignatureError != nil || md.Signature == nil { - t.Errorf("failed to validate: %s", md.SignatureError) + if md.SignatureError != nil || md.Signature == nil { + t.Errorf("#%d: failed to validate: %s", i, md.SignatureError) + } } } @@ -265,12 +310,16 @@ const testKeys1And2Hex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d1 const testKeys1And2PrivateHex = "9501d8044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd00110100010003ff4d91393b9a8e3430b14d6209df42f98dc927425b881f1209f319220841273a802a97c7bdb8b3a7740b3ab5866c4d1d308ad0d3a79bd1e883aacf1ac92dfe720285d10d08752a7efe3c609b1d00f17f2805b217be53999a7da7e493bfc3e9618fd17018991b8128aea70a05dbce30e4fbe626aa45775fa255dd9177aabf4df7cf0200c1ded12566e4bc2bb590455e5becfb2e2c9796482270a943343a7835de41080582c2be3caf5981aa838140e97afa40ad652a0b544f83eb1833b0957dce26e47b0200eacd6046741e9ce2ec5beb6fb5e6335457844fb09477f83b050a96be7da043e17f3a9523567ed40e7a521f818813a8b8a72209f1442844843ccc7eb9805442570200bdafe0438d97ac36e773c7162028d65844c4d463e2420aa2228c6e50dc2743c3d6c72d0d782a5173fe7be2169c8a9f4ef8a7cf3e37165e8c61b89c346cdc6c1799d2b41054657374204b6579203120285253412988b804130102002205024d3c5c10021b03060b090807030206150802090a0b0416020301021e01021780000a0910a34d7e18c20c31bbb5b304009cc45fe610b641a2c146331be94dade0a396e73ca725e1b25c21708d9cab46ecca5ccebc23055879df8f99eea39b377962a400f2ebdc36a7c99c333d74aeba346315137c3ff9d0a09b0273299090343048afb8107cf94cbd1400e3026f0ccac7ecebbc4d78588eb3e478fe2754d3ca664bcf3eac96ca4a6b0c8d7df5102f60f6b00200009d01d8044d3c5c10010400b201df61d67487301f11879d514f4248ade90c8f68c7af1284c161098de4c28c2850f1ec7b8e30f959793e571542ffc6532189409cb51c3d30dad78c4ad5165eda18b20d9826d8707d0f742e2ab492103a85bbd9ddf4f5720f6de7064feb0d39ee002219765bb07bcfb8b877f47abe270ddeda4f676108cecb6b9bb2ad484a4f00110100010003fd17a7490c22a79c59281fb7b20f5e6553ec0c1637ae382e8adaea295f50241037f8997cf42c1ce26417e015091451b15424b2c59eb8d4161b0975630408e394d3b00f88d4b4e18e2cc85e8251d4753a27c639c83f5ad4a571c4f19d7cd460b9b73c25ade730c99df09637bd173d8e3e981ac64432078263bb6dc30d3e974150dd0200d0ee05be3d4604d2146fb0457f31ba17c057560785aa804e8ca5530a7cd81d3440d0f4ba6851efcfd3954b7e68908fc0ba47f7ac37bf559c6c168b70d3a7c8cd0200da1c677c4bce06a068070f2b3733b0a714e88d62aa3f9a26c6f5216d48d5c2b5624144f3807c0df30be66b3268eeeca4df1fbded58faf49fc95dc3c35f134f8b01fd1396b6c0fc1b6c4f0eb8f5e44b8eace1e6073e20d0b8bc5385f86f1cf3f050f66af789f3ef1fc107b7f4421e19e0349c730c68f0a226981f4e889054fdb4dc149e8e889f04180102000905024d3c5c10021b0c000a0910a34d7e18c20c31bb1a03040085c8d62e16d05dc4e9dad64953c8a2eed8b6c12f92b1575eeaa6dcf7be9473dd5b24b37b6dffbb4e7c99ed1bd3cb11634be19b3e6e207bed7505c7ca111ccf47cb323bf1f8851eb6360e8034cbff8dd149993c959de89f8f77f38e7e98b8e3076323aa719328e2b408db5ec0d03936efd57422ba04f925cdc7b4c1af7590e40ab00200009501fe044d3c5c33010400b488c3e5f83f4d561f317817538d9d0397981e9aef1321ca68ebfae1cf8b7d388e19f4b5a24a82e2fbbf1c6c26557a6c5845307a03d815756f564ac7325b02bc83e87d5480a8fae848f07cb891f2d51ce7df83dcafdc12324517c86d472cc0ee10d47a68fd1d9ae49a6c19bbd36d82af597a0d88cc9c49de9df4e696fc1f0b5d0011010001fe030302e9030f3c783e14856063f16938530e148bc57a7aa3f3e4f90df9dceccdc779bc0835e1ad3d006e4a8d7b36d08b8e0de5a0d947254ecfbd22037e6572b426bcfdc517796b224b0036ff90bc574b5509bede85512f2eefb520fb4b02aa523ba739bff424a6fe81c5041f253f8d757e69a503d3563a104d0d49e9e890b9d0c26f96b55b743883b472caa7050c4acfd4a21f875bdf1258d88bd61224d303dc9df77f743137d51e6d5246b88c406780528fd9a3e15bab5452e5b93970d9dcc79f48b38651b9f15bfbcf6da452837e9cc70683d1bdca94507870f743e4ad902005812488dd342f836e72869afd00ce1850eea4cfa53ce10e3608e13d3c149394ee3cbd0e23d018fcbcb6e2ec5a1a22972d1d462ca05355d0d290dd2751e550d5efb38c6c89686344df64852bf4ff86638708f644e8ec6bd4af9b50d8541cb91891a431326ab2e332faa7ae86cfb6e0540aa63160c1e5cdd5a4add518b303fff0a20117c6bc77f7cfbaf36b04c865c6c2b42754657374204b6579203220285253412c20656e637279707465642070726976617465206b65792988b804130102002205024d3c5c33021b03060b090807030206150802090a0b0416020301021e01021780000a0910d4984f961e35246b98940400908a73b6a6169f700434f076c6c79015a49bee37130eaf23aaa3cfa9ce60bfe4acaa7bc95f1146ada5867e0079babb38804891f4f0b8ebca57a86b249dee786161a755b7a342e68ccf3f78ed6440a93a6626beb9a37aa66afcd4f888790cb4bb46d94a4ae3eb3d7d3e6b00f6bfec940303e89ec5b32a1eaaacce66497d539328b00200009d01fe044d3c5c33010400a4e913f9442abcc7f1804ccab27d2f787ffa592077ca935a8bb23165bd8d57576acac647cc596b2c3f814518cc8c82953c7a4478f32e0cf645630a5ba38d9618ef2bc3add69d459ae3dece5cab778938d988239f8c5ae437807075e06c828019959c644ff05ef6a5a1dab72227c98e3a040b0cf219026640698d7a13d8538a570011010001fe030302e9030f3c783e148560f936097339ae381d63116efcf802ff8b1c9360767db5219cc987375702a4123fd8657d3e22700f23f95020d1b261eda5257e9a72f9a918e8ef22dd5b3323ae03bbc1923dd224db988cadc16acc04b120a9f8b7e84da9716c53e0334d7b66586ddb9014df604b41be1e960dcfcbc96f4ed150a1a0dd070b9eb14276b9b6be413a769a75b519a53d3ecc0c220e85cd91ca354d57e7344517e64b43b6e29823cbd87eae26e2b2e78e6dedfbb76e3e9f77bcb844f9a8932eb3db2c3f9e44316e6f5d60e9e2a56e46b72abe6b06dc9a31cc63f10023d1f5e12d2a3ee93b675c96f504af0001220991c88db759e231b3320dcedf814dcf723fd9857e3d72d66a0f2af26950b915abdf56c1596f46a325bf17ad4810d3535fb02a259b247ac3dbd4cc3ecf9c51b6c07cebb009c1506fba0a89321ec8683e3fd009a6e551d50243e2d5092fefb3321083a4bad91320dc624bd6b5dddf93553e3d53924c05bfebec1fb4bd47e89a1a889f04180102000905024d3c5c33021b0c000a0910d4984f961e35246b26c703ff7ee29ef53bc1ae1ead533c408fa136db508434e233d6e62be621e031e5940bbd4c08142aed0f82217e7c3e1ec8de574bc06ccf3c36633be41ad78a9eacd209f861cae7b064100758545cc9dd83db71806dc1cfd5fb9ae5c7474bba0c19c44034ae61bae5eca379383339dece94ff56ff7aa44a582f3e5c38f45763af577c0934b0020000" +const dsaElGamalTestKeysHex = "9501e1044dfcb16a110400aa3e5c1a1f43dd28c2ffae8abf5cfce555ee874134d8ba0a0f7b868ce2214beddc74e5e1e21ded354a95d18acdaf69e5e342371a71fbb9093162e0c5f3427de413a7f2c157d83f5cd2f9d791256dc4f6f0e13f13c3302af27f2384075ab3021dff7a050e14854bbde0a1094174855fc02f0bae8e00a340d94a1f22b32e48485700a0cec672ac21258fb95f61de2ce1af74b2c4fa3e6703ff698edc9be22c02ae4d916e4fa223f819d46582c0516235848a77b577ea49018dcd5e9e15cff9dbb4663a1ae6dd7580fa40946d40c05f72814b0f88481207e6c0832c3bded4853ebba0a7e3bd8e8c66df33d5a537cd4acf946d1080e7a3dcea679cb2b11a72a33a2b6a9dc85f466ad2ddf4c3db6283fa645343286971e3dd700703fc0c4e290d45767f370831a90187e74e9972aae5bff488eeff7d620af0362bfb95c1a6c3413ab5d15a2e4139e5d07a54d72583914661ed6a87cce810be28a0aa8879a2dd39e52fb6fe800f4f181ac7e328f740cde3d09a05cecf9483e4cca4253e60d4429ffd679d9996a520012aad119878c941e3cf151459873bdfc2a9563472fe0303027a728f9feb3b864260a1babe83925ce794710cfd642ee4ae0e5b9d74cee49e9c67b6cd0ea5dfbb582132195a121356a1513e1bca73e5b80c58c7ccb4164453412f456c47616d616c2054657374204b65792031886204131102002205024dfcb16a021b03060b090807030206150802090a0b0416020301021e01021780000a091033af447ccd759b09fadd00a0b8fd6f5a790bad7e9f2dbb7632046dc4493588db009c087c6a9ba9f7f49fab221587a74788c00db4889ab00200009d0157044dfcb16a1004008dec3f9291205255ccff8c532318133a6840739dd68b03ba942676f9038612071447bf07d00d559c5c0875724ea16a4c774f80d8338b55fca691a0522e530e604215b467bbc9ccfd483a1da99d7bc2648b4318fdbd27766fc8bfad3fddb37c62b8ae7ccfe9577e9b8d1e77c1d417ed2c2ef02d52f4da11600d85d3229607943700030503ff506c94c87c8cab778e963b76cf63770f0a79bf48fb49d3b4e52234620fc9f7657f9f8d56c96a2b7c7826ae6b57ebb2221a3fe154b03b6637cea7e6d98e3e45d87cf8dc432f723d3d71f89c5192ac8d7290684d2c25ce55846a80c9a7823f6acd9bb29fa6cd71f20bc90eccfca20451d0c976e460e672b000df49466408d527affe0303027a728f9feb3b864260abd761730327bca2aaa4ea0525c175e92bf240682a0e83b226f97ecb2e935b62c9a133858ce31b271fa8eb41f6a1b3cd72a63025ce1a75ee4180dcc284884904181102000905024dfcb16a021b0c000a091033af447ccd759b09dd0b009e3c3e7296092c81bee5a19929462caaf2fff3ae26009e218c437a2340e7ea628149af1ec98ec091a43992b00200009501e1044dfcb1be1104009f61faa61aa43df75d128cbe53de528c4aec49ce9360c992e70c77072ad5623de0a3a6212771b66b39a30dad6781799e92608316900518ec01184a85d872365b7d2ba4bacfb5882ea3c2473d3750dc6178cc1cf82147fb58caa28b28e9f12f6d1efcb0534abed644156c91cca4ab78834268495160b2400bc422beb37d237c2300a0cac94911b6d493bda1e1fbc6feeca7cb7421d34b03fe22cec6ccb39675bb7b94a335c2b7be888fd3906a1125f33301d8aa6ec6ee6878f46f73961c8d57a3e9544d8ef2a2cbfd4d52da665b1266928cfe4cb347a58c412815f3b2d2369dec04b41ac9a71cc9547426d5ab941cccf3b18575637ccfb42df1a802df3cfe0a999f9e7109331170e3a221991bf868543960f8c816c28097e503fe319db10fb98049f3a57d7c80c420da66d56f3644371631fad3f0ff4040a19a4fedc2d07727a1b27576f75a4d28c47d8246f27071e12d7a8de62aad216ddbae6aa02efd6b8a3e2818cda48526549791ab277e447b3a36c57cefe9b592f5eab73959743fcc8e83cbefec03a329b55018b53eec196765ae40ef9e20521a603c551efe0303020950d53a146bf9c66034d00c23130cce95576a2ff78016ca471276e8227fb30b1ffbd92e61804fb0c3eff9e30b1a826ee8f3e4730b4d86273ca977b4164453412f456c47616d616c2054657374204b65792032886204131102002205024dfcb1be021b03060b090807030206150802090a0b0416020301021e01021780000a0910a86bf526325b21b22bd9009e34511620415c974750a20df5cb56b182f3b48e6600a0a9466cb1a1305a84953445f77d461593f1d42bc1b00200009d0157044dfcb1be1004009565a951da1ee87119d600c077198f1c1bceb0f7aa54552489298e41ff788fa8f0d43a69871f0f6f77ebdfb14a4260cf9fbeb65d5844b4272a1904dd95136d06c3da745dc46327dd44a0f16f60135914368c8039a34033862261806bb2c5ce1152e2840254697872c85441ccb7321431d75a747a4bfb1d2c66362b51ce76311700030503fc0ea76601c196768070b7365a200e6ddb09307f262d5f39eec467b5f5784e22abdf1aa49226f59ab37cb49969d8f5230ea65caf56015abda62604544ed526c5c522bf92bed178a078789f6c807b6d34885688024a5bed9e9f8c58d11d4b82487b44c5f470c5606806a0443b79cadb45e0f897a561a53f724e5349b9267c75ca17fe0303020950d53a146bf9c660bc5f4ce8f072465e2d2466434320c1e712272fafc20e342fe7608101580fa1a1a367e60486a7cd1246b7ef5586cf5e10b32762b710a30144f12dd17dd4884904181102000905024dfcb1be021b0c000a0910a86bf526325b21b2904c00a0b2b66b4b39ccffda1d10f3ea8d58f827e30a8b8e009f4255b2d8112a184e40cde43a34e8655ca7809370b0020000" + const signedMessageHex = "a3019bc0cbccc0c4b8d8b74ee2108fe16ec6d3ca490cbe362d3f8333d3f352531472538b8b13d353b97232f352158c20943157c71c16064626063656269052062e4e01987e9b6fccff4b7df3a34c534b23e679cbec3bc0f8f6e64dfb4b55fe3f8efa9ce110ddb5cd79faf1d753c51aecfa669f7e7aa043436596cccc3359cb7dd6bbe9ecaa69e5989d9e57209571edc0b2fa7f57b9b79a64ee6e99ce1371395fee92fec2796f7b15a77c386ff668ee27f6d38f0baa6c438b561657377bf6acff3c5947befd7bf4c196252f1d6e5c524d0300" const signedTextMessageHex = "a3019bc0cbccc8c4b8d8b74ee2108fe16ec6d36a250cbece0c178233d3f352531472538b8b13d35379b97232f352158ca0b4312f57c71c1646462606365626906a062e4e019811591798ff99bf8afee860b0d8a8c2a85c3387e3bcf0bb3b17987f2bbcfab2aa526d930cbfd3d98757184df3995c9f3e7790e36e3e9779f06089d4c64e9e47dd6202cb6e9bc73c5d11bb59fbaf89d22d8dc7cf199ddf17af96e77c5f65f9bbed56f427bd8db7af37f6c9984bf9385efaf5f184f986fb3e6adb0ecfe35bbf92d16a7aa2a344fb0bc52fb7624f0200" const signedEncryptedMessageHex = "848c032a67d68660df41c70103ff5789d0de26b6a50c985a02a13131ca829c413a35d0e6fa8d6842599252162808ac7439c72151c8c6183e76923fe3299301414d0c25a2f06a2257db3839e7df0ec964773f6e4c4ac7ff3b48c444237166dd46ba8ff443a5410dc670cb486672fdbe7c9dfafb75b4fea83af3a204fe2a7dfa86bd20122b4f3d2646cbeecb8f7be8d2c03b018bd210b1d3791e1aba74b0f1034e122ab72e760492c192383cf5e20b5628bd043272d63df9b923f147eb6091cd897553204832aba48fec54aa447547bb16305a1024713b90e77fd0065f1918271947549205af3c74891af22ee0b56cd29bfec6d6e351901cd4ab3ece7c486f1e32a792d4e474aed98ee84b3f591c7dff37b64e0ecd68fd036d517e412dcadf85840ce184ad7921ad446c4ee28db80447aea1ca8d4f574db4d4e37688158ddd19e14ee2eab4873d46947d65d14a23e788d912cf9a19624ca7352469b72a83866b7c23cb5ace3deab3c7018061b0ba0f39ed2befe27163e5083cf9b8271e3e3d52cc7ad6e2a3bd81d4c3d7022f8d" +const signedEncryptedMessage2Hex = "85010e03cf6a7abcd43e36731003fb057f5495b79db367e277cdbe4ab90d924ddee0c0381494112ff8c1238fb0184af35d1731573b01bc4c55ecacd2aafbe2003d36310487d1ecc9ac994f3fada7f9f7f5c3a64248ab7782906c82c6ff1303b69a84d9a9529c31ecafbcdb9ba87e05439897d87e8a2a3dec55e14df19bba7f7bd316291c002ae2efd24f83f9e3441203fc081c0c23dc3092a454ca8a082b27f631abf73aca341686982e8fbda7e0e7d863941d68f3de4a755c2964407f4b5e0477b3196b8c93d551dd23c8beef7d0f03fbb1b6066f78907faf4bf1677d8fcec72651124080e0b7feae6b476e72ab207d38d90b958759fdedfc3c6c35717c9dbfc979b3cfbbff0a76d24a5e57056bb88acbd2a901ef64bc6e4db02adc05b6250ff378de81dca18c1910ab257dff1b9771b85bb9bbe0a69f5989e6d1710a35e6dfcceb7d8fb5ccea8db3932b3d9ff3fe0d327597c68b3622aec8e3716c83a6c93f497543b459b58ba504ed6bcaa747d37d2ca746fe49ae0a6ce4a8b694234e941b5159ff8bd34b9023da2814076163b86f40eed7c9472f81b551452d5ab87004a373c0172ec87ea6ce42ccfa7dbdad66b745496c4873d8019e8c28d6b3" + const symmetricallyEncryptedCompressedHex = "8c0d04030302eb4a03808145d0d260c92f714339e13de5a79881216431925bf67ee2898ea61815f07894cd0703c50d0a76ef64d482196f47a8bc729af9b80bb6" const dsaTestKeyHex = "9901a2044d6c49de110400cb5ce438cf9250907ac2ba5bf6547931270b89f7c4b53d9d09f4d0213a5ef2ec1f26806d3d259960f872a4a102ef1581ea3f6d6882d15134f21ef6a84de933cc34c47cc9106efe3bd84c6aec12e78523661e29bc1a61f0aab17fa58a627fd5fd33f5149153fbe8cd70edf3d963bc287ef875270ff14b5bfdd1bca4483793923b00a0fe46d76cb6e4cbdc568435cd5480af3266d610d303fe33ae8273f30a96d4d34f42fa28ce1112d425b2e3bf7ea553d526e2db6b9255e9dc7419045ce817214d1a0056dbc8d5289956a4b1b69f20f1105124096e6a438f41f2e2495923b0f34b70642607d45559595c7fe94d7fa85fc41bf7d68c1fd509ebeaa5f315f6059a446b9369c277597e4f474a9591535354c7e7f4fd98a08aa60400b130c24ff20bdfbf683313f5daebf1c9b34b3bdadfc77f2ddd72ee1fb17e56c473664bc21d66467655dd74b9005e3a2bacce446f1920cd7017231ae447b67036c9b431b8179deacd5120262d894c26bc015bffe3d827ba7087ad9b700d2ca1f6d16cc1786581e5dd065f293c31209300f9b0afcc3f7c08dd26d0a22d87580b4db41054657374204b65792033202844534129886204131102002205024d6c49de021b03060b090807030206150802090a0b0416020301021e01021780000a0910338934250ccc03607e0400a0bdb9193e8a6b96fc2dfc108ae848914b504481f100a09c4dc148cb693293a67af24dd40d2b13a9e36794" diff --git a/src/pkg/crypto/openpgp/write.go b/src/pkg/crypto/openpgp/write.go index 48c86f604..9884472ce 100644 --- a/src/pkg/crypto/openpgp/write.go +++ b/src/pkg/crypto/openpgp/write.go @@ -9,10 +9,13 @@ import ( "crypto/openpgp/armor" "crypto/openpgp/error" "crypto/openpgp/packet" + "crypto/openpgp/s2k" "crypto/rand" _ "crypto/sha256" + "hash" "io" "os" + "strconv" "time" ) @@ -98,7 +101,7 @@ type FileHints struct { } // SymmetricallyEncrypt acts like gpg -c: it encrypts a file with a passphrase. -// The resulting WriteCloser MUST be closed after the contents of the file have +// The resulting WriteCloser must be closed after the contents of the file have // been written. func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHints) (plaintext io.WriteCloser, err os.Error) { if hints == nil { @@ -115,3 +118,191 @@ func SymmetricallyEncrypt(ciphertext io.Writer, passphrase []byte, hints *FileHi } return packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, hints.EpochSeconds) } + +// intersectPreferences mutates and returns a prefix of a that contains only +// the values in the intersection of a and b. The order of a is preserved. +func intersectPreferences(a []uint8, b []uint8) (intersection []uint8) { + var j int + for _, v := range a { + for _, v2 := range b { + if v == v2 { + a[j] = v + j++ + break + } + } + } + + return a[:j] +} + +func hashToHashId(h crypto.Hash) uint8 { + v, ok := s2k.HashToHashId(h) + if !ok { + panic("tried to convert unknown hash") + } + return v +} + +// Encrypt encrypts a message to a number of recipients and, optionally, signs +// it. hints contains optional information, that is also encrypted, that aids +// the recipients in processing the message. The resulting WriteCloser must +// be closed after the contents of the file have been written. +func Encrypt(ciphertext io.Writer, to []*Entity, signed *Entity, hints *FileHints) (plaintext io.WriteCloser, err os.Error) { + var signer *packet.PrivateKey + if signed != nil { + signer = signed.signingKey().PrivateKey + if signer == nil || signer.Encrypted { + return nil, error.InvalidArgumentError("signing key must be decrypted") + } + } + + // These are the possible ciphers that we'll use for the message. + candidateCiphers := []uint8{ + uint8(packet.CipherAES128), + uint8(packet.CipherAES256), + uint8(packet.CipherCAST5), + } + // These are the possible hash functions that we'll use for the signature. + candidateHashes := []uint8{ + hashToHashId(crypto.SHA256), + hashToHashId(crypto.SHA512), + hashToHashId(crypto.SHA1), + hashToHashId(crypto.RIPEMD160), + } + // In the event that a recipient doesn't specify any supported ciphers + // or hash functions, these are the ones that we assume that every + // implementation supports. + defaultCiphers := candidateCiphers[len(candidateCiphers)-1:] + defaultHashes := candidateHashes[len(candidateHashes)-1:] + + encryptKeys := make([]Key, len(to)) + for i := range to { + encryptKeys[i] = to[i].encryptionKey() + if encryptKeys[i].PublicKey == nil { + return nil, error.InvalidArgumentError("cannot encrypt a message to key id " + strconv.Uitob64(to[i].PrimaryKey.KeyId, 16) + " because it has no encryption keys") + } + + sig := to[i].primaryIdentity().SelfSignature + + preferredSymmetric := sig.PreferredSymmetric + if len(preferredSymmetric) == 0 { + preferredSymmetric = defaultCiphers + } + preferredHashes := sig.PreferredHash + if len(preferredHashes) == 0 { + preferredHashes = defaultHashes + } + candidateCiphers = intersectPreferences(candidateCiphers, preferredSymmetric) + candidateHashes = intersectPreferences(candidateHashes, preferredHashes) + } + + if len(candidateCiphers) == 0 || len(candidateHashes) == 0 { + return nil, error.InvalidArgumentError("cannot encrypt because recipient set shares no common algorithms") + } + + cipher := packet.CipherFunction(candidateCiphers[0]) + hash, _ := s2k.HashIdToHash(candidateHashes[0]) + symKey := make([]byte, cipher.KeySize()) + if _, err := io.ReadFull(rand.Reader, symKey); err != nil { + return nil, err + } + + for _, key := range encryptKeys { + if err := packet.SerializeEncryptedKey(ciphertext, rand.Reader, key.PublicKey, cipher, symKey); err != nil { + return nil, err + } + } + + encryptedData, err := packet.SerializeSymmetricallyEncrypted(ciphertext, cipher, symKey) + if err != nil { + return + } + + if signer != nil { + ops := &packet.OnePassSignature{ + SigType: packet.SigTypeBinary, + Hash: hash, + PubKeyAlgo: signer.PubKeyAlgo, + KeyId: signer.KeyId, + IsLast: true, + } + if err := ops.Serialize(encryptedData); err != nil { + return nil, err + } + } + + if hints == nil { + hints = &FileHints{} + } + + w := encryptedData + if signer != nil { + // If we need to write a signature packet after the literal + // data then we need to stop literalData from closing + // encryptedData. + w = noOpCloser{encryptedData} + + } + literalData, err := packet.SerializeLiteral(w, hints.IsBinary, hints.FileName, hints.EpochSeconds) + if err != nil { + return nil, err + } + + if signer != nil { + return signatureWriter{encryptedData, literalData, hash, hash.New(), signer}, nil + } + return literalData, nil +} + +// signatureWriter hashes the contents of a message while passing it along to +// literalData. When closed, it closes literalData, writes a signature packet +// to encryptedData and then also closes encryptedData. +type signatureWriter struct { + encryptedData io.WriteCloser + literalData io.WriteCloser + hashType crypto.Hash + h hash.Hash + signer *packet.PrivateKey +} + +func (s signatureWriter) Write(data []byte) (int, os.Error) { + s.h.Write(data) + return s.literalData.Write(data) +} + +func (s signatureWriter) Close() os.Error { + sig := &packet.Signature{ + SigType: packet.SigTypeBinary, + PubKeyAlgo: s.signer.PubKeyAlgo, + Hash: s.hashType, + CreationTime: uint32(time.Seconds()), + IssuerKeyId: &s.signer.KeyId, + } + + if err := sig.Sign(s.h, s.signer); err != nil { + return err + } + if err := s.literalData.Close(); err != nil { + return err + } + if err := sig.Serialize(s.encryptedData); err != nil { + return err + } + return s.encryptedData.Close() +} + +// noOpCloser is like an ioutil.NopCloser, but for an io.Writer. +// TODO: we have two of these in OpenPGP packages alone. This probably needs +// to be promoted somewhere more common. +type noOpCloser struct { + w io.Writer +} + +func (c noOpCloser) Write(data []byte) (n int, err os.Error) { + return c.w.Write(data) +} + +func (c noOpCloser) Close() os.Error { + return nil +} diff --git a/src/pkg/crypto/openpgp/write_test.go b/src/pkg/crypto/openpgp/write_test.go index 8551aeb63..c542dfa45 100644 --- a/src/pkg/crypto/openpgp/write_test.go +++ b/src/pkg/crypto/openpgp/write_test.go @@ -9,6 +9,7 @@ import ( "crypto/rand" "os" "io" + "io/ioutil" "testing" "time" ) @@ -120,3 +121,113 @@ func TestSymmetricEncryption(t *testing.T) { t.Errorf("recovered message incorrect got '%s', want '%s'", messageBuf.Bytes(), message) } } + +var testEncryptionTests = []struct { + keyRingHex string + isSigned bool +}{ + { + testKeys1And2PrivateHex, + false, + }, + { + testKeys1And2PrivateHex, + true, + }, + { + dsaElGamalTestKeysHex, + false, + }, + { + dsaElGamalTestKeysHex, + true, + }, +} + +func TestEncryption(t *testing.T) { + for i, test := range testEncryptionTests { + kring, _ := ReadKeyRing(readerFromHex(test.keyRingHex)) + + passphrase := []byte("passphrase") + for _, entity := range kring { + if entity.PrivateKey != nil && entity.PrivateKey.Encrypted { + err := entity.PrivateKey.Decrypt(passphrase) + if err != nil { + t.Errorf("#%d: failed to decrypt key", i) + } + } + for _, subkey := range entity.Subkeys { + if subkey.PrivateKey != nil && subkey.PrivateKey.Encrypted { + err := subkey.PrivateKey.Decrypt(passphrase) + if err != nil { + t.Errorf("#%d: failed to decrypt subkey", i) + } + } + } + } + + var signed *Entity + if test.isSigned { + signed = kring[0] + } + + buf := new(bytes.Buffer) + w, err := Encrypt(buf, kring[:1], signed, nil /* no hints */ ) + if err != nil { + t.Errorf("#%d: error in Encrypt: %s", i, err) + continue + } + + const message = "testing" + _, err = w.Write([]byte(message)) + if err != nil { + t.Errorf("#%d: error writing plaintext: %s", i, err) + continue + } + err = w.Close() + if err != nil { + t.Errorf("#%d: error closing WriteCloser: %s", i, err) + continue + } + + md, err := ReadMessage(buf, kring, nil /* no prompt */ ) + if err != nil { + t.Errorf("#%d: error reading message: %s", i, err) + continue + } + + if test.isSigned { + expectedKeyId := kring[0].signingKey().PublicKey.KeyId + if md.SignedByKeyId != expectedKeyId { + t.Errorf("#%d: message signed by wrong key id, got: %d, want: %d", i, *md.SignedBy, expectedKeyId) + } + if md.SignedBy == nil { + t.Errorf("#%d: failed to find the signing Entity", i) + } + } + + plaintext, err := ioutil.ReadAll(md.UnverifiedBody) + if err != nil { + t.Errorf("#%d: error reading encrypted contents: %s", i, err) + continue + } + + expectedKeyId := kring[0].encryptionKey().PublicKey.KeyId + if len(md.EncryptedToKeyIds) != 1 || md.EncryptedToKeyIds[0] != expectedKeyId { + t.Errorf("#%d: expected message to be encrypted to %v, but got %#v", i, expectedKeyId, md.EncryptedToKeyIds) + } + + if string(plaintext) != message { + t.Errorf("#%d: got: %s, want: %s", i, string(plaintext), message) + } + + if test.isSigned { + if md.SignatureError != nil { + t.Errorf("#%d: signature error: %s", i, err) + } + if md.Signature == nil { + t.Error("signature missing") + } + } + } +} diff --git a/src/pkg/crypto/rand/rand_windows.go b/src/pkg/crypto/rand/rand_windows.go index 281d6dc6a..0eab6b213 100755 --- a/src/pkg/crypto/rand/rand_windows.go +++ b/src/pkg/crypto/rand/rand_windows.go @@ -19,7 +19,7 @@ func init() { Reader = &rngReader{} } // A rngReader satisfies reads by reading from the Windows CryptGenRandom API. type rngReader struct { - prov uint32 + prov syscall.Handle mu sync.Mutex } diff --git a/src/pkg/crypto/rsa/pkcs1v15.go b/src/pkg/crypto/rsa/pkcs1v15.go index 3defa62ea..600623114 100644 --- a/src/pkg/crypto/rsa/pkcs1v15.go +++ b/src/pkg/crypto/rsa/pkcs1v15.go @@ -232,11 +232,11 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) func pkcs1v15HashInfo(hash crypto.Hash, inLen int) (hashLen int, prefix []byte, err os.Error) { hashLen = hash.Size() if inLen != hashLen { - return 0, nil, os.ErrorString("input must be hashed message") + return 0, nil, os.NewError("input must be hashed message") } prefix, ok := hashPrefixes[hash] if !ok { - return 0, nil, os.ErrorString("unsupported hash function") + return 0, nil, os.NewError("unsupported hash function") } return } diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go index 380f71570..6957659f2 100644 --- a/src/pkg/crypto/rsa/rsa.go +++ b/src/pkg/crypto/rsa/rsa.go @@ -64,7 +64,7 @@ func (priv *PrivateKey) Validate() os.Error { // easy for an attack to generate composites that pass this test. for _, prime := range priv.Primes { if !big.ProbablyPrime(prime, 20) { - return os.ErrorString("prime factor is composite") + return os.NewError("prime factor is composite") } } @@ -74,7 +74,7 @@ func (priv *PrivateKey) Validate() os.Error { modulus.Mul(modulus, prime) } if modulus.Cmp(priv.N) != 0 { - return os.ErrorString("invalid modulus") + return os.NewError("invalid modulus") } // Check that e and totient(Πprimes) are coprime. totient := new(big.Int).Set(bigOne) @@ -88,13 +88,13 @@ func (priv *PrivateKey) Validate() os.Error { y := new(big.Int) big.GcdInt(gcd, x, y, totient, e) if gcd.Cmp(bigOne) != 0 { - return os.ErrorString("invalid public exponent E") + return os.NewError("invalid public exponent E") } // Check that de ≡ 1 (mod totient(Πprimes)) de := new(big.Int).Mul(priv.D, e) de.Mod(de, totient) if de.Cmp(bigOne) != 0 { - return os.ErrorString("invalid private exponent D") + return os.NewError("invalid private exponent D") } return nil } @@ -127,7 +127,7 @@ func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (priv *Priva priv.E = 3 if nprimes < 2 { - return nil, os.ErrorString("rsa.GenerateMultiPrimeKey: nprimes must be >= 2") + return nil, os.NewError("rsa.GenerateMultiPrimeKey: nprimes must be >= 2") } primes := make([]*big.Int, nprimes) diff --git a/src/pkg/crypto/tls/conn.go b/src/pkg/crypto/tls/conn.go index 097e182bd..fac65afd9 100644 --- a/src/pkg/crypto/tls/conn.go +++ b/src/pkg/crypto/tls/conn.go @@ -790,10 +790,10 @@ func (c *Conn) VerifyHostname(host string) os.Error { c.handshakeMutex.Lock() defer c.handshakeMutex.Unlock() if !c.isClient { - return os.ErrorString("VerifyHostname called on TLS server connection") + return os.NewError("VerifyHostname called on TLS server connection") } if !c.handshakeComplete { - return os.ErrorString("TLS handshake has not yet been performed") + return os.NewError("TLS handshake has not yet been performed") } return c.peerCertificates[0].VerifyHostname(host) } diff --git a/src/pkg/crypto/tls/generate_cert.go b/src/pkg/crypto/tls/generate_cert.go index f46188879..41206e276 100644 --- a/src/pkg/crypto/tls/generate_cert.go +++ b/src/pkg/crypto/tls/generate_cert.go @@ -8,8 +8,10 @@ package main import ( - "crypto/rsa" + "big" + "crypto/x509/pkix" "crypto/rand" + "crypto/rsa" "crypto/x509" "encoding/pem" "flag" @@ -32,8 +34,8 @@ func main() { now := time.Seconds() template := x509.Certificate{ - SerialNumber: []byte{0}, - Subject: x509.Name{ + SerialNumber: new(big.Int).SetInt64(0), + Subject: pkix.Name{ CommonName: *hostName, Organization: []string{"Acme Co"}, }, diff --git a/src/pkg/crypto/tls/handshake_client.go b/src/pkg/crypto/tls/handshake_client.go index c758c96d4..15604cea7 100644 --- a/src/pkg/crypto/tls/handshake_client.go +++ b/src/pkg/crypto/tls/handshake_client.go @@ -40,7 +40,7 @@ func (c *Conn) clientHandshake() os.Error { _, err := io.ReadFull(c.config.rand(), hello.random[4:]) if err != nil { c.sendAlert(alertInternalError) - return os.ErrorString("short read from Rand") + return os.NewError("short read from Rand") } finishedHash.Write(hello.marshal()) @@ -69,7 +69,7 @@ func (c *Conn) clientHandshake() os.Error { if !hello.nextProtoNeg && serverHello.nextProtoNeg { c.sendAlert(alertHandshakeFailure) - return os.ErrorString("server advertised unrequested NPN") + return os.NewError("server advertised unrequested NPN") } suite, suiteId := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite) @@ -92,7 +92,7 @@ func (c *Conn) clientHandshake() os.Error { cert, err := x509.ParseCertificate(asn1Data) if err != nil { c.sendAlert(alertBadCertificate) - return os.ErrorString("failed to parse certificate from server: " + err.String()) + return os.NewError("failed to parse certificate from server: " + err.String()) } certs[i] = cert } diff --git a/src/pkg/crypto/tls/handshake_server.go b/src/pkg/crypto/tls/handshake_server.go index e9431c6fa..44a324041 100644 --- a/src/pkg/crypto/tls/handshake_server.go +++ b/src/pkg/crypto/tls/handshake_server.go @@ -173,7 +173,7 @@ FindCipherSuite: cert, err := x509.ParseCertificate(asn1Data) if err != nil { c.sendAlert(alertBadCertificate) - return os.ErrorString("could not parse client's certificate: " + err.String()) + return os.NewError("could not parse client's certificate: " + err.String()) } certs[i] = cert } @@ -182,7 +182,7 @@ FindCipherSuite: for i := 1; i < len(certs); i++ { if err := certs[i-1].CheckSignatureFrom(certs[i]); err != nil { c.sendAlert(alertBadCertificate) - return os.ErrorString("could not validate certificate signature: " + err.String()) + return os.NewError("could not validate certificate signature: " + err.String()) } } @@ -229,7 +229,7 @@ FindCipherSuite: err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, digest, certVerify.signature) if err != nil { c.sendAlert(alertBadCertificate) - return os.ErrorString("could not validate signature of connection nonces: " + err.String()) + return os.NewError("could not validate signature of connection nonces: " + err.String()) } finishedHash.Write(certVerify.marshal()) diff --git a/src/pkg/crypto/tls/key_agreement.go b/src/pkg/crypto/tls/key_agreement.go index 84f90c45a..48472fb6a 100644 --- a/src/pkg/crypto/tls/key_agreement.go +++ b/src/pkg/crypto/tls/key_agreement.go @@ -32,11 +32,11 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKe } if len(ckx.ciphertext) < 2 { - return nil, os.ErrorString("bad ClientKeyExchange") + return nil, os.NewError("bad ClientKeyExchange") } ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1]) if ciphertextLen != len(ckx.ciphertext)-2 { - return nil, os.ErrorString("bad ClientKeyExchange") + return nil, os.NewError("bad ClientKeyExchange") } ciphertext := ckx.ciphertext[2:] @@ -54,7 +54,7 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKe } func (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) os.Error { - return os.ErrorString("unexpected ServerKeyExchange") + return os.NewError("unexpected ServerKeyExchange") } func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, os.Error) { @@ -146,7 +146,7 @@ Curve: md5sha1 := md5SHA1Hash(clientHello.random, hello.random, serverECDHParams) sig, err := rsa.SignPKCS1v15(config.rand(), config.Certificates[0].PrivateKey, crypto.MD5SHA1, md5sha1) if err != nil { - return nil, os.ErrorString("failed to sign ECDHE parameters: " + err.String()) + return nil, os.NewError("failed to sign ECDHE parameters: " + err.String()) } skx := new(serverKeyExchangeMsg) @@ -162,11 +162,11 @@ Curve: func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg) ([]byte, os.Error) { if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 { - return nil, os.ErrorString("bad ClientKeyExchange") + return nil, os.NewError("bad ClientKeyExchange") } x, y := ka.curve.Unmarshal(ckx.ciphertext[1:]) if x == nil { - return nil, os.ErrorString("bad ClientKeyExchange") + return nil, os.NewError("bad ClientKeyExchange") } x, _ = ka.curve.ScalarMult(x, y, ka.privateKey) preMasterSecret := make([]byte, (ka.curve.BitSize+7)>>3) @@ -176,12 +176,14 @@ func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *cl return preMasterSecret, nil } +var errServerKeyExchange = os.NewError("invalid ServerKeyExchange") + func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) os.Error { if len(skx.key) < 4 { - goto Error + return errServerKeyExchange } if skx.key[0] != 3 { // named curve - return os.ErrorString("server selected unsupported curve") + return os.NewError("server selected unsupported curve") } curveid := uint16(skx.key[1])<<8 | uint16(skx.key[2]) @@ -193,39 +195,36 @@ func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientH case curveP521: ka.curve = elliptic.P521() default: - return os.ErrorString("server selected unsupported curve") + return os.NewError("server selected unsupported curve") } publicLen := int(skx.key[3]) if publicLen+4 > len(skx.key) { - goto Error + return errServerKeyExchange } ka.x, ka.y = ka.curve.Unmarshal(skx.key[4 : 4+publicLen]) if ka.x == nil { - goto Error + return errServerKeyExchange } serverECDHParams := skx.key[:4+publicLen] sig := skx.key[4+publicLen:] if len(sig) < 2 { - goto Error + return errServerKeyExchange } sigLen := int(sig[0])<<8 | int(sig[1]) if sigLen+2 != len(sig) { - goto Error + return errServerKeyExchange } sig = sig[2:] md5sha1 := md5SHA1Hash(clientHello.random, serverHello.random, serverECDHParams) return rsa.VerifyPKCS1v15(cert.PublicKey.(*rsa.PublicKey), crypto.MD5SHA1, md5sha1, sig) - -Error: - return os.ErrorString("invalid ServerKeyExchange") } func (ka *ecdheRSAKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, os.Error) { if ka.curve == nil { - return nil, nil, os.ErrorString("missing ServerKeyExchange message") + return nil, nil, os.NewError("missing ServerKeyExchange message") } priv, mx, my, err := ka.curve.GenerateKey(config.rand()) if err != nil { diff --git a/src/pkg/crypto/tls/tls.go b/src/pkg/crypto/tls/tls.go index 9e5c9270a..4f0859fee 100644 --- a/src/pkg/crypto/tls/tls.go +++ b/src/pkg/crypto/tls/tls.go @@ -147,19 +147,19 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err os.Err } if len(cert.Certificate) == 0 { - err = os.ErrorString("crypto/tls: failed to parse certificate PEM data") + err = os.NewError("crypto/tls: failed to parse certificate PEM data") return } keyDERBlock, _ := pem.Decode(keyPEMBlock) if keyDERBlock == nil { - err = os.ErrorString("crypto/tls: failed to parse key PEM data") + err = os.NewError("crypto/tls: failed to parse key PEM data") return } key, err := x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes) if err != nil { - err = os.ErrorString("crypto/tls: failed to parse key: " + err.String()) + err = os.NewError("crypto/tls: failed to parse key: " + err.String()) return } @@ -173,7 +173,7 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err os.Err } if x509Cert.PublicKeyAlgorithm != x509.RSA || x509Cert.PublicKey.(*rsa.PublicKey).N.Cmp(key.PublicKey.N) != 0 { - err = os.ErrorString("crypto/tls: private key does not match public key") + err = os.NewError("crypto/tls: private key does not match public key") return } diff --git a/src/pkg/crypto/twofish/twofish.go b/src/pkg/crypto/twofish/twofish.go index 1a1aac9b9..2e537c606 100644 --- a/src/pkg/crypto/twofish/twofish.go +++ b/src/pkg/crypto/twofish/twofish.go @@ -269,7 +269,7 @@ func h(in, key []byte, offset int) uint32 { // Encrypt encrypts a 16-byte block from src to dst, which may overlap. // Note that for amounts of data larger than a block, // it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/block/cbc.go). +// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). func (c *Cipher) Encrypt(dst, src []byte) { S1 := c.s[0] S2 := c.s[1] diff --git a/src/pkg/crypto/x509/pkix/pkix.go b/src/pkg/crypto/x509/pkix/pkix.go index 7806b2a2e..266fd557a 100644 --- a/src/pkg/crypto/x509/pkix/pkix.go +++ b/src/pkg/crypto/x509/pkix/pkix.go @@ -16,7 +16,7 @@ import ( // 5280, section 4.1.1.2. type AlgorithmIdentifier struct { Algorithm asn1.ObjectIdentifier - Parameters asn1.RawValue "optional" + Parameters asn1.RawValue `asn1:"optional"` } type RDNSequence []RelativeDistinguishedNameSET @@ -32,7 +32,7 @@ type AttributeTypeAndValue struct { // 5280, section 4.2. type Extension struct { Id asn1.ObjectIdentifier - Critical bool "optional" + Critical bool `asn1:"optional"` Value []byte } @@ -149,13 +149,13 @@ func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool { // 5280, section 5.1. type TBSCertificateList struct { Raw asn1.RawContent - Version int "optional,default:2" + Version int `asn1:"optional,default:2"` Signature AlgorithmIdentifier Issuer RDNSequence ThisUpdate *time.Time NextUpdate *time.Time - RevokedCertificates []RevokedCertificate "optional" - Extensions []Extension "tag:0,optional,explicit" + RevokedCertificates []RevokedCertificate `asn1:"optional"` + Extensions []Extension `asn1:"tag:0,optional,explicit"` } // RevokedCertificate represents the ASN.1 structure of the same name. See RFC @@ -163,5 +163,5 @@ type TBSCertificateList struct { type RevokedCertificate struct { SerialNumber *big.Int RevocationTime *time.Time - Extensions []Extension "optional" + Extensions []Extension `asn1:"optional"` } diff --git a/src/pkg/crypto/x509/verify.go b/src/pkg/crypto/x509/verify.go index 9145880a2..cad863db8 100644 --- a/src/pkg/crypto/x509/verify.go +++ b/src/pkg/crypto/x509/verify.go @@ -171,8 +171,14 @@ func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain [ chains = append(chains, appendToFreshChain(currentChain, root)) } +nextIntermediate: for _, intermediateNum := range opts.Intermediates.findVerifiedParents(c) { intermediate := opts.Intermediates.certs[intermediateNum] + for _, cert := range currentChain { + if cert == intermediate { + continue nextIntermediate + } + } err = intermediate.isValid(intermediateCertificate, opts) if err != nil { continue @@ -202,8 +208,8 @@ func matchHostnames(pattern, host string) bool { return false } - patternParts := strings.Split(pattern, ".", -1) - hostParts := strings.Split(host, ".", -1) + patternParts := strings.Split(pattern, ".") + hostParts := strings.Split(host, ".") if len(patternParts) != len(hostParts) { return false diff --git a/src/pkg/crypto/x509/verify_test.go b/src/pkg/crypto/x509/verify_test.go index 6a103dcfb..111f60eb1 100644 --- a/src/pkg/crypto/x509/verify_test.go +++ b/src/pkg/crypto/x509/verify_test.go @@ -72,23 +72,24 @@ var verifyTests = []verifyTest{ }, }, { - leaf: googleLeaf, - intermediates: []string{verisignRoot, thawteIntermediate}, - roots: []string{verisignRoot}, + leaf: dnssecExpLeaf, + intermediates: []string{startComIntermediate}, + roots: []string{startComRoot}, currentTime: 1302726541, expectedChains: [][]string{ - []string{"Google", "Thawte", "VeriSign"}, + []string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"}, }, }, { leaf: dnssecExpLeaf, - intermediates: []string{startComIntermediate}, + intermediates: []string{startComIntermediate, startComRoot}, roots: []string{startComRoot}, currentTime: 1302726541, expectedChains: [][]string{ []string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"}, + []string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"}, }, }, } @@ -120,7 +121,7 @@ func expectAuthorityUnknown(t *testing.T, i int, err os.Error) (ok bool) { func certificateFromPEM(pemBytes string) (*Certificate, os.Error) { block, _ := pem.Decode([]byte(pemBytes)) if block == nil { - return nil, os.ErrorString("failed to decode PEM") + return nil, os.NewError("failed to decode PEM") } return ParseCertificate(block.Bytes) } diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go index b10ffb0a2..348727a26 100644 --- a/src/pkg/crypto/x509/x509.go +++ b/src/pkg/crypto/x509/x509.go @@ -30,11 +30,11 @@ type pkcs1PrivateKey struct { P *big.Int Q *big.Int // We ignore these values, if present, because rsa will calculate them. - Dp *big.Int "optional" - Dq *big.Int "optional" - Qinv *big.Int "optional" + Dp *big.Int `asn1:"optional"` + Dq *big.Int `asn1:"optional"` + Qinv *big.Int `asn1:"optional"` - AdditionalPrimes []pkcs1AdditionalRSAPrime "optional" + AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional"` } type pkcs1AdditionalRSAPrime struct { @@ -58,11 +58,11 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) { } if priv.Version > 1 { - return nil, os.ErrorString("x509: unsupported private key version") + return nil, os.NewError("x509: unsupported private key version") } if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 { - return nil, os.ErrorString("private key contains zero or negative value") + return nil, os.NewError("private key contains zero or negative value") } key = new(rsa.PrivateKey) @@ -77,7 +77,7 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) { key.Primes[1] = priv.Q for i, a := range priv.AdditionalPrimes { if a.Prime.Sign() <= 0 { - return nil, os.ErrorString("private key contains zero or negative prime") + return nil, os.NewError("private key contains zero or negative prime") } key.Primes[i+2] = a.Prime // We ignore the other two values because rsa will calculate @@ -136,16 +136,16 @@ type certificate struct { type tbsCertificate struct { Raw asn1.RawContent - Version int "optional,explicit,default:1,tag:0" + Version int `asn1:"optional,explicit,default:1,tag:0"` SerialNumber *big.Int SignatureAlgorithm pkix.AlgorithmIdentifier Issuer pkix.RDNSequence Validity validity Subject pkix.RDNSequence PublicKey publicKeyInfo - UniqueId asn1.BitString "optional,tag:1" - SubjectUniqueId asn1.BitString "optional,tag:2" - Extensions []pkix.Extension "optional,explicit,tag:3" + UniqueId asn1.BitString `asn1:"optional,tag:1"` + SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"` + Extensions []pkix.Extension `asn1:"optional,explicit,tag:3"` } type dsaAlgorithmParameters struct { @@ -168,7 +168,7 @@ type publicKeyInfo struct { // RFC 5280, 4.2.1.1 type authKeyId struct { - Id []byte "optional,tag:0" + Id []byte `asn1:"optional,tag:0"` } type SignatureAlgorithm int @@ -457,10 +457,10 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature return err } if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 { - return os.ErrorString("DSA signature contained zero or negative values") + return os.NewError("DSA signature contained zero or negative values") } if !dsa.Verify(pub, digest, dsaSig.R, dsaSig.S) { - return os.ErrorString("DSA verification failure") + return os.NewError("DSA verification failure") } return } @@ -480,8 +480,8 @@ func (h UnhandledCriticalExtension) String() string { } type basicConstraints struct { - IsCA bool "optional" - MaxPathLen int "optional" + IsCA bool `asn1:"optional"` + MaxPathLen int `asn1:"optional"` } type rsaPublicKey struct { @@ -497,14 +497,14 @@ type policyInformation struct { // RFC 5280, 4.2.1.10 type nameConstraints struct { - Permitted []generalSubtree "optional,tag:0" - Excluded []generalSubtree "optional,tag:1" + Permitted []generalSubtree `asn1:"optional,tag:0"` + Excluded []generalSubtree `asn1:"optional,tag:1"` } type generalSubtree struct { - Name string "tag:2,optional,ia5" - Min int "optional,tag:0" - Max int "optional,tag:1" + Name string `asn1:"tag:2,optional,ia5"` + Min int `asn1:"optional,tag:0"` + Max int `asn1:"optional,tag:1"` } func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, os.Error) { @@ -535,7 +535,7 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{ return nil, err } if p.Sign() <= 0 || params.P.Sign() <= 0 || params.Q.Sign() <= 0 || params.G.Sign() <= 0 { - return nil, os.ErrorString("zero or negative DSA parameter") + return nil, os.NewError("zero or negative DSA parameter") } pub := &dsa.PublicKey{ Parameters: dsa.Parameters{ @@ -571,7 +571,7 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { } if in.TBSCertificate.SerialNumber.Sign() < 0 { - return nil, os.ErrorString("negative serial number") + return nil, os.NewError("negative serial number") } out.Version = in.TBSCertificate.Version + 1 diff --git a/src/pkg/crypto/xtea/cipher.go b/src/pkg/crypto/xtea/cipher.go index f2a5da003..b3fba3c84 100644 --- a/src/pkg/crypto/xtea/cipher.go +++ b/src/pkg/crypto/xtea/cipher.go @@ -48,13 +48,13 @@ func NewCipher(key []byte) (*Cipher, os.Error) { // BlockSize returns the XTEA block size, 8 bytes. // It is necessary to satisfy the Cipher interface in the -// package "crypto/block". +// package "crypto/cipher". func (c *Cipher) BlockSize() int { return BlockSize } // Encrypt encrypts the 8 byte buffer src using the key and stores the result in dst. // Note that for amounts of data larger than a block, // it is not safe to just call Encrypt on successive blocks; -// instead, use an encryption mode like CBC (see crypto/block/cbc.go). +// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go). func (c *Cipher) Encrypt(dst, src []byte) { encryptBlock(c, dst, src) } // Decrypt decrypts the 8 byte buffer src using the key k and stores the result in dst. diff --git a/src/pkg/csv/Makefile b/src/pkg/csv/Makefile new file mode 100644 index 000000000..e364d51d2 --- /dev/null +++ b/src/pkg/csv/Makefile @@ -0,0 +1,12 @@ +# Copyright 2011 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../Make.inc + +TARG=csv +GOFILES=\ + reader.go\ + writer.go\ + +include ../../Make.pkg diff --git a/src/pkg/csv/reader.go b/src/pkg/csv/reader.go new file mode 100644 index 000000000..1f4b61cf9 --- /dev/null +++ b/src/pkg/csv/reader.go @@ -0,0 +1,373 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package csv reads and writes comma-separated values (CSV) files. +// +// A csv file contains zero or more records of one or more fields per record. +// Each record is separated by the newline character. The final record may +// optionally be followed by a newline character. +// +// field1,field2,field3 +// +// White space is considered part of a field. +// +// Carriage returns before newline characters are silently removed. +// +// Blank lines are ignored. A line with only whitespace characters (excluding +// the ending newline character) is not considered a blank line. +// +// Fields which start and stop with the quote character " are called +// quoted-fields. The beginning and ending quote are not part of the +// field. +// +// The source: +// +// normal string,"quoted-field" +// +// results in the fields +// +// {`normal string`, `quoted-field`} +// +// Within a quoted-field a quote character followed by a second quote +// character is considered a single quote. +// +// "the ""word"" is true","a ""quoted-field""" +// +// results in +// +// {`the "word" is true`, `a "quoted-field"`} +// +// Newlines and commas may be included in a quoted-field +// +// "Multi-line +// field","comma is ," +// +// results in +// +// {`Multi-line +// field`, `comma is ,`} +package csv + +import ( + "bufio" + "bytes" + "fmt" + "io" + "os" + "unicode" +) + +// A ParseError is returned for parsing errors. +// The first line is 1. The first column is 0. +type ParseError struct { + Line int // Line where the error occurred + Column int // Column (rune index) where the error occurred + Error os.Error // The actual error +} + +func (e *ParseError) String() string { + return fmt.Sprintf("line %d, column %d: %s", e.Line, e.Column, e.Error) +} + +// These are the errors that can be returned in ParseError.Error +var ( + ErrTrailingComma = os.NewError("extra delimiter at end of line") + ErrBareQuote = os.NewError("bare \" in non-quoted-field") + ErrQuote = os.NewError("extraneous \" in field") + ErrFieldCount = os.NewError("wrong number of fields in line") +) + +// A Reader reads records from a CSV-encoded file. +// +// As returned by NewReader, a Reader expects input conforming to RFC 4180. +// The exported fields can be changed to customize the details before the +// first call to Read or ReadAll. +// +// Comma is the field delimiter. It defaults to ','. +// +// Comment, if not 0, is the comment character. Lines beginning with the +// Comment character is ignored. +// +// If FieldsPerRecord is positive, Read requires each record to +// have the given number of fields. If FieldsPerRecord is 0, Read sets it to +// the number of fields in the first record, so that future records must +// have the same field count. +// +// If LazyQuotes is true, a quote may appear in an unquoted field and a +// non-doubled quote may appear in a quoted field. +// +// If TrailingComma is true, the last field may be a unquoted empty field. +// +// If TrimLeadingSpace is true, leading white space in a field is ignored. +type Reader struct { + Comma int // Field delimiter (set to ',' by NewReader) + Comment int // Comment character for start of line + FieldsPerRecord int // Number of expected fields per record + LazyQuotes bool // Allow lazy quotes + TrailingComma bool // Allow trailing comma + TrimLeadingSpace bool // Trim leading space + line int + column int + r *bufio.Reader + field bytes.Buffer +} + +// NewReader returns a new Reader that reads from r. +func NewReader(r io.Reader) *Reader { + return &Reader{ + Comma: ',', + r: bufio.NewReader(r), + } +} + +// error creates a new ParseError based on err. +func (r *Reader) error(err os.Error) os.Error { + return &ParseError{ + Line: r.line, + Column: r.column, + Error: err, + } +} + +// Read reads one record from r. The record is a slice of strings with each +// string representing one field. +func (r *Reader) Read() (record []string, err os.Error) { + for { + record, err = r.parseRecord() + if record != nil { + break + } + if err != nil { + return nil, err + } + } + + if r.FieldsPerRecord > 0 { + if len(record) != r.FieldsPerRecord { + r.column = 0 // report at start of record + return record, r.error(ErrFieldCount) + } + } else if r.FieldsPerRecord == 0 { + r.FieldsPerRecord = len(record) + } + return record, nil +} + +// ReadAll reads all the remaining records from r. +// Each record is a slice of fields. +func (r *Reader) ReadAll() (records [][]string, err os.Error) { + for { + record, err := r.Read() + if err == os.EOF { + return records, nil + } + if err != nil { + return nil, err + } + records = append(records, record) + } + panic("unreachable") +} + +// readRune reads one rune from r, folding \r\n to \n and keeping track +// of our far into the line we have read. r.column will point to the start +// of this rune, not the end of this rune. +func (r *Reader) readRune() (int, os.Error) { + rune, _, err := r.r.ReadRune() + + // Handle \r\n here. We make the simplifying assumption that + // anytime \r is followed by \n that it can be folded to \n. + // We will not detect files which contain both \r\n and bare \n. + if rune == '\r' { + rune, _, err = r.r.ReadRune() + if err == nil { + if rune != '\n' { + r.r.UnreadRune() + rune = '\r' + } + } + } + r.column++ + return rune, err +} + +// unreadRune puts the last rune read from r back. +func (r *Reader) unreadRune() { + r.r.UnreadRune() + r.column-- +} + +// skip reads runes up to and including the rune delim or until error. +func (r *Reader) skip(delim int) os.Error { + for { + rune, err := r.readRune() + if err != nil { + return err + } + if rune == delim { + return nil + } + } + panic("unreachable") +} + +// parseRecord reads and parses a single csv record from r. +func (r *Reader) parseRecord() (fields []string, err os.Error) { + // Each record starts on a new line. We increment our line + // number (lines start at 1, not 0) and set column to -1 + // so as we increment in readRune it points to the character we read. + r.line++ + r.column = -1 + + // Peek at the first rune. If it is an error we are done. + // If we are support comments and it is the comment character + // the skip to the end of line. + + rune, _, err := r.r.ReadRune() + if err != nil { + return nil, err + } + + if r.Comment != 0 && rune == r.Comment { + return nil, r.skip('\n') + } + r.r.UnreadRune() + + // At this point we have at least one field. + for { + haveField, delim, err := r.parseField() + if haveField { + fields = append(fields, r.field.String()) + } + if delim == '\n' || err == os.EOF { + return fields, err + } else if err != nil { + return nil, err + } + } + panic("unreachable") +} + + +// parseField parses the next field in the record. The read field is +// located in r.field. Delim is the first character not part of the field +// (r.Comma or '\n'). +func (r *Reader) parseField() (haveField bool, delim int, err os.Error) { + r.field.Reset() + + rune, err := r.readRune() + if err != nil { + // If we have EOF and are not at the start of a line + // then we return the empty field. We have already + // checked for trailing commas if needed. + if err == os.EOF && r.column != 0 { + return true, 0, err + } + return false, 0, err + } + + if r.TrimLeadingSpace { + for unicode.IsSpace(rune) { + rune, err = r.readRune() + if err != nil { + return false, 0, err + } + } + } + + switch rune { + case r.Comma: + // will check below + + case '\n': + // We are a trailing empty field or a blank linke + if r.column == 0 { + return false, rune, nil + } + return true, rune, nil + + case '"': + // quoted field + Quoted: + for { + rune, err = r.readRune() + if err != nil { + if err == os.EOF { + if r.LazyQuotes { + return true, 0, err + } + return false, 0, r.error(ErrQuote) + } + return false, 0, err + } + switch rune { + case '"': + rune, err = r.readRune() + if err != nil || rune == r.Comma { + break Quoted + } + if rune == '\n' { + return true, rune, nil + } + if rune != '"' { + if !r.LazyQuotes { + r.column-- + return false, 0, r.error(ErrQuote) + } + // accept the bare quote + r.field.WriteRune('"') + } + case '\n': + r.line++ + r.column = -1 + } + r.field.WriteRune(rune) + } + + default: + // unquoted field + for { + r.field.WriteRune(rune) + rune, err = r.readRune() + if err != nil || rune == r.Comma { + break + } + if rune == '\n' { + return true, rune, nil + } + if !r.LazyQuotes && rune == '"' { + return false, 0, r.error(ErrBareQuote) + } + } + } + + if err != nil { + if err == os.EOF { + return true, 0, err + } + return false, 0, err + } + + if !r.TrailingComma { + // We don't allow trailing commas. See if we + // are at the end of the line (being mindful + // of triming spaces + c := r.column + rune, err = r.readRune() + if r.TrimLeadingSpace { + for unicode.IsSpace(rune) { + rune, err = r.readRune() + if err != nil { + break + } + } + } + if err == os.EOF || rune == '\n' { + r.column = c // report the comma + return false, 0, r.error(ErrTrailingComma) + } + r.unreadRune() + } + return true, rune, nil +} diff --git a/src/pkg/csv/reader_test.go b/src/pkg/csv/reader_test.go new file mode 100644 index 000000000..0068bad1d --- /dev/null +++ b/src/pkg/csv/reader_test.go @@ -0,0 +1,265 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package csv + +import ( + "reflect" + "strings" + "testing" +) + +var readTests = []struct { + Name string + Input string + Output [][]string + UseFieldsPerRecord bool // false (default) means FieldsPerRecord is -1 + + // These fields are copied into the Reader + Comma int + Comment int + FieldsPerRecord int + LazyQuotes bool + TrailingComma bool + TrimLeadingSpace bool + + Error string + Line int // Expected error line if != 0 + Column int // Expected error column if line != 0 +}{ + { + Name: "Simple", + Input: "a,b,c\n", + Output: [][]string{{"a", "b", "c"}}, + }, + { + Name: "CRLF", + Input: "a,b\r\nc,d\r\n", + Output: [][]string{{"a", "b"}, {"c", "d"}}, + }, + { + Name: "BareCR", + Input: "a,b\rc,d\r\n", + Output: [][]string{{"a", "b\rc", "d"}}, + }, + { + Name: "RFC4180test", + UseFieldsPerRecord: true, + Input: `#field1,field2,field3 +"aaa","bb +b","ccc" +"a,a","b""bb","ccc" +zzz,yyy,xxx +`, + Output: [][]string{ + {"#field1", "field2", "field3"}, + {"aaa", "bb\nb", "ccc"}, + {"a,a", `b"bb`, "ccc"}, + {"zzz", "yyy", "xxx"}, + }, + }, + { + Name: "NoEOLTest", + Input: "a,b,c", + Output: [][]string{{"a", "b", "c"}}, + }, + { + Name: "Semicolon", + Comma: ';', + Input: "a;b;c\n", + Output: [][]string{{"a", "b", "c"}}, + }, + { + Name: "MultiLine", + Input: `"two +line","one line","three +line +field"`, + Output: [][]string{{"two\nline", "one line", "three\nline\nfield"}}, + }, + { + Name: "BlankLine", + Input: "a,b,c\n\nd,e,f\n\n", + Output: [][]string{ + {"a", "b", "c"}, + {"d", "e", "f"}, + }, + }, + { + Name: "TrimSpace", + Input: " a, b, c\n", + TrimLeadingSpace: true, + Output: [][]string{{"a", "b", "c"}}, + }, + { + Name: "LeadingSpace", + Input: " a, b, c\n", + Output: [][]string{{" a", " b", " c"}}, + }, + { + Name: "Comment", + Comment: '#', + Input: "#1,2,3\na,b,c\n#comment", + Output: [][]string{{"a", "b", "c"}}, + }, + { + Name: "NoComment", + Input: "#1,2,3\na,b,c", + Output: [][]string{{"#1", "2", "3"}, {"a", "b", "c"}}, + }, + { + Name: "LazyQuotes", + LazyQuotes: true, + Input: `a "word","1"2",a","b`, + Output: [][]string{{`a "word"`, `1"2`, `a"`, `b`}}, + }, + { + Name: "BareQuotes", + LazyQuotes: true, + Input: `a "word","1"2",a"`, + Output: [][]string{{`a "word"`, `1"2`, `a"`}}, + }, + { + Name: "BareDoubleQuotes", + LazyQuotes: true, + Input: `a""b,c`, + Output: [][]string{{`a""b`, `c`}}, + }, + { + Name: "BadDoubleQuotes", + Input: `a""b,c`, + Output: [][]string{{`a""b`, `c`}}, + Error: `bare " in non-quoted-field`, Line: 1, Column: 1, + }, + { + Name: "TrimQuote", + Input: ` "a"," b",c`, + TrimLeadingSpace: true, + Output: [][]string{{"a", " b", "c"}}, + }, + { + Name: "BadBareQuote", + Input: `a "word","b"`, + Error: `bare " in non-quoted-field`, Line: 1, Column: 2, + }, + { + Name: "BadTrailingQuote", + Input: `"a word",b"`, + Error: `bare " in non-quoted-field`, Line: 1, Column: 10, + }, + { + Name: "ExtraneousQuote", + Input: `"a "word","b"`, + Error: `extraneous " in field`, Line: 1, Column: 3, + }, + { + Name: "BadFieldCount", + UseFieldsPerRecord: true, + Input: "a,b,c\nd,e", + Error: "wrong number of fields", Line: 2, + }, + { + Name: "BadFieldCount1", + UseFieldsPerRecord: true, + FieldsPerRecord: 2, + Input: `a,b,c`, + Error: "wrong number of fields", Line: 1, + }, + { + Name: "FieldCount", + Input: "a,b,c\nd,e", + Output: [][]string{{"a", "b", "c"}, {"d", "e"}}, + }, + { + Name: "BadTrailingCommaEOF", + Input: "a,b,c,", + Error: "extra delimiter at end of line", Line: 1, Column: 5, + }, + { + Name: "BadTrailingCommaEOL", + Input: "a,b,c,\n", + Error: "extra delimiter at end of line", Line: 1, Column: 5, + }, + { + Name: "BadTrailingCommaSpaceEOF", + TrimLeadingSpace: true, + Input: "a,b,c, ", + Error: "extra delimiter at end of line", Line: 1, Column: 5, + }, + { + Name: "BadTrailingCommaSpaceEOL", + TrimLeadingSpace: true, + Input: "a,b,c, \n", + Error: "extra delimiter at end of line", Line: 1, Column: 5, + }, + { + Name: "BadTrailingCommaLine3", + TrimLeadingSpace: true, + Input: "a,b,c\nd,e,f\ng,hi,", + Error: "extra delimiter at end of line", Line: 3, Column: 4, + }, + { + Name: "NotTrailingComma3", + Input: "a,b,c, \n", + Output: [][]string{{"a", "b", "c", " "}}, + }, + { + Name: "CommaFieldTest", + TrailingComma: true, + Input: `x,y,z,w +x,y,z, +x,y,, +x,,, +,,, +"x","y","z","w" +"x","y","z","" +"x","y","","" +"x","","","" +"","","","" +`, + Output: [][]string{ + {"x", "y", "z", "w"}, + {"x", "y", "z", ""}, + {"x", "y", "", ""}, + {"x", "", "", ""}, + {"", "", "", ""}, + {"x", "y", "z", "w"}, + {"x", "y", "z", ""}, + {"x", "y", "", ""}, + {"x", "", "", ""}, + {"", "", "", ""}, + }, + }, +} + +func TestRead(t *testing.T) { + for _, tt := range readTests { + r := NewReader(strings.NewReader(tt.Input)) + r.Comment = tt.Comment + if tt.UseFieldsPerRecord { + r.FieldsPerRecord = tt.FieldsPerRecord + } else { + r.FieldsPerRecord = -1 + } + r.LazyQuotes = tt.LazyQuotes + r.TrailingComma = tt.TrailingComma + r.TrimLeadingSpace = tt.TrimLeadingSpace + if tt.Comma != 0 { + r.Comma = tt.Comma + } + out, err := r.ReadAll() + perr, _ := err.(*ParseError) + if tt.Error != "" { + if err == nil || !strings.Contains(err.String(), tt.Error) { + t.Errorf("%s: error %v, want error %q", tt.Name, err, tt.Error) + } else if tt.Line != 0 && (tt.Line != perr.Line || tt.Column != perr.Column) { + t.Errorf("%s: error at %d:%d expected %d:%d", tt.Name, perr.Line, perr.Column, tt.Line, tt.Column) + } + } else if err != nil { + t.Errorf("%s: unexpected error %v", tt.Name, err) + } else if !reflect.DeepEqual(out, tt.Output) { + t.Errorf("%s: out=%q want %q", tt.Name, out, tt.Output) + } + } +} diff --git a/src/pkg/csv/writer.go b/src/pkg/csv/writer.go new file mode 100644 index 000000000..01386da19 --- /dev/null +++ b/src/pkg/csv/writer.go @@ -0,0 +1,123 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package csv + +import ( + "bufio" + "io" + "os" + "strings" + "unicode" + "utf8" +) + +// A Writer writes records to a CSV encoded file. +// +// As returned by NewWriter, a Writer writes records terminated by a +// newline and uses ',' as the field delimiter. The exported fields can be +// changed to customize the details before the first call to Write or WriteAll. +// +// Comma is the field delimiter. +// +// If UseCRLF is true, the Writer ends each record with \r\n instead of \n. +// just \n is written. +type Writer struct { + Comma int // Field delimiter (set to to ',' by NewWriter) + UseCRLF bool // True to use \r\n as the line terminator + w *bufio.Writer +} + +// NewWriter returns a new Writer that writes to w. +func NewWriter(w io.Writer) *Writer { + return &Writer{ + Comma: ',', + w: bufio.NewWriter(w), + } +} + +// Writer writes a single CSV record to w along with any necessary quoting. +// A record is a slice of strings with each string being one field. +func (w *Writer) Write(record []string) (err os.Error) { + for n, field := range record { + if n > 0 { + if _, err = w.w.WriteRune(w.Comma); err != nil { + return + } + } + + // If we don't have to have a quoted field then just + // write out the field and continue to the next field. + if !w.fieldNeedsQuotes(field) { + if _, err = w.w.WriteString(field); err != nil { + return + } + continue + } + if err = w.w.WriteByte('"'); err != nil { + return + } + + for _, rune := range field { + switch rune { + case '"': + _, err = w.w.WriteString(`""`) + case '\r': + if !w.UseCRLF { + err = w.w.WriteByte('\r') + } + case '\n': + if w.UseCRLF { + _, err = w.w.WriteString("\r\n") + } else { + err = w.w.WriteByte('\n') + } + default: + _, err = w.w.WriteRune(rune) + } + if err != nil { + return + } + } + + if err = w.w.WriteByte('"'); err != nil { + return + } + } + if w.UseCRLF { + _, err = w.w.WriteString("\r\n") + } else { + err = w.w.WriteByte('\n') + } + return +} + +// Flush writes any buffered data to the underlying io.Writer. +func (w *Writer) Flush() { + w.w.Flush() +} + +// WriteAll writes multiple CSV records to w using Write and then calls Flush. +func (w *Writer) WriteAll(records [][]string) (err os.Error) { + for _, record := range records { + err = w.Write(record) + if err != nil { + break + } + } + w.Flush() + return nil +} + +// fieldNeedsQuotes returns true if our field must be enclosed in quotes. +// Empty fields, files with a Comma, fields with a quote or newline, and +// fields which start with a space must be enclosed in quotes. +func (w *Writer) fieldNeedsQuotes(field string) bool { + if len(field) == 0 || strings.IndexRune(field, w.Comma) >= 0 || strings.IndexAny(field, "\"\r\n") >= 0 { + return true + } + + rune, _ := utf8.DecodeRuneInString(field) + return unicode.IsSpace(rune) +} diff --git a/src/pkg/csv/writer_test.go b/src/pkg/csv/writer_test.go new file mode 100644 index 000000000..578959007 --- /dev/null +++ b/src/pkg/csv/writer_test.go @@ -0,0 +1,44 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package csv + +import ( + "bytes" + "testing" +) + +var writeTests = []struct { + Input [][]string + Output string + UseCRLF bool +}{ + {Input: [][]string{{"abc"}}, Output: "abc\n"}, + {Input: [][]string{{"abc"}}, Output: "abc\r\n", UseCRLF: true}, + {Input: [][]string{{`"abc"`}}, Output: `"""abc"""` + "\n"}, + {Input: [][]string{{`a"b`}}, Output: `"a""b"` + "\n"}, + {Input: [][]string{{`"a"b"`}}, Output: `"""a""b"""` + "\n"}, + {Input: [][]string{{" abc"}}, Output: `" abc"` + "\n"}, + {Input: [][]string{{"abc,def"}}, Output: `"abc,def"` + "\n"}, + {Input: [][]string{{"abc", "def"}}, Output: "abc,def\n"}, + {Input: [][]string{{"abc"}, {"def"}}, Output: "abc\ndef\n"}, + {Input: [][]string{{"abc\ndef"}}, Output: "\"abc\ndef\"\n"}, + {Input: [][]string{{"abc\ndef"}}, Output: "\"abc\r\ndef\"\r\n", UseCRLF: true}, +} + +func TestWrite(t *testing.T) { + for n, tt := range writeTests { + b := &bytes.Buffer{} + f := NewWriter(b) + f.UseCRLF = tt.UseCRLF + err := f.WriteAll(tt.Input) + if err != nil { + t.Errorf("Unexpected error: %s\n", err) + } + out := b.String() + if out != tt.Output { + t.Errorf("#%d: out=%q want %q", n, out, tt.Output) + } + } +} diff --git a/src/pkg/debug/dwarf/type.go b/src/pkg/debug/dwarf/type.go index a33785b04..f35365ebe 100644 --- a/src/pkg/debug/dwarf/type.go +++ b/src/pkg/debug/dwarf/type.go @@ -566,12 +566,13 @@ func (d *Data) Type(off Offset) (Type, os.Error) { goto Error } - b, ok := e.Val(AttrByteSize).(int64) - if !ok { - b = -1 + { + b, ok := e.Val(AttrByteSize).(int64) + if !ok { + b = -1 + } + typ.Common().ByteSize = b } - typ.Common().ByteSize = b - return typ, nil Error: diff --git a/src/pkg/debug/elf/file.go b/src/pkg/debug/elf/file.go index 220ab9408..346fe2a78 100644 --- a/src/pkg/debug/elf/file.go +++ b/src/pkg/debug/elf/file.go @@ -81,7 +81,7 @@ func (s *Section) Data() ([]byte, os.Error) { // specified link value. func (f *File) stringTable(link uint32) ([]byte, os.Error) { if link <= 0 || link >= uint32(len(f.Sections)) { - return nil, os.ErrorString("section has invalid string table link") + return nil, os.NewError("section has invalid string table link") } return f.Sections[link].Data() } @@ -341,27 +341,27 @@ func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, os.Error) { return f.getSymbols32(typ) } - return nil, nil, os.ErrorString("not implemented") + return nil, nil, os.NewError("not implemented") } func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, os.Error) { symtabSection := f.SectionByType(typ) if symtabSection == nil { - return nil, nil, os.ErrorString("no symbol section") + return nil, nil, os.NewError("no symbol section") } data, err := symtabSection.Data() if err != nil { - return nil, nil, os.ErrorString("cannot load symbol section") + return nil, nil, os.NewError("cannot load symbol section") } symtab := bytes.NewBuffer(data) if symtab.Len()%Sym32Size != 0 { - return nil, nil, os.ErrorString("length of symbol section is not a multiple of SymSize") + return nil, nil, os.NewError("length of symbol section is not a multiple of SymSize") } strdata, err := f.stringTable(symtabSection.Link) if err != nil { - return nil, nil, os.ErrorString("cannot load string table section") + return nil, nil, os.NewError("cannot load string table section") } // The first entry is all zeros. @@ -390,21 +390,21 @@ func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, os.Error) { func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, os.Error) { symtabSection := f.SectionByType(typ) if symtabSection == nil { - return nil, nil, os.ErrorString("no symbol section") + return nil, nil, os.NewError("no symbol section") } data, err := symtabSection.Data() if err != nil { - return nil, nil, os.ErrorString("cannot load symbol section") + return nil, nil, os.NewError("cannot load symbol section") } symtab := bytes.NewBuffer(data) if symtab.Len()%Sym64Size != 0 { - return nil, nil, os.ErrorString("length of symbol section is not a multiple of Sym64Size") + return nil, nil, os.NewError("length of symbol section is not a multiple of Sym64Size") } strdata, err := f.stringTable(symtabSection.Link) if err != nil { - return nil, nil, os.ErrorString("cannot load string table section") + return nil, nil, os.NewError("cannot load string table section") } // The first entry is all zeros. @@ -462,12 +462,12 @@ func (f *File) applyRelocations(dst []byte, rels []byte) os.Error { return f.applyRelocationsAMD64(dst, rels) } - return os.ErrorString("not implemented") + return os.NewError("not implemented") } func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) os.Error { if len(rels)%Sym64Size != 0 { - return os.ErrorString("length of relocation section is not a multiple of Sym64Size") + return os.NewError("length of relocation section is not a multiple of Sym64Size") } symbols, _, err := f.getSymbols(SHT_SYMTAB) diff --git a/src/pkg/debug/elf/file_test.go b/src/pkg/debug/elf/file_test.go index 84068ea12..37f62796e 100644 --- a/src/pkg/debug/elf/file_test.go +++ b/src/pkg/debug/elf/file_test.go @@ -136,15 +136,15 @@ type relocationTest struct { var relocationTests = []relocationTest{ { - "testdata/go-relocation-test-gcc441-x86-64.o", + "testdata/go-relocation-test-gcc441-x86-64.obj", &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}, }, { - "testdata/go-relocation-test-gcc441-x86.o", + "testdata/go-relocation-test-gcc441-x86.obj", &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "t.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x5)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}, }, { - "testdata/go-relocation-test-gcc424-x86-64.o", + "testdata/go-relocation-test-gcc424-x86-64.obj", &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}, }, } diff --git a/src/pkg/debug/elf/testdata/go-relocation-test-gcc424-x86-64.o b/src/pkg/debug/elf/testdata/go-relocation-test-gcc424-x86-64.obj Binary files differindex a7c6d6e56..a7c6d6e56 100644 --- a/src/pkg/debug/elf/testdata/go-relocation-test-gcc424-x86-64.o +++ b/src/pkg/debug/elf/testdata/go-relocation-test-gcc424-x86-64.obj diff --git a/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86-64.o b/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86-64.obj Binary files differindex 2d37ab6e6..2d37ab6e6 100644 --- a/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86-64.o +++ b/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86-64.obj diff --git a/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86.o b/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86.obj Binary files differindex 0d59fe303..0d59fe303 100644 --- a/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86.o +++ b/src/pkg/debug/elf/testdata/go-relocation-test-gcc441-x86.obj diff --git a/src/pkg/debug/proc/proc_linux.go b/src/pkg/debug/proc/proc_linux.go index 153c3e99b..7ec797114 100644 --- a/src/pkg/debug/proc/proc_linux.go +++ b/src/pkg/debug/proc/proc_linux.go @@ -1229,7 +1229,7 @@ func (p *process) attachAllThreads() os.Error { return err } - statParts := strings.Split(string(statFile), " ", 4) + statParts := strings.SplitN(string(statFile), " ", 4) if len(statParts) > 2 && statParts[2] == "Z" { // tid is a zombie p.logTrace("thread %d is a zombie", tid) @@ -1284,9 +1284,11 @@ func Attach(pid int) (Process, os.Error) { // details. func StartProcess(argv0 string, argv []string, attr *os.ProcAttr) (Process, os.Error) { sysattr := &syscall.ProcAttr{ - Dir: attr.Dir, - Env: attr.Env, - Ptrace: true, + Dir: attr.Dir, + Env: attr.Env, + Sys: &syscall.SysProcAttr{ + Ptrace: true, + }, } p := newProcess(-1) diff --git a/src/pkg/encoding/pem/pem.go b/src/pkg/encoding/pem/pem.go index c2398807f..ebe57edc0 100644 --- a/src/pkg/encoding/pem/pem.go +++ b/src/pkg/encoding/pem/pem.go @@ -86,7 +86,7 @@ func Decode(data []byte) (p *Block, rest []byte) { typeLine, rest := getLine(rest) if !bytes.HasSuffix(typeLine, pemEndOfLine) { - goto Error + return decodeError(data, rest) } typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)] @@ -118,22 +118,23 @@ func Decode(data []byte) (p *Block, rest []byte) { i := bytes.Index(rest, pemEnd) if i < 0 { - goto Error + return decodeError(data, rest) } base64Data := removeWhitespace(rest[0:i]) p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data))) n, err := base64.StdEncoding.Decode(p.Bytes, base64Data) if err != nil { - goto Error + return decodeError(data, rest) } p.Bytes = p.Bytes[0:n] _, rest = getLine(rest[i+len(pemEnd):]) return +} -Error: +func decodeError(data, rest []byte) (*Block, []byte) { // If we get here then we have rejected a likely looking, but // ultimately invalid PEM block. We need to start over from a new // position. We have consumed the preamble line and will have consumed @@ -154,11 +155,11 @@ Error: // // we've failed to parse using the first BEGIN line // and now will try again, using the second BEGIN line. - p, rest = Decode(rest) + p, rest := Decode(rest) if p == nil { rest = data } - return + return p, rest } const pemLineLength = 64 diff --git a/src/pkg/exec/Makefile b/src/pkg/exec/Makefile index 262ecac85..90bb74b41 100644 --- a/src/pkg/exec/Makefile +++ b/src/pkg/exec/Makefile @@ -20,6 +20,9 @@ GOFILES_linux=\ GOFILES_windows=\ lp_windows.go\ +GOFILES_plan9=\ + lp_plan9.go\ + GOFILES+=$(GOFILES_$(GOOS)) include ../../Make.pkg diff --git a/src/pkg/exec/exec.go b/src/pkg/exec/exec.go index 935f24c21..5b988d5eb 100644 --- a/src/pkg/exec/exec.go +++ b/src/pkg/exec/exec.go @@ -12,6 +12,7 @@ import ( "io" "os" "strconv" + "syscall" ) // Error records the name of a binary that failed to be be executed @@ -62,6 +63,10 @@ type Cmd struct { Stdout io.Writer Stderr io.Writer + // SysProcAttr holds optional, operating system-specific attributes. + // Run passes it to os.StartProcess as the os.ProcAttr's Sys field. + SysProcAttr *syscall.SysProcAttr + // Process is the underlying process, once started. Process *os.Process @@ -225,6 +230,7 @@ func (c *Cmd) Start() os.Error { Dir: c.Dir, Files: c.childFiles, Env: c.envv(), + Sys: c.SysProcAttr, }) if err != nil { return err diff --git a/src/pkg/exec/exec_test.go b/src/pkg/exec/exec_test.go index c45a7d70a..f6cebb905 100644 --- a/src/pkg/exec/exec_test.go +++ b/src/pkg/exec/exec_test.go @@ -55,7 +55,7 @@ func TestCatGoodAndBadFile(t *testing.T) { t.Errorf("expected Waitmsg from cat combined; got %T: %v", err, err) } s := string(bs) - sp := strings.Split(s, "\n", 2) + sp := strings.SplitN(s, "\n", 2) if len(sp) != 2 { t.Fatalf("expected two lines from cat; got %q", s) } diff --git a/src/pkg/exec/lp_plan9.go b/src/pkg/exec/lp_plan9.go new file mode 100644 index 000000000..e4751a4df --- /dev/null +++ b/src/pkg/exec/lp_plan9.go @@ -0,0 +1,51 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package exec + +import ( + "os" + "strings" +) + +// ErrNotFound is the error resulting if a path search failed to find an executable file. +var ErrNotFound = os.NewError("executable file not found in $path") + +func findExecutable(file string) os.Error { + d, err := os.Stat(file) + if err != nil { + return err + } + if d.IsRegular() && d.Permission()&0111 != 0 { + return nil + } + return os.EPERM +} + +// LookPath searches for an executable binary named file +// in the directories named by the path environment variable. +// If file begins with "/", "#", "./", or "../", it is tried +// directly and the path is not consulted. +func LookPath(file string) (string, os.Error) { + // skip the path lookup for these prefixes + skip := []string{"/", "#", "./", "../"} + + for _, p := range skip { + if strings.HasPrefix(file, p) { + err := findExecutable(file) + if err == nil { + return file, nil + } + return "", &Error{file, err} + } + } + + path := os.Getenv("path") + for _, dir := range strings.Split(path, "\000") { + if err := findExecutable(dir + "/" + file); err == nil { + return dir + "/" + file, nil + } + } + return "", &Error{file, ErrNotFound} +} diff --git a/src/pkg/exec/lp_unix.go b/src/pkg/exec/lp_unix.go index 3fc3be832..008fb11a8 100644 --- a/src/pkg/exec/lp_unix.go +++ b/src/pkg/exec/lp_unix.go @@ -10,7 +10,7 @@ import ( ) // ErrNotFound is the error resulting if a path search failed to find an executable file. -var ErrNotFound = os.ErrorString("executable file not found in $PATH") +var ErrNotFound = os.NewError("executable file not found in $PATH") func findExecutable(file string) os.Error { d, err := os.Stat(file) @@ -39,7 +39,7 @@ func LookPath(file string) (string, os.Error) { return "", &Error{file, err} } pathenv := os.Getenv("PATH") - for _, dir := range strings.Split(pathenv, ":", -1) { + for _, dir := range strings.Split(pathenv, ":") { if dir == "" { // Unix shell semantics: path element "" means "." dir = "." diff --git a/src/pkg/exec/lp_windows.go b/src/pkg/exec/lp_windows.go index 758861021..7581088eb 100644 --- a/src/pkg/exec/lp_windows.go +++ b/src/pkg/exec/lp_windows.go @@ -10,7 +10,7 @@ import ( ) // ErrNotFound is the error resulting if a path search failed to find an executable file. -var ErrNotFound = os.ErrorString("executable file not found in %PATH%") +var ErrNotFound = os.NewError("executable file not found in %PATH%") func chkStat(file string) os.Error { d, err := os.Stat(file) @@ -38,20 +38,25 @@ func findExecutable(file string, exts []string) (string, os.Error) { return f, nil } } - return ``, ErrNotFound + return ``, os.ENOENT } func LookPath(file string) (f string, err os.Error) { + x := os.Getenv(`PATHEXT`) + if x == `` { + x = `.COM;.EXE;.BAT;.CMD` + } exts := []string{} - if x := os.Getenv(`PATHEXT`); x != `` { - exts = strings.Split(strings.ToLower(x), `;`, -1) - for i, e := range exts { - if e == `` || e[0] != '.' { - exts[i] = `.` + e - } + for _, e := range strings.Split(strings.ToLower(x), `;`) { + if e == "" { + continue + } + if e[0] != '.' { + e = "." + e } + exts = append(exts, e) } - if strings.Contains(file, `\`) || strings.Contains(file, `/`) { + if strings.IndexAny(file, `:\/`) != -1 { if f, err = findExecutable(file, exts); err == nil { return } @@ -62,7 +67,7 @@ func LookPath(file string) (f string, err os.Error) { return } } else { - for _, dir := range strings.Split(pathenv, `;`, -1) { + for _, dir := range strings.Split(pathenv, `;`) { if f, err = findExecutable(dir+`\`+file, exts); err == nil { return } diff --git a/src/pkg/exp/ogle/cmd.go b/src/pkg/exp/ogle/cmd.go index a8db523ea..ff0d24c69 100644 --- a/src/pkg/exp/ogle/cmd.go +++ b/src/pkg/exp/ogle/cmd.go @@ -154,7 +154,7 @@ func cmdLoad(args []byte) os.Error { } println("Attached to", pid) } else { - parts := strings.Split(path, " ", -1) + parts := strings.Split(path, " ") if len(parts) == 0 { fname = "" } else { diff --git a/src/pkg/exp/regexp/syntax/Makefile b/src/pkg/exp/regexp/syntax/Makefile new file mode 100644 index 000000000..97d4ad6ca --- /dev/null +++ b/src/pkg/exp/regexp/syntax/Makefile @@ -0,0 +1,16 @@ +# Copyright 2011 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../../../Make.inc + +TARG=exp/regexp/syntax +GOFILES=\ + compile.go\ + parse.go\ + perl_groups.go\ + prog.go\ + regexp.go\ + simplify.go\ + +include ../../../../Make.pkg diff --git a/src/pkg/exp/regexp/syntax/compile.go b/src/pkg/exp/regexp/syntax/compile.go new file mode 100644 index 000000000..ec9556fde --- /dev/null +++ b/src/pkg/exp/regexp/syntax/compile.go @@ -0,0 +1,264 @@ +package syntax + +import ( + "os" + "unicode" +) + +// A patchList is a list of instruction pointers that need to be filled in (patched). +// Because the pointers haven't been filled in yet, we can reuse their storage +// to hold the list. It's kind of sleazy, but works well in practice. +// See http://swtch.com/~rsc/regexp/regexp1.html for inspiration. +// +// These aren't really pointers: they're integers, so we can reinterpret them +// this way without using package unsafe. A value l denotes +// p.inst[l>>1].Out (l&1==0) or .Arg (l&1==1). +// l == 0 denotes the empty list, okay because we start every program +// with a fail instruction, so we'll never want to point at its output link. +type patchList uint32 + +func (l patchList) next(p *Prog) patchList { + i := &p.Inst[l>>1] + if l&1 == 0 { + return patchList(i.Out) + } + return patchList(i.Arg) +} + +func (l patchList) patch(p *Prog, val uint32) { + for l != 0 { + i := &p.Inst[l>>1] + if l&1 == 0 { + l = patchList(i.Out) + i.Out = val + } else { + l = patchList(i.Arg) + i.Arg = val + } + } +} + +func (l1 patchList) append(p *Prog, l2 patchList) patchList { + if l1 == 0 { + return l2 + } + if l2 == 0 { + return l1 + } + + last := l1 + for { + next := last.next(p) + if next == 0 { + break + } + last = next + } + + i := &p.Inst[last>>1] + if last&1 == 0 { + i.Out = uint32(l2) + } else { + i.Arg = uint32(l2) + } + return l1 +} + +// A frag represents a compiled program fragment. +type frag struct { + i uint32 // index of first instruction + out patchList // where to record end instruction +} + +type compiler struct { + p *Prog +} + +// Compile compiles the regexp into a program to be executed. +func Compile(re *Regexp) (*Prog, os.Error) { + var c compiler + c.init() + f := c.compile(re) + f.out.patch(c.p, c.inst(InstMatch).i) + c.p.Start = int(f.i) + return c.p, nil +} + +func (c *compiler) init() { + c.p = new(Prog) + c.inst(InstFail) +} + +var anyRuneNotNL = []int{0, '\n' - 1, '\n' - 1, unicode.MaxRune} +var anyRune = []int{0, unicode.MaxRune} + +func (c *compiler) compile(re *Regexp) frag { + switch re.Op { + case OpNoMatch: + return c.fail() + case OpEmptyMatch: + return c.nop() + case OpLiteral: + if len(re.Rune) == 0 { + return c.nop() + } + var f frag + for j := range re.Rune { + f1 := c.rune(re.Rune[j : j+1]) + if j == 0 { + f = f1 + } else { + f = c.cat(f, f1) + } + } + return f + case OpCharClass: + return c.rune(re.Rune) + case OpAnyCharNotNL: + return c.rune(anyRuneNotNL) + case OpAnyChar: + return c.rune(anyRune) + case OpBeginLine: + return c.empty(EmptyBeginLine) + case OpEndLine: + return c.empty(EmptyEndLine) + case OpBeginText: + return c.empty(EmptyBeginText) + case OpEndText: + return c.empty(EmptyEndText) + case OpWordBoundary: + return c.empty(EmptyWordBoundary) + case OpNoWordBoundary: + return c.empty(EmptyNoWordBoundary) + case OpCapture: + bra := c.cap(uint32(re.Cap << 1)) + sub := c.compile(re.Sub[0]) + ket := c.cap(uint32(re.Cap<<1 | 1)) + return c.cat(c.cat(bra, sub), ket) + case OpStar: + return c.star(c.compile(re.Sub[0]), re.Flags&NonGreedy != 0) + case OpPlus: + return c.plus(c.compile(re.Sub[0]), re.Flags&NonGreedy != 0) + case OpQuest: + return c.quest(c.compile(re.Sub[0]), re.Flags&NonGreedy != 0) + case OpConcat: + if len(re.Sub) == 0 { + return c.nop() + } + var f frag + for i, sub := range re.Sub { + if i == 0 { + f = c.compile(sub) + } else { + f = c.cat(f, c.compile(sub)) + } + } + return f + case OpAlternate: + var f frag + for _, sub := range re.Sub { + f = c.alt(f, c.compile(sub)) + } + return f + } + panic("regexp: unhandled case in compile") +} + +func (c *compiler) inst(op InstOp) frag { + // TODO: impose length limit + f := frag{i: uint32(len(c.p.Inst))} + c.p.Inst = append(c.p.Inst, Inst{Op: op}) + return f +} + +func (c *compiler) nop() frag { + f := c.inst(InstNop) + f.out = patchList(f.i << 1) + return f +} + +func (c *compiler) fail() frag { + return frag{} +} + +func (c *compiler) cap(arg uint32) frag { + f := c.inst(InstCapture) + f.out = patchList(f.i << 1) + c.p.Inst[f.i].Arg = arg + return f +} + +func (c *compiler) cat(f1, f2 frag) frag { + // concat of failure is failure + if f1.i == 0 || f2.i == 0 { + return frag{} + } + + // TODO: elide nop + + f1.out.patch(c.p, f2.i) + return frag{f1.i, f2.out} +} + +func (c *compiler) alt(f1, f2 frag) frag { + // alt of failure is other + if f1.i == 0 { + return f2 + } + if f2.i == 0 { + return f1 + } + + f := c.inst(InstAlt) + i := &c.p.Inst[f.i] + i.Out = f1.i + i.Arg = f2.i + f.out = f1.out.append(c.p, f2.out) + return f +} + +func (c *compiler) quest(f1 frag, nongreedy bool) frag { + f := c.inst(InstAlt) + i := &c.p.Inst[f.i] + if nongreedy { + i.Arg = f1.i + f.out = patchList(f.i << 1) + } else { + i.Out = f1.i + f.out = patchList(f.i<<1 | 1) + } + f.out = f.out.append(c.p, f1.out) + return f +} + +func (c *compiler) star(f1 frag, nongreedy bool) frag { + f := c.inst(InstAlt) + i := &c.p.Inst[f.i] + if nongreedy { + i.Arg = f1.i + f.out = patchList(f.i << 1) + } else { + i.Out = f1.i + f.out = patchList(f.i<<1 | 1) + } + f1.out.patch(c.p, f.i) + return f +} + +func (c *compiler) plus(f1 frag, nongreedy bool) frag { + return frag{f1.i, c.star(f1, nongreedy).out} +} + +func (c *compiler) empty(op EmptyOp) frag { + f := c.inst(InstEmptyWidth) + c.p.Inst[f.i].Arg = uint32(op) + f.out = patchList(f.i << 1) + return f +} + +func (c *compiler) rune(rune []int) frag { + f := c.inst(InstRune) + c.p.Inst[f.i].Rune = rune + f.out = patchList(f.i << 1) + return f +} diff --git a/src/pkg/exp/regexp/syntax/make_perl_groups.pl b/src/pkg/exp/regexp/syntax/make_perl_groups.pl new file mode 100755 index 000000000..6d1b84b10 --- /dev/null +++ b/src/pkg/exp/regexp/syntax/make_perl_groups.pl @@ -0,0 +1,103 @@ +#!/usr/bin/perl +# Copyright 2008 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +# Modified version of RE2's make_perl_groups.pl. + +# Generate table entries giving character ranges +# for POSIX/Perl character classes. Rather than +# figure out what the definition is, it is easier to ask +# Perl about each letter from 0-128 and write down +# its answer. + +@posixclasses = ( + "[:alnum:]", + "[:alpha:]", + "[:ascii:]", + "[:blank:]", + "[:cntrl:]", + "[:digit:]", + "[:graph:]", + "[:lower:]", + "[:print:]", + "[:punct:]", + "[:space:]", + "[:upper:]", + "[:word:]", + "[:xdigit:]", +); + +@perlclasses = ( + "\\d", + "\\s", + "\\w", +); + +sub ComputeClass($) { + my @ranges; + my ($class) = @_; + my $regexp = "[$class]"; + my $start = -1; + for (my $i=0; $i<=129; $i++) { + if ($i == 129) { $i = 256; } + if ($i <= 128 && chr($i) =~ $regexp) { + if ($start < 0) { + $start = $i; + } + } else { + if ($start >= 0) { + push @ranges, [$start, $i-1]; + } + $start = -1; + } + } + return @ranges; +} + +sub PrintClass($$@) { + my ($cname, $name, @ranges) = @_; + print "var code$cname = []int{ /* $name */\n"; + for (my $i=0; $i<@ranges; $i++) { + my @a = @{$ranges[$i]}; + printf "\t0x%x, 0x%x,\n", $a[0], $a[1]; + } + print "}\n\n"; + my $n = @ranges; + $negname = $name; + if ($negname =~ /:/) { + $negname =~ s/:/:^/; + } else { + $negname =~ y/a-z/A-Z/; + } + return "\t`$name`: {+1, code$cname},\n" . + "\t`$negname`: {-1, code$cname},\n"; +} + +my $gen = 0; + +sub PrintClasses($@) { + my ($cname, @classes) = @_; + my @entries; + foreach my $cl (@classes) { + my @ranges = ComputeClass($cl); + push @entries, PrintClass(++$gen, $cl, @ranges); + } + print "var ${cname}Group = map[string]charGroup{\n"; + foreach my $e (@entries) { + print $e; + } + print "}\n"; + my $count = @entries; +} + +print <<EOF; +// GENERATED BY make_perl_groups.pl; DO NOT EDIT. +// make_perl_groups.pl >perl_groups.go + +package syntax + +EOF + +PrintClasses("perl", @perlclasses); +PrintClasses("posix", @posixclasses); diff --git a/src/pkg/exp/regexp/syntax/parse.go b/src/pkg/exp/regexp/syntax/parse.go new file mode 100644 index 000000000..b6c91f7e1 --- /dev/null +++ b/src/pkg/exp/regexp/syntax/parse.go @@ -0,0 +1,1798 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package syntax + +import ( + "os" + "sort" + "strings" + "unicode" + "utf8" +) + +// An Error describes a failure to parse a regular expression +// and gives the offending expression. +type Error struct { + Code ErrorCode + Expr string +} + +func (e *Error) String() string { + return "error parsing regexp: " + e.Code.String() + ": `" + e.Expr + "`" +} + +// An ErrorCode describes a failure to parse a regular expression. +type ErrorCode string + +const ( + // Unexpected error + ErrInternalError ErrorCode = "regexp/syntax: internal error" + + // Parse errors + ErrInvalidCharClass ErrorCode = "invalid character class" + ErrInvalidCharRange ErrorCode = "invalid character class range" + ErrInvalidEscape ErrorCode = "invalid escape sequence" + ErrInvalidNamedCapture ErrorCode = "invalid named capture" + ErrInvalidPerlOp ErrorCode = "invalid or unsupported Perl syntax" + ErrInvalidRepeatOp ErrorCode = "invalid nested repetition operator" + ErrInvalidRepeatSize ErrorCode = "invalid repeat count" + ErrInvalidUTF8 ErrorCode = "invalid UTF-8" + ErrMissingBracket ErrorCode = "missing closing ]" + ErrMissingParen ErrorCode = "missing closing )" + ErrMissingRepeatArgument ErrorCode = "missing argument to repetition operator" + ErrTrailingBackslash ErrorCode = "trailing backslash at end of expression" +) + +func (e ErrorCode) String() string { + return string(e) +} + +// Flags control the behavior of the parser and record information about regexp context. +type Flags uint16 + +const ( + FoldCase Flags = 1 << iota // case-insensitive match + Literal // treat pattern as literal string + ClassNL // allow character classes like [^a-z] and [[:space:]] to match newline + DotNL // allow . to match newline + OneLine // treat ^ and $ as only matching at beginning and end of text + NonGreedy // make repetition operators default to non-greedy + PerlX // allow Perl extensions + UnicodeGroups // allow \p{Han}, \P{Han} for Unicode group and negation + WasDollar // regexp OpEndText was $, not \z + Simple // regexp contains no counted repetition + + MatchNL = ClassNL | DotNL + + Perl = ClassNL | OneLine | PerlX | UnicodeGroups // as close to Perl as possible + POSIX Flags = 0 // POSIX syntax +) + +// Pseudo-ops for parsing stack. +const ( + opLeftParen = opPseudo + iota + opVerticalBar +) + +type parser struct { + flags Flags // parse mode flags + stack []*Regexp // stack of parsed expressions + free *Regexp + numCap int // number of capturing groups seen + wholeRegexp string + tmpClass []int // temporary char class work space +} + +func (p *parser) newRegexp(op Op) *Regexp { + re := p.free + if re != nil { + p.free = re.Sub0[0] + *re = Regexp{} + } else { + re = new(Regexp) + } + re.Op = op + return re +} + +func (p *parser) reuse(re *Regexp) { + re.Sub0[0] = p.free + p.free = re +} + +// Parse stack manipulation. + +// push pushes the regexp re onto the parse stack and returns the regexp. +func (p *parser) push(re *Regexp) *Regexp { + if re.Op == OpCharClass && len(re.Rune) == 2 && re.Rune[0] == re.Rune[1] { + // Single rune. + if p.maybeConcat(re.Rune[0], p.flags&^FoldCase) { + return nil + } + re.Op = OpLiteral + re.Rune = re.Rune[:1] + re.Flags = p.flags &^ FoldCase + } else if re.Op == OpCharClass && len(re.Rune) == 4 && + re.Rune[0] == re.Rune[1] && re.Rune[2] == re.Rune[3] && + unicode.SimpleFold(re.Rune[0]) == re.Rune[2] && + unicode.SimpleFold(re.Rune[2]) == re.Rune[0] || + re.Op == OpCharClass && len(re.Rune) == 2 && + re.Rune[0]+1 == re.Rune[1] && + unicode.SimpleFold(re.Rune[0]) == re.Rune[1] && + unicode.SimpleFold(re.Rune[1]) == re.Rune[0] { + // Case-insensitive rune like [Aa] or [Δδ]. + if p.maybeConcat(re.Rune[0], p.flags|FoldCase) { + return nil + } + + // Rewrite as (case-insensitive) literal. + re.Op = OpLiteral + re.Rune = re.Rune[:1] + re.Flags = p.flags | FoldCase + } else { + // Incremental concatenation. + p.maybeConcat(-1, 0) + } + + p.stack = append(p.stack, re) + return re +} + +// maybeConcat implements incremental concatenation +// of literal runes into string nodes. The parser calls this +// before each push, so only the top fragment of the stack +// might need processing. Since this is called before a push, +// the topmost literal is no longer subject to operators like * +// (Otherwise ab* would turn into (ab)*.) +// If r >= 0 and there's a node left over, maybeConcat uses it +// to push r with the given flags. +// maybeConcat reports whether r was pushed. +func (p *parser) maybeConcat(r int, flags Flags) bool { + n := len(p.stack) + if n < 2 { + return false + } + + re1 := p.stack[n-1] + re2 := p.stack[n-2] + if re1.Op != OpLiteral || re2.Op != OpLiteral || re1.Flags&FoldCase != re2.Flags&FoldCase { + return false + } + + // Push re1 into re2. + re2.Rune = append(re2.Rune, re1.Rune...) + + // Reuse re1 if possible. + if r >= 0 { + re1.Rune = re1.Rune0[:1] + re1.Rune[0] = r + re1.Flags = flags + return true + } + + p.stack = p.stack[:n-1] + p.reuse(re1) + return false // did not push r +} + +// newLiteral returns a new OpLiteral Regexp with the given flags +func (p *parser) newLiteral(r int, flags Flags) *Regexp { + re := p.newRegexp(OpLiteral) + re.Flags = flags + re.Rune0[0] = r + re.Rune = re.Rune0[:1] + return re +} + +// literal pushes a literal regexp for the rune r on the stack +// and returns that regexp. +func (p *parser) literal(r int) { + p.push(p.newLiteral(r, p.flags)) +} + +// op pushes a regexp with the given op onto the stack +// and returns that regexp. +func (p *parser) op(op Op) *Regexp { + re := p.newRegexp(op) + re.Flags = p.flags + return p.push(re) +} + +// repeat replaces the top stack element with itself repeated +// according to op. +func (p *parser) repeat(op Op, min, max int, opstr, t, lastRepeat string) (string, os.Error) { + flags := p.flags + if p.flags&PerlX != 0 { + if len(t) > 0 && t[0] == '?' { + t = t[1:] + flags ^= NonGreedy + } + if lastRepeat != "" { + // In Perl it is not allowed to stack repetition operators: + // a** is a syntax error, not a doubled star, and a++ means + // something else entirely, which we don't support! + return "", &Error{ErrInvalidRepeatOp, lastRepeat[:len(lastRepeat)-len(t)]} + } + } + n := len(p.stack) + if n == 0 { + return "", &Error{ErrMissingRepeatArgument, opstr} + } + sub := p.stack[n-1] + re := p.newRegexp(op) + re.Min = min + re.Max = max + re.Flags = flags + re.Sub = re.Sub0[:1] + re.Sub[0] = sub + p.stack[n-1] = re + return t, nil +} + +// concat replaces the top of the stack (above the topmost '|' or '(') with its concatenation. +func (p *parser) concat() *Regexp { + p.maybeConcat(-1, 0) + + // Scan down to find pseudo-operator | or (. + i := len(p.stack) + for i > 0 && p.stack[i-1].Op < opPseudo { + i-- + } + subs := p.stack[i:] + p.stack = p.stack[:i] + + // Empty concatenation is special case. + if len(subs) == 0 { + return p.push(p.newRegexp(OpEmptyMatch)) + } + + return p.push(p.collapse(subs, OpConcat)) +} + +// alternate replaces the top of the stack (above the topmost '(') with its alternation. +func (p *parser) alternate() *Regexp { + // Scan down to find pseudo-operator (. + // There are no | above (. + i := len(p.stack) + for i > 0 && p.stack[i-1].Op < opPseudo { + i-- + } + subs := p.stack[i:] + p.stack = p.stack[:i] + + // Make sure top class is clean. + // All the others already are (see swapVerticalBar). + if len(subs) > 0 { + cleanAlt(subs[len(subs)-1]) + } + + // Empty alternate is special case + // (shouldn't happen but easy to handle). + if len(subs) == 0 { + return p.push(p.newRegexp(OpNoMatch)) + } + + return p.push(p.collapse(subs, OpAlternate)) +} + +// cleanAlt cleans re for eventual inclusion in an alternation. +func cleanAlt(re *Regexp) { + switch re.Op { + case OpCharClass: + re.Rune = cleanClass(&re.Rune) + if len(re.Rune) == 2 && re.Rune[0] == 0 && re.Rune[1] == unicode.MaxRune { + re.Rune = nil + re.Op = OpAnyChar + return + } + if len(re.Rune) == 4 && re.Rune[0] == 0 && re.Rune[1] == '\n'-1 && re.Rune[2] == '\n'+1 && re.Rune[3] == unicode.MaxRune { + re.Rune = nil + re.Op = OpAnyCharNotNL + return + } + if cap(re.Rune)-len(re.Rune) > 100 { + // re.Rune will not grow any more. + // Make a copy or inline to reclaim storage. + re.Rune = append(re.Rune0[:0], re.Rune...) + } + } +} + +// collapse returns the result of applying op to sub. +// If sub contains op nodes, they all get hoisted up +// so that there is never a concat of a concat or an +// alternate of an alternate. +func (p *parser) collapse(subs []*Regexp, op Op) *Regexp { + if len(subs) == 1 { + return subs[0] + } + re := p.newRegexp(op) + re.Sub = re.Sub0[:0] + for _, sub := range subs { + if sub.Op == op { + re.Sub = append(re.Sub, sub.Sub...) + p.reuse(sub) + } else { + re.Sub = append(re.Sub, sub) + } + } + if op == OpAlternate { + re.Sub = p.factor(re.Sub, re.Flags) + if len(re.Sub) == 1 { + old := re + re = re.Sub[0] + p.reuse(old) + } + } + return re +} + +// factor factors common prefixes from the alternation list sub. +// It returns a replacement list that reuses the same storage and +// frees (passes to p.reuse) any removed *Regexps. +// +// For example, +// ABC|ABD|AEF|BCX|BCY +// simplifies by literal prefix extraction to +// A(B(C|D)|EF)|BC(X|Y) +// which simplifies by character class introduction to +// A(B[CD]|EF)|BC[XY] +// +func (p *parser) factor(sub []*Regexp, flags Flags) []*Regexp { + if len(sub) < 2 { + return sub + } + + // Round 1: Factor out common literal prefixes. + var str []int + var strflags Flags + start := 0 + out := sub[:0] + for i := 0; i <= len(sub); i++ { + // Invariant: the Regexps that were in sub[0:start] have been + // used or marked for reuse, and the slice space has been reused + // for out (len(out) <= start). + // + // Invariant: sub[start:i] consists of regexps that all begin + // with str as modified by strflags. + var istr []int + var iflags Flags + if i < len(sub) { + istr, iflags = p.leadingString(sub[i]) + if iflags == strflags { + same := 0 + for same < len(str) && same < len(istr) && str[same] == istr[same] { + same++ + } + if same > 0 { + // Matches at least one rune in current range. + // Keep going around. + str = str[:same] + continue + } + } + } + + // Found end of a run with common leading literal string: + // sub[start:i] all begin with str[0:len(str)], but sub[i] + // does not even begin with str[0]. + // + // Factor out common string and append factored expression to out. + if i == start { + // Nothing to do - run of length 0. + } else if i == start+1 { + // Just one: don't bother factoring. + out = append(out, sub[start]) + } else { + // Construct factored form: prefix(suffix1|suffix2|...) + prefix := p.newRegexp(OpLiteral) + prefix.Flags = strflags + prefix.Rune = append(prefix.Rune[:0], str...) + + for j := start; j < i; j++ { + sub[j] = p.removeLeadingString(sub[j], len(str)) + } + suffix := p.collapse(sub[start:i], OpAlternate) // recurse + + re := p.newRegexp(OpConcat) + re.Sub = append(re.Sub[:0], prefix, suffix) + out = append(out, re) + } + + // Prepare for next iteration. + start = i + str = istr + strflags = iflags + } + sub = out + + // Round 2: Factor out common complex prefixes, + // just the first piece of each concatenation, + // whatever it is. This is good enough a lot of the time. + start = 0 + out = sub[:0] + var first *Regexp + for i := 0; i <= len(sub); i++ { + // Invariant: the Regexps that were in sub[0:start] have been + // used or marked for reuse, and the slice space has been reused + // for out (len(out) <= start). + // + // Invariant: sub[start:i] consists of regexps that all begin + // with str as modified by strflags. + var ifirst *Regexp + if i < len(sub) { + ifirst = p.leadingRegexp(sub[i]) + if first != nil && first.Equal(ifirst) { + continue + } + } + + // Found end of a run with common leading regexp: + // sub[start:i] all begin with first but sub[i] does not. + // + // Factor out common regexp and append factored expression to out. + if i == start { + // Nothing to do - run of length 0. + } else if i == start+1 { + // Just one: don't bother factoring. + out = append(out, sub[start]) + } else { + // Construct factored form: prefix(suffix1|suffix2|...) + prefix := first + + for j := start; j < i; j++ { + reuse := j != start // prefix came from sub[start] + sub[j] = p.removeLeadingRegexp(sub[j], reuse) + } + suffix := p.collapse(sub[start:i], OpAlternate) // recurse + + re := p.newRegexp(OpConcat) + re.Sub = append(re.Sub[:0], prefix, suffix) + out = append(out, re) + } + + // Prepare for next iteration. + start = i + first = ifirst + } + sub = out + + // Round 3: Collapse runs of single literals into character classes. + start = 0 + out = sub[:0] + for i := 0; i <= len(sub); i++ { + // Invariant: the Regexps that were in sub[0:start] have been + // used or marked for reuse, and the slice space has been reused + // for out (len(out) <= start). + // + // Invariant: sub[start:i] consists of regexps that are either + // literal runes or character classes. + if i < len(sub) && isCharClass(sub[i]) { + continue + } + + // sub[i] is not a char or char class; + // emit char class for sub[start:i]... + if i == start { + // Nothing to do - run of length 0. + } else if i == start+1 { + out = append(out, sub[start]) + } else { + // Make new char class. + // Start with most complex regexp in sub[start]. + max := start + for j := start + 1; j < i; j++ { + if sub[max].Op < sub[j].Op || sub[max].Op == sub[j].Op && len(sub[max].Rune) < len(sub[j].Rune) { + max = j + } + } + sub[start], sub[max] = sub[max], sub[start] + + for j := start + 1; j < i; j++ { + mergeCharClass(sub[start], sub[j]) + p.reuse(sub[j]) + } + cleanAlt(sub[start]) + out = append(out, sub[start]) + } + + // ... and then emit sub[i]. + if i < len(sub) { + out = append(out, sub[i]) + } + start = i + 1 + } + sub = out + + // Round 4: Collapse runs of empty matches into a single empty match. + start = 0 + out = sub[:0] + for i := range sub { + if i+1 < len(sub) && sub[i].Op == OpEmptyMatch && sub[i+1].Op == OpEmptyMatch { + continue + } + out = append(out, sub[i]) + } + sub = out + + return sub +} + +// leadingString returns the leading literal string that re begins with. +// The string refers to storage in re or its children. +func (p *parser) leadingString(re *Regexp) ([]int, Flags) { + if re.Op == OpConcat && len(re.Sub) > 0 { + re = re.Sub[0] + } + if re.Op != OpLiteral { + return nil, 0 + } + return re.Rune, re.Flags & FoldCase +} + +// removeLeadingString removes the first n leading runes +// from the beginning of re. It returns the replacement for re. +func (p *parser) removeLeadingString(re *Regexp, n int) *Regexp { + if re.Op == OpConcat && len(re.Sub) > 0 { + // Removing a leading string in a concatenation + // might simplify the concatenation. + sub := re.Sub[0] + sub = p.removeLeadingString(sub, n) + re.Sub[0] = sub + if sub.Op == OpEmptyMatch { + p.reuse(sub) + switch len(re.Sub) { + case 0, 1: + // Impossible but handle. + re.Op = OpEmptyMatch + re.Sub = nil + case 2: + old := re + re = re.Sub[1] + p.reuse(old) + default: + copy(re.Sub, re.Sub[1:]) + re.Sub = re.Sub[:len(re.Sub)-1] + } + } + return re + } + + if re.Op == OpLiteral { + re.Rune = re.Rune[:copy(re.Rune, re.Rune[n:])] + if len(re.Rune) == 0 { + re.Op = OpEmptyMatch + } + } + return re +} + +// leadingRegexp returns the leading regexp that re begins with. +// The regexp refers to storage in re or its children. +func (p *parser) leadingRegexp(re *Regexp) *Regexp { + if re.Op == OpEmptyMatch { + return nil + } + if re.Op == OpConcat && len(re.Sub) > 0 { + sub := re.Sub[0] + if sub.Op == OpEmptyMatch { + return nil + } + return sub + } + return re +} + +// removeLeadingRegexp removes the leading regexp in re. +// It returns the replacement for re. +// If reuse is true, it passes the removed regexp (if no longer needed) to p.reuse. +func (p *parser) removeLeadingRegexp(re *Regexp, reuse bool) *Regexp { + if re.Op == OpConcat && len(re.Sub) > 0 { + if reuse { + p.reuse(re.Sub[0]) + } + re.Sub = re.Sub[:copy(re.Sub, re.Sub[1:])] + switch len(re.Sub) { + case 0: + re.Op = OpEmptyMatch + re.Sub = nil + case 1: + old := re + re = re.Sub[0] + p.reuse(old) + } + return re + } + re.Op = OpEmptyMatch + return re +} + +func literalRegexp(s string, flags Flags) *Regexp { + re := &Regexp{Op: OpLiteral} + re.Flags = flags + re.Rune = re.Rune0[:0] // use local storage for small strings + for _, c := range s { + if len(re.Rune) >= cap(re.Rune) { + // string is too long to fit in Rune0. let Go handle it + re.Rune = []int(s) + break + } + re.Rune = append(re.Rune, c) + } + return re +} + +// Parsing. + +func Parse(s string, flags Flags) (*Regexp, os.Error) { + if flags&Literal != 0 { + // Trivial parser for literal string. + if err := checkUTF8(s); err != nil { + return nil, err + } + return literalRegexp(s, flags), nil + } + + // Otherwise, must do real work. + var ( + p parser + err os.Error + c int + op Op + lastRepeat string + min, max int + ) + p.flags = flags + p.wholeRegexp = s + t := s + for t != "" { + repeat := "" + BigSwitch: + switch t[0] { + default: + if c, t, err = nextRune(t); err != nil { + return nil, err + } + p.literal(c) + + case '(': + if p.flags&PerlX != 0 && len(t) >= 2 && t[1] == '?' { + // Flag changes and non-capturing groups. + if t, err = p.parsePerlFlags(t); err != nil { + return nil, err + } + break + } + p.numCap++ + p.op(opLeftParen).Cap = p.numCap + t = t[1:] + case '|': + if err = p.parseVerticalBar(); err != nil { + return nil, err + } + t = t[1:] + case ')': + if err = p.parseRightParen(); err != nil { + return nil, err + } + t = t[1:] + case '^': + if p.flags&OneLine != 0 { + p.op(OpBeginText) + } else { + p.op(OpBeginLine) + } + t = t[1:] + case '$': + if p.flags&OneLine != 0 { + p.op(OpEndText).Flags |= WasDollar + } else { + p.op(OpEndLine) + } + t = t[1:] + case '.': + if p.flags&DotNL != 0 { + p.op(OpAnyChar) + } else { + p.op(OpAnyCharNotNL) + } + t = t[1:] + case '[': + if t, err = p.parseClass(t); err != nil { + return nil, err + } + case '*', '+', '?': + switch t[0] { + case '*': + op = OpStar + case '+': + op = OpPlus + case '?': + op = OpQuest + } + if t, err = p.repeat(op, min, max, t[:1], t[1:], lastRepeat); err != nil { + return nil, err + } + case '{': + op = OpRepeat + min, max, tt, ok := p.parseRepeat(t) + if !ok { + // If the repeat cannot be parsed, { is a literal. + p.literal('{') + t = t[1:] + break + } + if t, err = p.repeat(op, min, max, t[:len(t)-len(tt)], tt, lastRepeat); err != nil { + return nil, err + } + case '\\': + if p.flags&PerlX != 0 && len(t) >= 2 { + switch t[1] { + case 'A': + p.op(OpBeginText) + t = t[2:] + break BigSwitch + case 'b': + p.op(OpWordBoundary) + t = t[2:] + break BigSwitch + case 'B': + p.op(OpNoWordBoundary) + t = t[2:] + break BigSwitch + case 'C': + // any byte; not supported + return nil, &Error{ErrInvalidEscape, t[:2]} + case 'Q': + // \Q ... \E: the ... is always literals + var lit string + if i := strings.Index(t, `\E`); i < 0 { + lit = t[2:] + t = "" + } else { + lit = t[2:i] + t = t[i+2:] + } + p.push(literalRegexp(lit, p.flags)) + break BigSwitch + case 'z': + p.op(OpEndText) + t = t[2:] + break BigSwitch + } + } + + re := p.newRegexp(OpCharClass) + re.Flags = p.flags + + // Look for Unicode character group like \p{Han} + if len(t) >= 2 && (t[1] == 'p' || t[1] == 'P') { + r, rest, err := p.parseUnicodeClass(t, re.Rune0[:0]) + if err != nil { + return nil, err + } + if r != nil { + re.Rune = r + t = rest + p.push(re) + break BigSwitch + } + } + + // Perl character class escape. + if r, rest := p.parsePerlClassEscape(t, re.Rune0[:0]); r != nil { + re.Rune = r + t = rest + p.push(re) + break BigSwitch + } + p.reuse(re) + + // Ordinary single-character escape. + if c, t, err = p.parseEscape(t); err != nil { + return nil, err + } + p.literal(c) + } + lastRepeat = repeat + } + + p.concat() + if p.swapVerticalBar() { + // pop vertical bar + p.stack = p.stack[:len(p.stack)-1] + } + p.alternate() + + n := len(p.stack) + if n != 1 { + return nil, &Error{ErrMissingParen, s} + } + return p.stack[0], nil +} + +// parseRepeat parses {min} (max=min) or {min,} (max=-1) or {min,max}. +// If s is not of that form, it returns ok == false. +func (p *parser) parseRepeat(s string) (min, max int, rest string, ok bool) { + if s == "" || s[0] != '{' { + return + } + s = s[1:] + if min, s, ok = p.parseInt(s); !ok { + return + } + if s == "" { + return + } + if s[0] != ',' { + max = min + } else { + s = s[1:] + if s == "" { + return + } + if s[0] == '}' { + max = -1 + } else if max, s, ok = p.parseInt(s); !ok { + return + } + } + if s == "" || s[0] != '}' { + return + } + rest = s[1:] + ok = true + return +} + +// parsePerlFlags parses a Perl flag setting or non-capturing group or both, +// like (?i) or (?: or (?i:. It removes the prefix from s and updates the parse state. +// The caller must have ensured that s begins with "(?". +func (p *parser) parsePerlFlags(s string) (rest string, err os.Error) { + t := s + + // Check for named captures, first introduced in Python's regexp library. + // As usual, there are three slightly different syntaxes: + // + // (?P<name>expr) the original, introduced by Python + // (?<name>expr) the .NET alteration, adopted by Perl 5.10 + // (?'name'expr) another .NET alteration, adopted by Perl 5.10 + // + // Perl 5.10 gave in and implemented the Python version too, + // but they claim that the last two are the preferred forms. + // PCRE and languages based on it (specifically, PHP and Ruby) + // support all three as well. EcmaScript 4 uses only the Python form. + // + // In both the open source world (via Code Search) and the + // Google source tree, (?P<expr>name) is the dominant form, + // so that's the one we implement. One is enough. + if len(t) > 4 && t[2] == 'P' && t[3] == '<' { + // Pull out name. + end := strings.IndexRune(t, '>') + if end < 0 { + if err = checkUTF8(t); err != nil { + return "", err + } + return "", &Error{ErrInvalidNamedCapture, s} + } + + capture := t[:end+1] // "(?P<name>" + name := t[4:end] // "name" + if err = checkUTF8(name); err != nil { + return "", err + } + if !isValidCaptureName(name) { + return "", &Error{ErrInvalidNamedCapture, capture} + } + + // Like ordinary capture, but named. + p.numCap++ + re := p.op(opLeftParen) + re.Cap = p.numCap + re.Name = name + return t[end+1:], nil + } + + // Non-capturing group. Might also twiddle Perl flags. + var c int + t = t[2:] // skip (? + flags := p.flags + sign := +1 + sawFlag := false +Loop: + for t != "" { + if c, t, err = nextRune(t); err != nil { + return "", err + } + switch c { + default: + break Loop + + // Flags. + case 'i': + flags |= FoldCase + sawFlag = true + case 'm': + flags &^= OneLine + sawFlag = true + case 's': + flags |= DotNL + sawFlag = true + case 'U': + flags |= NonGreedy + sawFlag = true + + // Switch to negation. + case '-': + if sign < 0 { + break Loop + } + sign = -1 + // Invert flags so that | above turn into &^ and vice versa. + // We'll invert flags again before using it below. + flags = ^flags + sawFlag = false + + // End of flags, starting group or not. + case ':', ')': + if sign < 0 { + if !sawFlag { + break Loop + } + flags = ^flags + } + if c == ':' { + // Open new group + p.op(opLeftParen) + } + p.flags = flags + return t, nil + } + } + + return "", &Error{ErrInvalidPerlOp, s[:len(s)-len(t)]} +} + +// isValidCaptureName reports whether name +// is a valid capture name: [A-Za-z0-9_]+. +// PCRE limits names to 32 bytes. +// Python rejects names starting with digits. +// We don't enforce either of those. +func isValidCaptureName(name string) bool { + if name == "" { + return false + } + for _, c := range name { + if c != '_' && !isalnum(c) { + return false + } + } + return true +} + +// parseInt parses a decimal integer. +func (p *parser) parseInt(s string) (n int, rest string, ok bool) { + if s == "" || s[0] < '0' || '9' < s[0] { + return + } + // Disallow leading zeros. + if len(s) >= 2 && s[0] == '0' && '0' <= s[1] && s[1] <= '9' { + return + } + for s != "" && '0' <= s[0] && s[0] <= '9' { + // Avoid overflow. + if n >= 1e8 { + return + } + n = n*10 + int(s[0]) - '0' + s = s[1:] + } + rest = s + ok = true + return +} + +// can this be represented as a character class? +// single-rune literal string, char class, ., and .|\n. +func isCharClass(re *Regexp) bool { + return re.Op == OpLiteral && len(re.Rune) == 1 || + re.Op == OpCharClass || + re.Op == OpAnyCharNotNL || + re.Op == OpAnyChar +} + +// does re match r? +func matchRune(re *Regexp, r int) bool { + switch re.Op { + case OpLiteral: + return len(re.Rune) == 1 && re.Rune[0] == r + case OpCharClass: + for i := 0; i < len(re.Rune); i += 2 { + if re.Rune[i] <= r && r <= re.Rune[i+1] { + return true + } + } + return false + case OpAnyCharNotNL: + return r != '\n' + case OpAnyChar: + return true + } + return false +} + +// parseVerticalBar handles a | in the input. +func (p *parser) parseVerticalBar() os.Error { + p.concat() + + // The concatenation we just parsed is on top of the stack. + // If it sits above an opVerticalBar, swap it below + // (things below an opVerticalBar become an alternation). + // Otherwise, push a new vertical bar. + if !p.swapVerticalBar() { + p.op(opVerticalBar) + } + + return nil +} + +// mergeCharClass makes dst = dst|src. +// The caller must ensure that dst.Op >= src.Op, +// to reduce the amount of copying. +func mergeCharClass(dst, src *Regexp) { + switch dst.Op { + case OpAnyChar: + // src doesn't add anything. + case OpAnyCharNotNL: + // src might add \n + if matchRune(src, '\n') { + dst.Op = OpAnyChar + } + case OpCharClass: + // src is simpler, so either literal or char class + if src.Op == OpLiteral { + dst.Rune = appendRange(dst.Rune, src.Rune[0], src.Rune[0]) + } else { + dst.Rune = appendClass(dst.Rune, src.Rune) + } + case OpLiteral: + // both literal + if src.Rune[0] == dst.Rune[0] { + break + } + dst.Op = OpCharClass + dst.Rune = append(dst.Rune, dst.Rune[0]) + dst.Rune = appendRange(dst.Rune, src.Rune[0], src.Rune[0]) + } +} + +// If the top of the stack is an element followed by an opVerticalBar +// swapVerticalBar swaps the two and returns true. +// Otherwise it returns false. +func (p *parser) swapVerticalBar() bool { + // If above and below vertical bar are literal or char class, + // can merge into a single char class. + n := len(p.stack) + if n >= 3 && p.stack[n-2].Op == opVerticalBar && isCharClass(p.stack[n-1]) && isCharClass(p.stack[n-3]) { + re1 := p.stack[n-1] + re3 := p.stack[n-3] + // Make re3 the more complex of the two. + if re1.Op > re3.Op { + re1, re3 = re3, re1 + p.stack[n-3] = re3 + } + mergeCharClass(re3, re1) + p.reuse(re1) + p.stack = p.stack[:n-1] + return true + } + + if n >= 2 { + re1 := p.stack[n-1] + re2 := p.stack[n-2] + if re2.Op == opVerticalBar { + if n >= 3 { + // Now out of reach. + // Clean opportunistically. + cleanAlt(p.stack[n-3]) + } + p.stack[n-2] = re1 + p.stack[n-1] = re2 + return true + } + } + return false +} + +// parseRightParen handles a ) in the input. +func (p *parser) parseRightParen() os.Error { + p.concat() + if p.swapVerticalBar() { + // pop vertical bar + p.stack = p.stack[:len(p.stack)-1] + } + p.alternate() + + n := len(p.stack) + if n < 2 { + return &Error{ErrInternalError, ""} + } + re1 := p.stack[n-1] + re2 := p.stack[n-2] + p.stack = p.stack[:n-2] + if re2.Op != opLeftParen { + return &Error{ErrMissingParen, p.wholeRegexp} + } + if re2.Cap == 0 { + // Just for grouping. + p.push(re1) + } else { + re2.Op = OpCapture + re2.Sub = re2.Sub0[:1] + re2.Sub[0] = re1 + p.push(re2) + } + return nil +} + +// parseEscape parses an escape sequence at the beginning of s +// and returns the rune. +func (p *parser) parseEscape(s string) (r int, rest string, err os.Error) { + t := s[1:] + if t == "" { + return 0, "", &Error{ErrTrailingBackslash, ""} + } + c, t, err := nextRune(t) + if err != nil { + return 0, "", err + } + +Switch: + switch c { + default: + if c < utf8.RuneSelf && !isalnum(c) { + // Escaped non-word characters are always themselves. + // PCRE is not quite so rigorous: it accepts things like + // \q, but we don't. We once rejected \_, but too many + // programs and people insist on using it, so allow \_. + return c, t, nil + } + + // Octal escapes. + case '1', '2', '3', '4', '5', '6', '7': + // Single non-zero digit is a backreference; not supported + if t == "" || t[0] < '0' || t[0] > '7' { + break + } + fallthrough + case '0': + // Consume up to three octal digits; already have one. + r = c - '0' + for i := 1; i < 3; i++ { + if t == "" || t[0] < '0' || t[0] > '7' { + break + } + r = r*8 + int(t[0]) - '0' + t = t[1:] + } + return r, t, nil + + // Hexadecimal escapes. + case 'x': + if t == "" { + break + } + if c, t, err = nextRune(t); err != nil { + return 0, "", err + } + if c == '{' { + // Any number of digits in braces. + // Perl accepts any text at all; it ignores all text + // after the first non-hex digit. We require only hex digits, + // and at least one. + nhex := 0 + r = 0 + for { + if t == "" { + break Switch + } + if c, t, err = nextRune(t); err != nil { + return 0, "", err + } + if c == '}' { + break + } + v := unhex(c) + if v < 0 { + break Switch + } + r = r*16 + v + if r > unicode.MaxRune { + break Switch + } + nhex++ + } + if nhex == 0 { + break Switch + } + return r, t, nil + } + + // Easy case: two hex digits. + x := unhex(c) + if c, t, err = nextRune(t); err != nil { + return 0, "", err + } + y := unhex(c) + if x < 0 || y < 0 { + break + } + return x*16 + y, t, nil + + // C escapes. There is no case 'b', to avoid misparsing + // the Perl word-boundary \b as the C backspace \b + // when in POSIX mode. In Perl, /\b/ means word-boundary + // but /[\b]/ means backspace. We don't support that. + // If you want a backspace, embed a literal backspace + // character or use \x08. + case 'a': + return '\a', t, err + case 'f': + return '\f', t, err + case 'n': + return '\n', t, err + case 'r': + return '\r', t, err + case 't': + return '\t', t, err + case 'v': + return '\v', t, err + } + return 0, "", &Error{ErrInvalidEscape, s[:len(s)-len(t)]} +} + +// parseClassChar parses a character class character at the beginning of s +// and returns it. +func (p *parser) parseClassChar(s, wholeClass string) (r int, rest string, err os.Error) { + if s == "" { + return 0, "", &Error{Code: ErrMissingBracket, Expr: wholeClass} + } + + // Allow regular escape sequences even though + // many need not be escaped in this context. + if s[0] == '\\' { + return p.parseEscape(s) + } + + return nextRune(s) +} + +type charGroup struct { + sign int + class []int +} + +// parsePerlClassEscape parses a leading Perl character class escape like \d +// from the beginning of s. If one is present, it appends the characters to r +// and returns the new slice r and the remainder of the string. +func (p *parser) parsePerlClassEscape(s string, r []int) (out []int, rest string) { + if p.flags&PerlX == 0 || len(s) < 2 || s[0] != '\\' { + return + } + g := perlGroup[s[0:2]] + if g.sign == 0 { + return + } + return p.appendGroup(r, g), s[2:] +} + +// parseNamedClass parses a leading POSIX named character class like [:alnum:] +// from the beginning of s. If one is present, it appends the characters to r +// and returns the new slice r and the remainder of the string. +func (p *parser) parseNamedClass(s string, r []int) (out []int, rest string, err os.Error) { + if len(s) < 2 || s[0] != '[' || s[1] != ':' { + return + } + + i := strings.Index(s[2:], ":]") + if i < 0 { + return + } + i += 2 + name, s := s[0:i+2], s[i+2:] + g := posixGroup[name] + if g.sign == 0 { + return nil, "", &Error{ErrInvalidCharRange, name} + } + return p.appendGroup(r, g), s, nil +} + +func (p *parser) appendGroup(r []int, g charGroup) []int { + if p.flags&FoldCase == 0 { + if g.sign < 0 { + r = appendNegatedClass(r, g.class) + } else { + r = appendClass(r, g.class) + } + } else { + tmp := p.tmpClass[:0] + tmp = appendFoldedClass(tmp, g.class) + p.tmpClass = tmp + tmp = cleanClass(&p.tmpClass) + if g.sign < 0 { + r = appendNegatedClass(r, tmp) + } else { + r = appendClass(r, tmp) + } + } + return r +} + +// unicodeTable returns the unicode.RangeTable identified by name +// and the table of additional fold-equivalent code points. +func unicodeTable(name string) (*unicode.RangeTable, *unicode.RangeTable) { + if t := unicode.Categories[name]; t != nil { + return t, unicode.FoldCategory[name] + } + if t := unicode.Scripts[name]; t != nil { + return t, unicode.FoldScript[name] + } + return nil, nil +} + +// parseUnicodeClass parses a leading Unicode character class like \p{Han} +// from the beginning of s. If one is present, it appends the characters to r +// and returns the new slice r and the remainder of the string. +func (p *parser) parseUnicodeClass(s string, r []int) (out []int, rest string, err os.Error) { + if p.flags&UnicodeGroups == 0 || len(s) < 2 || s[0] != '\\' || s[1] != 'p' && s[1] != 'P' { + return + } + + // Committed to parse or return error. + sign := +1 + if s[1] == 'P' { + sign = -1 + } + t := s[2:] + c, t, err := nextRune(t) + if err != nil { + return + } + var seq, name string + if c != '{' { + // Single-letter name. + seq = s[:len(s)-len(t)] + name = seq[2:] + } else { + // Name is in braces. + end := strings.IndexRune(s, '}') + if end < 0 { + if err = checkUTF8(s); err != nil { + return + } + return nil, "", &Error{ErrInvalidCharRange, s} + } + seq, t = s[:end+1], s[end+1:] + name = s[3:end] + if err = checkUTF8(name); err != nil { + return + } + } + + // Group can have leading negation too. \p{^Han} == \P{Han}, \P{^Han} == \p{Han}. + if name != "" && name[0] == '^' { + sign = -sign + name = name[1:] + } + + tab, fold := unicodeTable(name) + if tab == nil { + return nil, "", &Error{ErrInvalidCharRange, seq} + } + + if p.flags&FoldCase == 0 || fold == nil { + if sign > 0 { + r = appendTable(r, tab) + } else { + r = appendNegatedTable(r, tab) + } + } else { + // Merge and clean tab and fold in a temporary buffer. + // This is necessary for the negative case and just tidy + // for the positive case. + tmp := p.tmpClass[:0] + tmp = appendTable(tmp, tab) + tmp = appendTable(tmp, fold) + p.tmpClass = tmp + tmp = cleanClass(&p.tmpClass) + if sign > 0 { + r = appendClass(r, tmp) + } else { + r = appendNegatedClass(r, tmp) + } + } + return r, t, nil +} + +// parseClass parses a character class at the beginning of s +// and pushes it onto the parse stack. +func (p *parser) parseClass(s string) (rest string, err os.Error) { + t := s[1:] // chop [ + re := p.newRegexp(OpCharClass) + re.Flags = p.flags + re.Rune = re.Rune0[:0] + + sign := +1 + if t != "" && t[0] == '^' { + sign = -1 + t = t[1:] + + // If character class does not match \n, add it here, + // so that negation later will do the right thing. + if p.flags&ClassNL == 0 { + re.Rune = append(re.Rune, '\n', '\n') + } + } + + class := re.Rune + first := true // ] and - are okay as first char in class + for t == "" || t[0] != ']' || first { + // POSIX: - is only okay unescaped as first or last in class. + // Perl: - is okay anywhere. + if t != "" && t[0] == '-' && p.flags&PerlX == 0 && !first && (len(t) == 1 || t[1] != ']') { + _, size := utf8.DecodeRuneInString(t[1:]) + return "", &Error{Code: ErrInvalidCharRange, Expr: t[:1+size]} + } + first = false + + // Look for POSIX [:alnum:] etc. + if len(t) > 2 && t[0] == '[' && t[1] == ':' { + nclass, nt, err := p.parseNamedClass(t, class) + if err != nil { + return "", err + } + if nclass != nil { + class, t = nclass, nt + continue + } + } + + // Look for Unicode character group like \p{Han}. + nclass, nt, err := p.parseUnicodeClass(t, class) + if err != nil { + return "", err + } + if nclass != nil { + class, t = nclass, nt + continue + } + + // Look for Perl character class symbols (extension). + if nclass, nt := p.parsePerlClassEscape(t, class); nclass != nil { + class, t = nclass, nt + continue + } + + // Single character or simple range. + rng := t + var lo, hi int + if lo, t, err = p.parseClassChar(t, s); err != nil { + return "", err + } + hi = lo + // [a-] means (a|-) so check for final ]. + if len(t) >= 2 && t[0] == '-' && t[1] != ']' { + t = t[1:] + if hi, t, err = p.parseClassChar(t, s); err != nil { + return "", err + } + if hi < lo { + rng = rng[:len(rng)-len(t)] + return "", &Error{Code: ErrInvalidCharRange, Expr: rng} + } + } + if p.flags&FoldCase == 0 { + class = appendRange(class, lo, hi) + } else { + class = appendFoldedRange(class, lo, hi) + } + } + t = t[1:] // chop ] + + // Use &re.Rune instead of &class to avoid allocation. + re.Rune = class + class = cleanClass(&re.Rune) + if sign < 0 { + class = negateClass(class) + } + re.Rune = class + p.push(re) + return t, nil +} + +// cleanClass sorts the ranges (pairs of elements of r), +// merges them, and eliminates duplicates. +func cleanClass(rp *[]int) []int { + + // Sort by lo increasing, hi decreasing to break ties. + sort.Sort(ranges{rp}) + + r := *rp + if len(r) < 2 { + return r + } + + // Merge abutting, overlapping. + w := 2 // write index + for i := 2; i < len(r); i += 2 { + lo, hi := r[i], r[i+1] + if lo <= r[w-1]+1 { + // merge with previous range + if hi > r[w-1] { + r[w-1] = hi + } + continue + } + // new disjoint range + r[w] = lo + r[w+1] = hi + w += 2 + } + + return r[:w] +} + +// appendRange returns the result of appending the range lo-hi to the class r. +func appendRange(r []int, lo, hi int) []int { + // Expand last range or next to last range if it overlaps or abuts. + // Checking two ranges helps when appending case-folded + // alphabets, so that one range can be expanding A-Z and the + // other expanding a-z. + n := len(r) + for i := 2; i <= 4; i += 2 { // twice, using i=2, i=4 + if n >= i { + rlo, rhi := r[n-i], r[n-i+1] + if lo <= rhi+1 && rlo <= hi+1 { + if lo < rlo { + r[n-i] = lo + } + if hi > rhi { + r[n-i+1] = hi + } + return r + } + } + } + + return append(r, lo, hi) +} + +const ( + // minimum and maximum runes involved in folding. + // checked during test. + minFold = 0x0041 + maxFold = 0x1044f +) + +// appendFoldedRange returns the result of appending the range lo-hi +// and its case folding-equivalent runes to the class r. +func appendFoldedRange(r []int, lo, hi int) []int { + // Optimizations. + if lo <= minFold && hi >= maxFold { + // Range is full: folding can't add more. + return appendRange(r, lo, hi) + } + if hi < minFold || lo > maxFold { + // Range is outside folding possibilities. + return appendRange(r, lo, hi) + } + if lo < minFold { + // [lo, minFold-1] needs no folding. + r = appendRange(r, lo, minFold-1) + lo = minFold + } + if hi > maxFold { + // [maxFold+1, hi] needs no folding. + r = appendRange(r, maxFold+1, hi) + hi = maxFold + } + + // Brute force. Depend on appendRange to coalesce ranges on the fly. + for c := lo; c <= hi; c++ { + r = appendRange(r, c, c) + f := unicode.SimpleFold(c) + for f != c { + r = appendRange(r, f, f) + f = unicode.SimpleFold(f) + } + } + return r +} + +// appendClass returns the result of appending the class x to the class r. +// It assume x is clean. +func appendClass(r []int, x []int) []int { + for i := 0; i < len(x); i += 2 { + r = appendRange(r, x[i], x[i+1]) + } + return r +} + +// appendFolded returns the result of appending the case folding of the class x to the class r. +func appendFoldedClass(r []int, x []int) []int { + for i := 0; i < len(x); i += 2 { + r = appendFoldedRange(r, x[i], x[i+1]) + } + return r +} + +// appendNegatedClass returns the result of appending the negation of the class x to the class r. +// It assumes x is clean. +func appendNegatedClass(r []int, x []int) []int { + nextLo := 0 + for i := 0; i < len(x); i += 2 { + lo, hi := x[i], x[i+1] + if nextLo <= lo-1 { + r = appendRange(r, nextLo, lo-1) + } + nextLo = hi + 1 + } + if nextLo <= unicode.MaxRune { + r = appendRange(r, nextLo, unicode.MaxRune) + } + return r +} + +// appendTable returns the result of appending x to the class r. +func appendTable(r []int, x *unicode.RangeTable) []int { + for _, xr := range x.R16 { + lo, hi, stride := int(xr.Lo), int(xr.Hi), int(xr.Stride) + if stride == 1 { + r = appendRange(r, lo, hi) + continue + } + for c := lo; c <= hi; c += stride { + r = appendRange(r, c, c) + } + } + for _, xr := range x.R32 { + lo, hi, stride := int(xr.Lo), int(xr.Hi), int(xr.Stride) + if stride == 1 { + r = appendRange(r, lo, hi) + continue + } + for c := lo; c <= hi; c += stride { + r = appendRange(r, c, c) + } + } + return r +} + +// appendNegatedTable returns the result of appending the negation of x to the class r. +func appendNegatedTable(r []int, x *unicode.RangeTable) []int { + nextLo := 0 // lo end of next class to add + for _, xr := range x.R16 { + lo, hi, stride := int(xr.Lo), int(xr.Hi), int(xr.Stride) + if stride == 1 { + if nextLo <= lo-1 { + r = appendRange(r, nextLo, lo-1) + } + nextLo = hi + 1 + continue + } + for c := lo; c <= hi; c += stride { + if nextLo <= c-1 { + r = appendRange(r, nextLo, c-1) + } + nextLo = c + 1 + } + } + for _, xr := range x.R32 { + lo, hi, stride := int(xr.Lo), int(xr.Hi), int(xr.Stride) + if stride == 1 { + if nextLo <= lo-1 { + r = appendRange(r, nextLo, lo-1) + } + nextLo = hi + 1 + continue + } + for c := lo; c <= hi; c += stride { + if nextLo <= c-1 { + r = appendRange(r, nextLo, c-1) + } + nextLo = c + 1 + } + } + if nextLo <= unicode.MaxRune { + r = appendRange(r, nextLo, unicode.MaxRune) + } + return r +} + +// negateClass overwrites r and returns r's negation. +// It assumes the class r is already clean. +func negateClass(r []int) []int { + nextLo := 0 // lo end of next class to add + w := 0 // write index + for i := 0; i < len(r); i += 2 { + lo, hi := r[i], r[i+1] + if nextLo <= lo-1 { + r[w] = nextLo + r[w+1] = lo - 1 + w += 2 + } + nextLo = hi + 1 + } + r = r[:w] + if nextLo <= unicode.MaxRune { + // It's possible for the negation to have one more + // range - this one - than the original class, so use append. + r = append(r, nextLo, unicode.MaxRune) + } + return r +} + +// ranges implements sort.Interface on a []rune. +// The choice of receiver type definition is strange +// but avoids an allocation since we already have +// a *[]int. +type ranges struct { + p *[]int +} + +func (ra ranges) Less(i, j int) bool { + p := *ra.p + i *= 2 + j *= 2 + return p[i] < p[j] || p[i] == p[j] && p[i+1] > p[j+1] +} + +func (ra ranges) Len() int { + return len(*ra.p) / 2 +} + +func (ra ranges) Swap(i, j int) { + p := *ra.p + i *= 2 + j *= 2 + p[i], p[i+1], p[j], p[j+1] = p[j], p[j+1], p[i], p[i+1] +} + + +func checkUTF8(s string) os.Error { + for s != "" { + rune, size := utf8.DecodeRuneInString(s) + if rune == utf8.RuneError && size == 1 { + return &Error{Code: ErrInvalidUTF8, Expr: s} + } + s = s[size:] + } + return nil +} + +func nextRune(s string) (c int, t string, err os.Error) { + c, size := utf8.DecodeRuneInString(s) + if c == utf8.RuneError && size == 1 { + return 0, "", &Error{Code: ErrInvalidUTF8, Expr: s} + } + return c, s[size:], nil +} + +func isalnum(c int) bool { + return '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' +} + +func unhex(c int) int { + if '0' <= c && c <= '9' { + return c - '0' + } + if 'a' <= c && c <= 'f' { + return c - 'a' + 10 + } + if 'A' <= c && c <= 'F' { + return c - 'A' + 10 + } + return -1 +} diff --git a/src/pkg/exp/regexp/syntax/parse_test.go b/src/pkg/exp/regexp/syntax/parse_test.go new file mode 100644 index 000000000..779b9afde --- /dev/null +++ b/src/pkg/exp/regexp/syntax/parse_test.go @@ -0,0 +1,350 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package syntax + +import ( + "bytes" + "fmt" + "testing" + "unicode" +) + +var parseTests = []struct { + Regexp string + Dump string +}{ + // Base cases + {`a`, `lit{a}`}, + {`a.`, `cat{lit{a}dot{}}`}, + {`a.b`, `cat{lit{a}dot{}lit{b}}`}, + {`ab`, `str{ab}`}, + {`a.b.c`, `cat{lit{a}dot{}lit{b}dot{}lit{c}}`}, + {`abc`, `str{abc}`}, + {`a|^`, `alt{lit{a}bol{}}`}, + {`a|b`, `cc{0x61-0x62}`}, + {`(a)`, `cap{lit{a}}`}, + {`(a)|b`, `alt{cap{lit{a}}lit{b}}`}, + {`a*`, `star{lit{a}}`}, + {`a+`, `plus{lit{a}}`}, + {`a?`, `que{lit{a}}`}, + {`a{2}`, `rep{2,2 lit{a}}`}, + {`a{2,3}`, `rep{2,3 lit{a}}`}, + {`a{2,}`, `rep{2,-1 lit{a}}`}, + {`a*?`, `nstar{lit{a}}`}, + {`a+?`, `nplus{lit{a}}`}, + {`a??`, `nque{lit{a}}`}, + {`a{2}?`, `nrep{2,2 lit{a}}`}, + {`a{2,3}?`, `nrep{2,3 lit{a}}`}, + {`a{2,}?`, `nrep{2,-1 lit{a}}`}, + {``, `emp{}`}, + {`|`, `emp{}`}, // alt{emp{}emp{}} but got factored + {`|x|`, `alt{emp{}lit{x}emp{}}`}, + {`.`, `dot{}`}, + {`^`, `bol{}`}, + {`$`, `eol{}`}, + {`\|`, `lit{|}`}, + {`\(`, `lit{(}`}, + {`\)`, `lit{)}`}, + {`\*`, `lit{*}`}, + {`\+`, `lit{+}`}, + {`\?`, `lit{?}`}, + {`{`, `lit{{}`}, + {`}`, `lit{}}`}, + {`\.`, `lit{.}`}, + {`\^`, `lit{^}`}, + {`\$`, `lit{$}`}, + {`\\`, `lit{\}`}, + {`[ace]`, `cc{0x61 0x63 0x65}`}, + {`[abc]`, `cc{0x61-0x63}`}, + {`[a-z]`, `cc{0x61-0x7a}`}, + {`[a]`, `lit{a}`}, + {`\-`, `lit{-}`}, + {`-`, `lit{-}`}, + {`\_`, `lit{_}`}, + {`abc`, `str{abc}`}, + {`abc|def`, `alt{str{abc}str{def}}`}, + {`abc|def|ghi`, `alt{str{abc}str{def}str{ghi}}`}, + + // Posix and Perl extensions + {`[[:lower:]]`, `cc{0x61-0x7a}`}, + {`[a-z]`, `cc{0x61-0x7a}`}, + {`[^[:lower:]]`, `cc{0x0-0x60 0x7b-0x10ffff}`}, + {`[[:^lower:]]`, `cc{0x0-0x60 0x7b-0x10ffff}`}, + {`(?i)[[:lower:]]`, `cc{0x41-0x5a 0x61-0x7a 0x17f 0x212a}`}, + {`(?i)[a-z]`, `cc{0x41-0x5a 0x61-0x7a 0x17f 0x212a}`}, + {`(?i)[^[:lower:]]`, `cc{0x0-0x40 0x5b-0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}`}, + {`(?i)[[:^lower:]]`, `cc{0x0-0x40 0x5b-0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}`}, + {`\d`, `cc{0x30-0x39}`}, + {`\D`, `cc{0x0-0x2f 0x3a-0x10ffff}`}, + {`\s`, `cc{0x9-0xa 0xc-0xd 0x20}`}, + {`\S`, `cc{0x0-0x8 0xb 0xe-0x1f 0x21-0x10ffff}`}, + {`\w`, `cc{0x30-0x39 0x41-0x5a 0x5f 0x61-0x7a}`}, + {`\W`, `cc{0x0-0x2f 0x3a-0x40 0x5b-0x5e 0x60 0x7b-0x10ffff}`}, + {`(?i)\w`, `cc{0x30-0x39 0x41-0x5a 0x5f 0x61-0x7a 0x17f 0x212a}`}, + {`(?i)\W`, `cc{0x0-0x2f 0x3a-0x40 0x5b-0x5e 0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}`}, + {`[^\\]`, `cc{0x0-0x5b 0x5d-0x10ffff}`}, + // { `\C`, `byte{}` }, // probably never + + // Unicode, negatives, and a double negative. + {`\p{Braille}`, `cc{0x2800-0x28ff}`}, + {`\P{Braille}`, `cc{0x0-0x27ff 0x2900-0x10ffff}`}, + {`\p{^Braille}`, `cc{0x0-0x27ff 0x2900-0x10ffff}`}, + {`\P{^Braille}`, `cc{0x2800-0x28ff}`}, + {`\pZ`, `cc{0x20 0xa0 0x1680 0x180e 0x2000-0x200a 0x2028-0x2029 0x202f 0x205f 0x3000}`}, + {`[\p{Braille}]`, `cc{0x2800-0x28ff}`}, + {`[\P{Braille}]`, `cc{0x0-0x27ff 0x2900-0x10ffff}`}, + {`[\p{^Braille}]`, `cc{0x0-0x27ff 0x2900-0x10ffff}`}, + {`[\P{^Braille}]`, `cc{0x2800-0x28ff}`}, + {`[\pZ]`, `cc{0x20 0xa0 0x1680 0x180e 0x2000-0x200a 0x2028-0x2029 0x202f 0x205f 0x3000}`}, + {`\p{Lu}`, mkCharClass(unicode.IsUpper)}, + {`[\p{Lu}]`, mkCharClass(unicode.IsUpper)}, + {`(?i)[\p{Lu}]`, mkCharClass(isUpperFold)}, + + // Hex, octal. + {`[\012-\234]\141`, `cat{cc{0xa-0x9c}lit{a}}`}, + {`[\x{41}-\x7a]\x61`, `cat{cc{0x41-0x7a}lit{a}}`}, + + // More interesting regular expressions. + {`a{,2}`, `str{a{,2}}`}, + {`\.\^\$\\`, `str{.^$\}`}, + {`[a-zABC]`, `cc{0x41-0x43 0x61-0x7a}`}, + {`[^a]`, `cc{0x0-0x60 0x62-0x10ffff}`}, + {`[α-ε☺]`, `cc{0x3b1-0x3b5 0x263a}`}, // utf-8 + {`a*{`, `cat{star{lit{a}}lit{{}}`}, + + // Test precedences + {`(?:ab)*`, `star{str{ab}}`}, + {`(ab)*`, `star{cap{str{ab}}}`}, + {`ab|cd`, `alt{str{ab}str{cd}}`}, + {`a(b|c)d`, `cat{lit{a}cap{cc{0x62-0x63}}lit{d}}`}, + + // Test flattening. + {`(?:a)`, `lit{a}`}, + {`(?:ab)(?:cd)`, `str{abcd}`}, + {`(?:a+b+)(?:c+d+)`, `cat{plus{lit{a}}plus{lit{b}}plus{lit{c}}plus{lit{d}}}`}, + {`(?:a+|b+)|(?:c+|d+)`, `alt{plus{lit{a}}plus{lit{b}}plus{lit{c}}plus{lit{d}}}`}, + {`(?:a|b)|(?:c|d)`, `cc{0x61-0x64}`}, + {`a|.`, `dot{}`}, + {`.|a`, `dot{}`}, + {`(?:[abc]|A|Z|hello|world)`, `alt{cc{0x41 0x5a 0x61-0x63}str{hello}str{world}}`}, + {`(?:[abc]|A|Z)`, `cc{0x41 0x5a 0x61-0x63}`}, + + // Test Perl quoted literals + {`\Q+|*?{[\E`, `str{+|*?{[}`}, + {`\Q+\E+`, `plus{lit{+}}`}, + {`\Q\\E`, `lit{\}`}, + {`\Q\\\E`, `str{\\}`}, + + // Test Perl \A and \z + {`(?m)^`, `bol{}`}, + {`(?m)$`, `eol{}`}, + {`(?-m)^`, `bot{}`}, + {`(?-m)$`, `eot{}`}, + {`(?m)\A`, `bot{}`}, + {`(?m)\z`, `eot{\z}`}, + {`(?-m)\A`, `bot{}`}, + {`(?-m)\z`, `eot{\z}`}, + + // Test named captures + {`(?P<name>a)`, `cap{name:lit{a}}`}, + + // Case-folded literals + {`[Aa]`, `litfold{A}`}, + {`[\x{100}\x{101}]`, `litfold{Ā}`}, + {`[Δδ]`, `litfold{Δ}`}, + + // Strings + {`abcde`, `str{abcde}`}, + {`[Aa][Bb]cd`, `cat{strfold{AB}str{cd}}`}, + + // Factoring. + {`abc|abd|aef|bcx|bcy`, `alt{cat{lit{a}alt{cat{lit{b}cc{0x63-0x64}}str{ef}}}cat{str{bc}cc{0x78-0x79}}}`}, + {`ax+y|ax+z|ay+w`, `cat{lit{a}alt{cat{plus{lit{x}}cc{0x79-0x7a}}cat{plus{lit{y}}lit{w}}}}`}, +} + +const testFlags = MatchNL | PerlX | UnicodeGroups + +// Test Parse -> Dump. +func TestParseDump(t *testing.T) { + for _, tt := range parseTests { + re, err := Parse(tt.Regexp, testFlags) + if err != nil { + t.Errorf("Parse(%#q): %v", tt.Regexp, err) + continue + } + d := dump(re) + if d != tt.Dump { + t.Errorf("Parse(%#q).Dump() = %#q want %#q", tt.Regexp, d, tt.Dump) + } + } +} + +// dump prints a string representation of the regexp showing +// the structure explicitly. +func dump(re *Regexp) string { + var b bytes.Buffer + dumpRegexp(&b, re) + return b.String() +} + +var opNames = []string{ + OpNoMatch: "no", + OpEmptyMatch: "emp", + OpLiteral: "lit", + OpCharClass: "cc", + OpAnyCharNotNL: "dnl", + OpAnyChar: "dot", + OpBeginLine: "bol", + OpEndLine: "eol", + OpBeginText: "bot", + OpEndText: "eot", + OpWordBoundary: "wb", + OpNoWordBoundary: "nwb", + OpCapture: "cap", + OpStar: "star", + OpPlus: "plus", + OpQuest: "que", + OpRepeat: "rep", + OpConcat: "cat", + OpAlternate: "alt", +} + +// dumpRegexp writes an encoding of the syntax tree for the regexp re to b. +// It is used during testing to distinguish between parses that might print +// the same using re's String method. +func dumpRegexp(b *bytes.Buffer, re *Regexp) { + if int(re.Op) >= len(opNames) || opNames[re.Op] == "" { + fmt.Fprintf(b, "op%d", re.Op) + } else { + switch re.Op { + default: + b.WriteString(opNames[re.Op]) + case OpStar, OpPlus, OpQuest, OpRepeat: + if re.Flags&NonGreedy != 0 { + b.WriteByte('n') + } + b.WriteString(opNames[re.Op]) + case OpLiteral: + if len(re.Rune) > 1 { + b.WriteString("str") + } else { + b.WriteString("lit") + } + if re.Flags&FoldCase != 0 { + for _, r := range re.Rune { + if unicode.SimpleFold(r) != r { + b.WriteString("fold") + break + } + } + } + } + } + b.WriteByte('{') + switch re.Op { + case OpEndText: + if re.Flags&WasDollar == 0 { + b.WriteString(`\z`) + } + case OpLiteral: + for _, r := range re.Rune { + b.WriteRune(r) + } + case OpConcat, OpAlternate: + for _, sub := range re.Sub { + dumpRegexp(b, sub) + } + case OpStar, OpPlus, OpQuest: + dumpRegexp(b, re.Sub[0]) + case OpRepeat: + fmt.Fprintf(b, "%d,%d ", re.Min, re.Max) + dumpRegexp(b, re.Sub[0]) + case OpCapture: + if re.Name != "" { + b.WriteString(re.Name) + b.WriteByte(':') + } + dumpRegexp(b, re.Sub[0]) + case OpCharClass: + sep := "" + for i := 0; i < len(re.Rune); i += 2 { + b.WriteString(sep) + sep = " " + lo, hi := re.Rune[i], re.Rune[i+1] + if lo == hi { + fmt.Fprintf(b, "%#x", lo) + } else { + fmt.Fprintf(b, "%#x-%#x", lo, hi) + } + } + } + b.WriteByte('}') +} + +func mkCharClass(f func(int) bool) string { + re := &Regexp{Op: OpCharClass} + lo := -1 + for i := 0; i <= unicode.MaxRune; i++ { + if f(i) { + if lo < 0 { + lo = i + } + } else { + if lo >= 0 { + re.Rune = append(re.Rune, lo, i-1) + lo = -1 + } + } + } + if lo >= 0 { + re.Rune = append(re.Rune, lo, unicode.MaxRune) + } + return dump(re) +} + +func isUpperFold(rune int) bool { + if unicode.IsUpper(rune) { + return true + } + c := unicode.SimpleFold(rune) + for c != rune { + if unicode.IsUpper(c) { + return true + } + c = unicode.SimpleFold(c) + } + return false +} + +func TestFoldConstants(t *testing.T) { + last := -1 + for i := 0; i <= unicode.MaxRune; i++ { + if unicode.SimpleFold(i) == i { + continue + } + if last == -1 && minFold != i { + t.Errorf("minFold=%#U should be %#U", minFold, i) + } + last = i + } + if maxFold != last { + t.Errorf("maxFold=%#U should be %#U", maxFold, last) + } +} + +func TestAppendRangeCollapse(t *testing.T) { + // AppendRange should collapse each of the new ranges + // into the earlier ones (it looks back two ranges), so that + // the slice never grows very large. + // Note that we are not calling cleanClass. + var r []int + for i := 'A'; i <= 'Z'; i++ { + r = appendRange(r, i, i) + r = appendRange(r, i+'a'-'A', i+'a'-'A') + } + if string(r) != "AZaz" { + t.Errorf("appendRange interlaced A-Z a-z = %s, want AZaz", string(r)) + } +} diff --git a/src/pkg/exp/regexp/syntax/perl_groups.go b/src/pkg/exp/regexp/syntax/perl_groups.go new file mode 100644 index 000000000..05b392c40 --- /dev/null +++ b/src/pkg/exp/regexp/syntax/perl_groups.go @@ -0,0 +1,130 @@ +// GENERATED BY make_perl_groups.pl; DO NOT EDIT. +// make_perl_groups.pl >perl_groups.go + +package syntax + +var code1 = []int{ /* \d */ + 0x30, 0x39, +} + +var code2 = []int{ /* \s */ + 0x9, 0xa, + 0xc, 0xd, + 0x20, 0x20, +} + +var code3 = []int{ /* \w */ + 0x30, 0x39, + 0x41, 0x5a, + 0x5f, 0x5f, + 0x61, 0x7a, +} + +var perlGroup = map[string]charGroup{ + `\d`: {+1, code1}, + `\D`: {-1, code1}, + `\s`: {+1, code2}, + `\S`: {-1, code2}, + `\w`: {+1, code3}, + `\W`: {-1, code3}, +} +var code4 = []int{ /* [:alnum:] */ + 0x30, 0x39, + 0x41, 0x5a, + 0x61, 0x7a, +} + +var code5 = []int{ /* [:alpha:] */ + 0x41, 0x5a, + 0x61, 0x7a, +} + +var code6 = []int{ /* [:ascii:] */ + 0x0, 0x7f, +} + +var code7 = []int{ /* [:blank:] */ + 0x9, 0x9, + 0x20, 0x20, +} + +var code8 = []int{ /* [:cntrl:] */ + 0x0, 0x1f, + 0x7f, 0x7f, +} + +var code9 = []int{ /* [:digit:] */ + 0x30, 0x39, +} + +var code10 = []int{ /* [:graph:] */ + 0x21, 0x7e, +} + +var code11 = []int{ /* [:lower:] */ + 0x61, 0x7a, +} + +var code12 = []int{ /* [:print:] */ + 0x20, 0x7e, +} + +var code13 = []int{ /* [:punct:] */ + 0x21, 0x2f, + 0x3a, 0x40, + 0x5b, 0x60, + 0x7b, 0x7e, +} + +var code14 = []int{ /* [:space:] */ + 0x9, 0xd, + 0x20, 0x20, +} + +var code15 = []int{ /* [:upper:] */ + 0x41, 0x5a, +} + +var code16 = []int{ /* [:word:] */ + 0x30, 0x39, + 0x41, 0x5a, + 0x5f, 0x5f, + 0x61, 0x7a, +} + +var code17 = []int{ /* [:xdigit:] */ + 0x30, 0x39, + 0x41, 0x46, + 0x61, 0x66, +} + +var posixGroup = map[string]charGroup{ + `[:alnum:]`: {+1, code4}, + `[:^alnum:]`: {-1, code4}, + `[:alpha:]`: {+1, code5}, + `[:^alpha:]`: {-1, code5}, + `[:ascii:]`: {+1, code6}, + `[:^ascii:]`: {-1, code6}, + `[:blank:]`: {+1, code7}, + `[:^blank:]`: {-1, code7}, + `[:cntrl:]`: {+1, code8}, + `[:^cntrl:]`: {-1, code8}, + `[:digit:]`: {+1, code9}, + `[:^digit:]`: {-1, code9}, + `[:graph:]`: {+1, code10}, + `[:^graph:]`: {-1, code10}, + `[:lower:]`: {+1, code11}, + `[:^lower:]`: {-1, code11}, + `[:print:]`: {+1, code12}, + `[:^print:]`: {-1, code12}, + `[:punct:]`: {+1, code13}, + `[:^punct:]`: {-1, code13}, + `[:space:]`: {+1, code14}, + `[:^space:]`: {-1, code14}, + `[:upper:]`: {+1, code15}, + `[:^upper:]`: {-1, code15}, + `[:word:]`: {+1, code16}, + `[:^word:]`: {-1, code16}, + `[:xdigit:]`: {+1, code17}, + `[:^xdigit:]`: {-1, code17}, +} diff --git a/src/pkg/exp/regexp/syntax/prog.go b/src/pkg/exp/regexp/syntax/prog.go new file mode 100644 index 000000000..6eeb3da0c --- /dev/null +++ b/src/pkg/exp/regexp/syntax/prog.go @@ -0,0 +1,182 @@ +package syntax + +import ( + "bytes" + "strconv" +) + +// Compiled program. +// May not belong in this package, but convenient for now. + +// A Prog is a compiled regular expression program. +type Prog struct { + Inst []Inst + Start int // index of start instruction +} + +// An InstOp is an instruction opcode. +type InstOp uint8 + +const ( + InstAlt InstOp = iota + InstAltMatch + InstCapture + InstEmptyWidth + InstMatch + InstFail + InstNop + InstRune +) + +// An EmptyOp specifies a kind or mixture of zero-width assertions. +type EmptyOp uint8 + +const ( + EmptyBeginLine EmptyOp = 1 << iota + EmptyEndLine + EmptyBeginText + EmptyEndText + EmptyWordBoundary + EmptyNoWordBoundary +) + +// An Inst is a single instruction in a regular expression program. +type Inst struct { + Op InstOp + Out uint32 // all but InstMatch, InstFail + Arg uint32 // InstAlt, InstAltMatch, InstCapture, InstEmptyWidth + Rune []int +} + +func (p *Prog) String() string { + var b bytes.Buffer + dumpProg(&b, p) + return b.String() +} + +// MatchRune returns true if the instruction matches (and consumes) r. +// It should only be called when i.Op == InstRune. +func (i *Inst) MatchRune(r int) bool { + rune := i.Rune + + // Special case: single-rune slice is from literal string, not char class. + // TODO: Case folding. + if len(rune) == 1 { + return r == rune[0] + } + + // Peek at the first few pairs. + // Should handle ASCII well. + for j := 0; j < len(rune) && j <= 8; j += 2 { + if r < rune[j] { + return false + } + if r <= rune[j+1] { + return true + } + } + + // Otherwise binary search. + lo := 0 + hi := len(rune) / 2 + for lo < hi { + m := lo + (hi-lo)/2 + if c := rune[2*m]; c <= r { + if r <= rune[2*m+1] { + return true + } + lo = m + 1 + } else { + hi = m + } + } + return false +} + +// As per re2's Prog::IsWordChar. Determines whether rune is an ASCII word char. +// Since we act on runes, it would be easy to support Unicode here. +func wordRune(rune int) bool { + return rune == '_' || + ('A' <= rune && rune <= 'Z') || + ('a' <= rune && rune <= 'z') || + ('0' <= rune && rune <= '9') +} + +// MatchEmptyWidth returns true if the instruction matches +// an empty string between the runes before and after. +// It should only be called when i.Op == InstEmptyWidth. +func (i *Inst) MatchEmptyWidth(before int, after int) bool { + switch EmptyOp(i.Arg) { + case EmptyBeginLine: + return before == '\n' || before == -1 + case EmptyEndLine: + return after == '\n' || after == -1 + case EmptyBeginText: + return before == -1 + case EmptyEndText: + return after == -1 + case EmptyWordBoundary: + return wordRune(before) != wordRune(after) + case EmptyNoWordBoundary: + return wordRune(before) == wordRune(after) + } + panic("unknown empty width arg") +} + + +func (i *Inst) String() string { + var b bytes.Buffer + dumpInst(&b, i) + return b.String() +} + +func bw(b *bytes.Buffer, args ...string) { + for _, s := range args { + b.WriteString(s) + } +} + +func dumpProg(b *bytes.Buffer, p *Prog) { + for j := range p.Inst { + i := &p.Inst[j] + pc := strconv.Itoa(j) + if len(pc) < 3 { + b.WriteString(" "[len(pc):]) + } + if j == p.Start { + pc += "*" + } + bw(b, pc, "\t") + dumpInst(b, i) + bw(b, "\n") + } +} + +func u32(i uint32) string { + return strconv.Uitoa64(uint64(i)) +} + +func dumpInst(b *bytes.Buffer, i *Inst) { + switch i.Op { + case InstAlt: + bw(b, "alt -> ", u32(i.Out), ", ", u32(i.Arg)) + case InstAltMatch: + bw(b, "altmatch -> ", u32(i.Out), ", ", u32(i.Arg)) + case InstCapture: + bw(b, "cap ", u32(i.Arg), " -> ", u32(i.Out)) + case InstEmptyWidth: + bw(b, "empty ", u32(i.Arg), " -> ", u32(i.Out)) + case InstMatch: + bw(b, "match") + case InstFail: + bw(b, "fail") + case InstNop: + bw(b, "nop -> ", u32(i.Out)) + case InstRune: + if i.Rune == nil { + // shouldn't happen + bw(b, "rune <nil>") + } + bw(b, "rune ", strconv.QuoteToASCII(string(i.Rune)), " -> ", u32(i.Out)) + } +} diff --git a/src/pkg/exp/regexp/syntax/prog_test.go b/src/pkg/exp/regexp/syntax/prog_test.go new file mode 100644 index 000000000..7be4281c2 --- /dev/null +++ b/src/pkg/exp/regexp/syntax/prog_test.go @@ -0,0 +1,91 @@ +package syntax + +import ( + "testing" +) + +var compileTests = []struct { + Regexp string + Prog string +}{ + {"a", ` 0 fail + 1* rune "a" -> 2 + 2 match +`}, + {"[A-M][n-z]", ` 0 fail + 1* rune "AM" -> 2 + 2 rune "nz" -> 3 + 3 match +`}, + {"", ` 0 fail + 1* nop -> 2 + 2 match +`}, + {"a?", ` 0 fail + 1 rune "a" -> 3 + 2* alt -> 1, 3 + 3 match +`}, + {"a??", ` 0 fail + 1 rune "a" -> 3 + 2* alt -> 3, 1 + 3 match +`}, + {"a+", ` 0 fail + 1* rune "a" -> 2 + 2 alt -> 1, 3 + 3 match +`}, + {"a+?", ` 0 fail + 1* rune "a" -> 2 + 2 alt -> 3, 1 + 3 match +`}, + {"a*", ` 0 fail + 1 rune "a" -> 2 + 2* alt -> 1, 3 + 3 match +`}, + {"a*?", ` 0 fail + 1 rune "a" -> 2 + 2* alt -> 3, 1 + 3 match +`}, + {"a+b+", ` 0 fail + 1* rune "a" -> 2 + 2 alt -> 1, 3 + 3 rune "b" -> 4 + 4 alt -> 3, 5 + 5 match +`}, + {"(a+)(b+)", ` 0 fail + 1* cap 2 -> 2 + 2 rune "a" -> 3 + 3 alt -> 2, 4 + 4 cap 3 -> 5 + 5 cap 4 -> 6 + 6 rune "b" -> 7 + 7 alt -> 6, 8 + 8 cap 5 -> 9 + 9 match +`}, + {"a+|b+", ` 0 fail + 1 rune "a" -> 2 + 2 alt -> 1, 6 + 3 rune "b" -> 4 + 4 alt -> 3, 6 + 5* alt -> 1, 3 + 6 match +`}, +} + +func TestCompile(t *testing.T) { + for _, tt := range compileTests { + re, _ := Parse(tt.Regexp, Perl) + p, _ := Compile(re) + s := p.String() + if s != tt.Prog { + t.Errorf("compiled %#q:\n--- have\n%s---\n--- want\n%s---", tt.Regexp, s, tt.Prog) + } + } +} diff --git a/src/pkg/exp/regexp/syntax/regexp.go b/src/pkg/exp/regexp/syntax/regexp.go new file mode 100644 index 000000000..00a4addef --- /dev/null +++ b/src/pkg/exp/regexp/syntax/regexp.go @@ -0,0 +1,284 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package syntax parses regular expressions into syntax trees. +// WORK IN PROGRESS. +package syntax + +// Note to implementers: +// In this package, re is always a *Regexp and r is always a rune. + +import ( + "bytes" + "strconv" + "strings" + "unicode" +) + +// A Regexp is a node in a regular expression syntax tree. +type Regexp struct { + Op Op // operator + Flags Flags + Sub []*Regexp // subexpressions, if any + Sub0 [1]*Regexp // storage for short Sub + Rune []int // matched runes, for OpLiteral, OpCharClass + Rune0 [2]int // storage for short Rune + Min, Max int // min, max for OpRepeat + Cap int // capturing index, for OpCapture + Name string // capturing name, for OpCapture +} + +// An Op is a single regular expression operator. +type Op uint8 + +// Operators are listed in precedence order, tightest binding to weakest. +// Character class operators are listed simplest to most complex +// (OpLiteral, OpCharClass, OpAnyCharNotNL, OpAnyChar). + +const ( + OpNoMatch Op = 1 + iota // matches no strings + OpEmptyMatch // matches empty string + OpLiteral // matches Runes sequence + OpCharClass // matches Runes interpreted as range pair list + OpAnyCharNotNL // matches any character + OpAnyChar // matches any character + OpBeginLine // matches empty string at beginning of line + OpEndLine // matches empty string at end of line + OpBeginText // matches empty string at beginning of text + OpEndText // matches empty string at end of text + OpWordBoundary // matches word boundary `\b` + OpNoWordBoundary // matches word non-boundary `\B` + OpCapture // capturing subexpression with index Cap, optional name Name + OpStar // matches Sub[0] zero or more times + OpPlus // matches Sub[0] one or more times + OpQuest // matches Sub[0] zero or one times + OpRepeat // matches Sub[0] at least Min times, at most Max (Max == -1 is no limit) + OpConcat // matches concatenation of Subs + OpAlternate // matches alternation of Subs +) + +const opPseudo Op = 128 // where pseudo-ops start + +// Equal returns true if x and y have identical structure. +func (x *Regexp) Equal(y *Regexp) bool { + if x == nil || y == nil { + return x == y + } + if x.Op != y.Op { + return false + } + switch x.Op { + case OpEndText: + // The parse flags remember whether this is \z or \Z. + if x.Flags&WasDollar != y.Flags&WasDollar { + return false + } + + case OpLiteral, OpCharClass: + if len(x.Rune) != len(y.Rune) { + return false + } + for i, r := range x.Rune { + if r != y.Rune[i] { + return false + } + } + + case OpAlternate, OpConcat: + if len(x.Sub) != len(y.Sub) { + return false + } + for i, sub := range x.Sub { + if !sub.Equal(y.Sub[i]) { + return false + } + } + + case OpStar, OpPlus, OpQuest: + if x.Flags&NonGreedy != y.Flags&NonGreedy || !x.Sub[0].Equal(y.Sub[0]) { + return false + } + + case OpRepeat: + if x.Flags&NonGreedy != y.Flags&NonGreedy || x.Min != y.Min || x.Max != y.Max || !x.Sub[0].Equal(y.Sub[0]) { + return false + } + + case OpCapture: + if x.Cap != y.Cap || x.Name != y.Name || !x.Sub[0].Equal(y.Sub[0]) { + return false + } + } + return true +} + +// writeRegexp writes the Perl syntax for the regular expression re to b. +func writeRegexp(b *bytes.Buffer, re *Regexp) { + switch re.Op { + default: + b.WriteString("<invalid op" + strconv.Itoa(int(re.Op)) + ">") + case OpNoMatch: + b.WriteString(`[^\x00-\x{10FFFF}]`) + case OpEmptyMatch: + b.WriteString(`(?:)`) + case OpLiteral: + if re.Flags&FoldCase != 0 { + b.WriteString(`(?i:`) + } + for _, r := range re.Rune { + escape(b, r, false) + } + if re.Flags&FoldCase != 0 { + b.WriteString(`)`) + } + case OpCharClass: + if len(re.Rune)%2 != 0 { + b.WriteString(`[invalid char class]`) + break + } + b.WriteRune('[') + if len(re.Rune) == 0 { + b.WriteString(`^\x00-\x{10FFFF}`) + } else if re.Rune[0] == 0 && re.Rune[len(re.Rune)-1] == unicode.MaxRune { + // Contains 0 and MaxRune. Probably a negated class. + // Print the gaps. + b.WriteRune('^') + for i := 1; i < len(re.Rune)-1; i += 2 { + lo, hi := re.Rune[i]+1, re.Rune[i+1]-1 + escape(b, lo, lo == '-') + if lo != hi { + b.WriteRune('-') + escape(b, hi, hi == '-') + } + } + } else { + for i := 0; i < len(re.Rune); i += 2 { + lo, hi := re.Rune[i], re.Rune[i+1] + escape(b, lo, lo == '-') + if lo != hi { + b.WriteRune('-') + escape(b, hi, hi == '-') + } + } + } + b.WriteRune(']') + case OpAnyCharNotNL: + b.WriteString(`[^\n]`) + case OpAnyChar: + b.WriteRune('.') + case OpBeginLine: + b.WriteRune('^') + case OpEndLine: + b.WriteRune('$') + case OpBeginText: + b.WriteString(`\A`) + case OpEndText: + b.WriteString(`\z`) + case OpWordBoundary: + b.WriteString(`\b`) + case OpNoWordBoundary: + b.WriteString(`\B`) + case OpCapture: + if re.Name != "" { + b.WriteString(`(?P<`) + b.WriteString(re.Name) + b.WriteRune('>') + } else { + b.WriteRune('(') + } + if re.Sub[0].Op != OpEmptyMatch { + writeRegexp(b, re.Sub[0]) + } + b.WriteRune(')') + case OpStar, OpPlus, OpQuest, OpRepeat: + if sub := re.Sub[0]; sub.Op > OpCapture { + b.WriteString(`(?:`) + writeRegexp(b, sub) + b.WriteString(`)`) + } else { + writeRegexp(b, sub) + } + switch re.Op { + case OpStar: + b.WriteRune('*') + case OpPlus: + b.WriteRune('+') + case OpQuest: + b.WriteRune('?') + case OpRepeat: + b.WriteRune('{') + b.WriteString(strconv.Itoa(re.Min)) + if re.Max != re.Min { + b.WriteRune(',') + if re.Max >= 0 { + b.WriteString(strconv.Itoa(re.Max)) + } + } + b.WriteRune('}') + } + case OpConcat: + for _, sub := range re.Sub { + if sub.Op == OpAlternate { + b.WriteString(`(?:`) + writeRegexp(b, sub) + b.WriteString(`)`) + } else { + writeRegexp(b, sub) + } + } + case OpAlternate: + for i, sub := range re.Sub { + if i > 0 { + b.WriteRune('|') + } + writeRegexp(b, sub) + } + } +} + +func (re *Regexp) String() string { + var b bytes.Buffer + writeRegexp(&b, re) + return b.String() +} + +const meta = `\.+*?()|[]{}^$` + +func escape(b *bytes.Buffer, r int, force bool) { + if unicode.IsPrint(r) { + if strings.IndexRune(meta, r) >= 0 || force { + b.WriteRune('\\') + } + b.WriteRune(r) + return + } + + switch r { + case '\a': + b.WriteString(`\a`) + case '\f': + b.WriteString(`\f`) + case '\n': + b.WriteString(`\n`) + case '\r': + b.WriteString(`\r`) + case '\t': + b.WriteString(`\t`) + case '\v': + b.WriteString(`\v`) + default: + if r < 0x100 { + b.WriteString(`\x`) + s := strconv.Itob(r, 16) + if len(s) == 1 { + b.WriteRune('0') + } + b.WriteString(s) + break + } + b.WriteString(`\x{`) + b.WriteString(strconv.Itob(r, 16)) + b.WriteString(`}`) + } +} diff --git a/src/pkg/exp/regexp/syntax/simplify.go b/src/pkg/exp/regexp/syntax/simplify.go new file mode 100644 index 000000000..72390417b --- /dev/null +++ b/src/pkg/exp/regexp/syntax/simplify.go @@ -0,0 +1,151 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package syntax + +// Simplify returns a regexp equivalent to re but without counted repetitions +// and with various other simplifications, such as rewriting /(?:a+)+/ to /a+/. +// The resulting regexp will execute correctly but its string representation +// will not produce the same parse tree, because capturing parentheses +// may have been duplicated or removed. For example, the simplified form +// for /(x){1,2}/ is /(x)(x)?/ but both parentheses capture as $1. +// The returned regexp may share structure with or be the original. +func (re *Regexp) Simplify() *Regexp { + if re == nil { + return nil + } + switch re.Op { + case OpCapture, OpConcat, OpAlternate: + // Simplify children, building new Regexp if children change. + nre := re + for i, sub := range re.Sub { + nsub := sub.Simplify() + if nre == re && nsub != sub { + // Start a copy. + nre = new(Regexp) + *nre = *re + nre.Rune = nil + nre.Sub = append(nre.Sub0[:0], re.Sub[:i]...) + } + if nre != re { + nre.Sub = append(nre.Sub, nsub) + } + } + return nre + + case OpStar, OpPlus, OpQuest: + sub := re.Sub[0].Simplify() + return simplify1(re.Op, re.Flags, sub, re) + + case OpRepeat: + // Special special case: x{0} matches the empty string + // and doesn't even need to consider x. + if re.Min == 0 && re.Max == 0 { + return &Regexp{Op: OpEmptyMatch} + } + + // The fun begins. + sub := re.Sub[0].Simplify() + + // x{n,} means at least n matches of x. + if re.Max == -1 { + // Special case: x{0,} is x*. + if re.Min == 0 { + return simplify1(OpStar, re.Flags, sub, nil) + } + + // Special case: x{1,} is x+. + if re.Min == 1 { + return simplify1(OpPlus, re.Flags, sub, nil) + } + + // General case: x{4,} is xxxx+. + nre := &Regexp{Op: OpConcat} + nre.Sub = nre.Sub0[:0] + for i := 0; i < re.Min-1; i++ { + nre.Sub = append(nre.Sub, sub) + } + nre.Sub = append(nre.Sub, simplify1(OpPlus, re.Flags, sub, nil)) + return nre + } + + // Special case x{0} handled above. + + // Special case: x{1} is just x. + if re.Min == 1 && re.Max == 1 { + return sub + } + + // General case: x{n,m} means n copies of x and m copies of x? + // The machine will do less work if we nest the final m copies, + // so that x{2,5} = xx(x(x(x)?)?)? + + // Build leading prefix: xx. + var prefix *Regexp + if re.Min > 0 { + prefix = &Regexp{Op: OpConcat} + prefix.Sub = prefix.Sub0[:0] + for i := 0; i < re.Min; i++ { + prefix.Sub = append(prefix.Sub, sub) + } + } + + // Build and attach suffix: (x(x(x)?)?)? + if re.Max > re.Min { + suffix := simplify1(OpQuest, re.Flags, sub, nil) + for i := re.Min + 1; i < re.Max; i++ { + nre2 := &Regexp{Op: OpConcat} + nre2.Sub = append(nre2.Sub0[:0], sub, suffix) + suffix = simplify1(OpQuest, re.Flags, nre2, nil) + } + if prefix == nil { + return suffix + } + prefix.Sub = append(prefix.Sub, suffix) + } + if prefix != nil { + return prefix + } + + // Some degenerate case like min > max or min < max < 0. + // Handle as impossible match. + return &Regexp{Op: OpNoMatch} + } + + return re +} + +// simplify1 implements Simplify for the unary OpStar, +// OpPlus, and OpQuest operators. It returns the simple regexp +// equivalent to +// +// Regexp{Op: op, Flags: flags, Sub: {sub}} +// +// under the assumption that sub is already simple, and +// without first allocating that structure. If the regexp +// to be returned turns out to be equivalent to re, simplify1 +// returns re instead. +// +// simplify1 is factored out of Simplify because the implementation +// for other operators generates these unary expressions. +// Letting them call simplify1 makes sure the expressions they +// generate are simple. +func simplify1(op Op, flags Flags, sub, re *Regexp) *Regexp { + // Special case: repeat the empty string as much as + // you want, but it's still the empty string. + if sub.Op == OpEmptyMatch { + return sub + } + // The operators are idempotent if the flags match. + if op == sub.Op && flags&NonGreedy == sub.Flags&NonGreedy { + return sub + } + if re != nil && re.Op == op && re.Flags&NonGreedy == flags&NonGreedy && sub == re.Sub[0] { + return re + } + + re = &Regexp{Op: op, Flags: flags} + re.Sub = append(re.Sub0[:0], sub) + return re +} diff --git a/src/pkg/exp/regexp/syntax/simplify_test.go b/src/pkg/exp/regexp/syntax/simplify_test.go new file mode 100644 index 000000000..c8cec2183 --- /dev/null +++ b/src/pkg/exp/regexp/syntax/simplify_test.go @@ -0,0 +1,151 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package syntax + +import "testing" + +var simplifyTests = []struct { + Regexp string + Simple string +}{ + // Already-simple constructs + {`a`, `a`}, + {`ab`, `ab`}, + {`a|b`, `[a-b]`}, + {`ab|cd`, `ab|cd`}, + {`(ab)*`, `(ab)*`}, + {`(ab)+`, `(ab)+`}, + {`(ab)?`, `(ab)?`}, + {`.`, `.`}, + {`^`, `^`}, + {`$`, `$`}, + {`[ac]`, `[ac]`}, + {`[^ac]`, `[^ac]`}, + + // Posix character classes + {`[[:alnum:]]`, `[0-9A-Za-z]`}, + {`[[:alpha:]]`, `[A-Za-z]`}, + {`[[:blank:]]`, `[\t ]`}, + {`[[:cntrl:]]`, `[\x00-\x1f\x7f]`}, + {`[[:digit:]]`, `[0-9]`}, + {`[[:graph:]]`, `[!-~]`}, + {`[[:lower:]]`, `[a-z]`}, + {`[[:print:]]`, `[ -~]`}, + {`[[:punct:]]`, "[!-/:-@\\[-`\\{-~]"}, + {`[[:space:]]`, `[\t-\r ]`}, + {`[[:upper:]]`, `[A-Z]`}, + {`[[:xdigit:]]`, `[0-9A-Fa-f]`}, + + // Perl character classes + {`\d`, `[0-9]`}, + {`\s`, `[\t-\n\f-\r ]`}, + {`\w`, `[0-9A-Z_a-z]`}, + {`\D`, `[^0-9]`}, + {`\S`, `[^\t-\n\f-\r ]`}, + {`\W`, `[^0-9A-Z_a-z]`}, + {`[\d]`, `[0-9]`}, + {`[\s]`, `[\t-\n\f-\r ]`}, + {`[\w]`, `[0-9A-Z_a-z]`}, + {`[\D]`, `[^0-9]`}, + {`[\S]`, `[^\t-\n\f-\r ]`}, + {`[\W]`, `[^0-9A-Z_a-z]`}, + + // Posix repetitions + {`a{1}`, `a`}, + {`a{2}`, `aa`}, + {`a{5}`, `aaaaa`}, + {`a{0,1}`, `a?`}, + // The next three are illegible because Simplify inserts (?:) + // parens instead of () parens to avoid creating extra + // captured subexpressions. The comments show a version with fewer parens. + {`(a){0,2}`, `(?:(a)(a)?)?`}, // (aa?)? + {`(a){0,4}`, `(?:(a)(?:(a)(?:(a)(a)?)?)?)?`}, // (a(a(aa?)?)?)? + {`(a){2,6}`, `(a)(a)(?:(a)(?:(a)(?:(a)(a)?)?)?)?`}, // aa(a(a(aa?)?)?)? + {`a{0,2}`, `(?:aa?)?`}, // (aa?)? + {`a{0,4}`, `(?:a(?:a(?:aa?)?)?)?`}, // (a(a(aa?)?)?)? + {`a{2,6}`, `aa(?:a(?:a(?:aa?)?)?)?`}, // aa(a(a(aa?)?)?)? + {`a{0,}`, `a*`}, + {`a{1,}`, `a+`}, + {`a{2,}`, `aa+`}, + {`a{5,}`, `aaaaa+`}, + + // Test that operators simplify their arguments. + {`(?:a{1,}){1,}`, `a+`}, + {`(a{1,}b{1,})`, `(a+b+)`}, + {`a{1,}|b{1,}`, `a+|b+`}, + {`(?:a{1,})*`, `(?:a+)*`}, + {`(?:a{1,})+`, `a+`}, + {`(?:a{1,})?`, `(?:a+)?`}, + {``, `(?:)`}, + {`a{0}`, `(?:)`}, + + // Character class simplification + {`[ab]`, `[a-b]`}, + {`[a-za-za-z]`, `[a-z]`}, + {`[A-Za-zA-Za-z]`, `[A-Za-z]`}, + {`[ABCDEFGH]`, `[A-H]`}, + {`[AB-CD-EF-GH]`, `[A-H]`}, + {`[W-ZP-XE-R]`, `[E-Z]`}, + {`[a-ee-gg-m]`, `[a-m]`}, + {`[a-ea-ha-m]`, `[a-m]`}, + {`[a-ma-ha-e]`, `[a-m]`}, + {`[a-zA-Z0-9 -~]`, `[ -~]`}, + + // Empty character classes + {`[^[:cntrl:][:^cntrl:]]`, `[^\x00-\x{10FFFF}]`}, + + // Full character classes + {`[[:cntrl:][:^cntrl:]]`, `.`}, + + // Unicode case folding. + {`(?i)A`, `(?i:A)`}, + {`(?i)a`, `(?i:a)`}, + {`(?i)[A]`, `(?i:A)`}, + {`(?i)[a]`, `(?i:A)`}, + {`(?i)K`, `(?i:K)`}, + {`(?i)k`, `(?i:k)`}, + {`(?i)\x{212a}`, "(?i:\u212A)"}, + {`(?i)[K]`, "[Kk\u212A]"}, + {`(?i)[k]`, "[Kk\u212A]"}, + {`(?i)[\x{212a}]`, "[Kk\u212A]"}, + {`(?i)[a-z]`, "[A-Za-z\u017F\u212A]"}, + {`(?i)[\x00-\x{FFFD}]`, "[\\x00-\uFFFD]"}, + {`(?i)[\x00-\x{10FFFF}]`, `.`}, + + // Empty string as a regular expression. + // The empty string must be preserved inside parens in order + // to make submatches work right, so these tests are less + // interesting than they might otherwise be. String inserts + // explicit (?:) in place of non-parenthesized empty strings, + // to make them easier to spot for other parsers. + {`(a|b|)`, `([a-b]|(?:))`}, + {`(|)`, `()`}, + {`a()`, `a()`}, + {`(()|())`, `(()|())`}, + {`(a|)`, `(a|(?:))`}, + {`ab()cd()`, `ab()cd()`}, + {`()`, `()`}, + {`()*`, `()*`}, + {`()+`, `()+`}, + {`()?`, `()?`}, + {`(){0}`, `(?:)`}, + {`(){1}`, `()`}, + {`(){1,}`, `()+`}, + {`(){0,2}`, `(?:()()?)?`}, +} + +func TestSimplify(t *testing.T) { + for _, tt := range simplifyTests { + re, err := Parse(tt.Regexp, MatchNL|Perl&^OneLine) + if err != nil { + t.Errorf("Parse(%#q) = error %v", tt.Regexp, err) + continue + } + s := re.Simplify().String() + if s != tt.Simple { + t.Errorf("Simplify(%#q) = %#q, want %#q", tt.Regexp, s, tt.Simple) + } + } +} diff --git a/src/pkg/exp/template/Makefile b/src/pkg/exp/template/Makefile new file mode 100644 index 000000000..8550b0d52 --- /dev/null +++ b/src/pkg/exp/template/Makefile @@ -0,0 +1,15 @@ +# Copyright 2011 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../../Make.inc + +TARG=exp/template +GOFILES=\ + exec.go\ + funcs.go\ + lex.go\ + parse.go\ + set.go\ + +include ../../../Make.pkg diff --git a/src/pkg/exp/template/exec.go b/src/pkg/exp/template/exec.go new file mode 100644 index 000000000..fb0a9e621 --- /dev/null +++ b/src/pkg/exp/template/exec.go @@ -0,0 +1,508 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package template + +import ( + "fmt" + "io" + "os" + "reflect" + "strings" + "unicode" + "utf8" +) + +// state represents the state of an execution. It's not part of the +// template so that multiple executions of the same template +// can execute in parallel. +type state struct { + tmpl *Template + wr io.Writer + set *Set + line int // line number for errors +} + +// errorf formats the error and terminates processing. +func (s *state) errorf(format string, args ...interface{}) { + format = fmt.Sprintf("template: %s:%d: %s", s.tmpl.name, s.line, format) + panic(fmt.Errorf(format, args...)) +} + +// error terminates processing. +func (s *state) error(err os.Error) { + s.errorf("%s", err) +} + +// Execute applies a parsed template to the specified data object, +// writing the output to wr. +func (t *Template) Execute(wr io.Writer, data interface{}) os.Error { + return t.ExecuteInSet(wr, data, nil) +} + +// ExecuteInSet applies a parsed template to the specified data object, +// writing the output to wr. Nested template invocations will be resolved +// from the specified set. +func (t *Template) ExecuteInSet(wr io.Writer, data interface{}, set *Set) (err os.Error) { + defer t.recover(&err) + state := &state{ + tmpl: t, + wr: wr, + set: set, + line: 1, + } + if t.root == nil { + state.errorf("must be parsed before execution") + } + state.walk(reflect.ValueOf(data), t.root) + return +} + +// Walk functions step through the major pieces of the template structure, +// generating output as they go. +func (s *state) walk(data reflect.Value, n node) { + switch n := n.(type) { + case *actionNode: + s.line = n.line + s.printValue(n, s.evalPipeline(data, n.pipeline)) + case *listNode: + for _, node := range n.nodes { + s.walk(data, node) + } + case *ifNode: + s.line = n.line + s.walkIfOrWith(nodeIf, data, n.pipeline, n.list, n.elseList) + case *rangeNode: + s.line = n.line + s.walkRange(data, n) + case *textNode: + if _, err := s.wr.Write(n.text); err != nil { + s.error(err) + } + case *templateNode: + s.line = n.line + s.walkTemplate(data, n) + case *withNode: + s.line = n.line + s.walkIfOrWith(nodeWith, data, n.pipeline, n.list, n.elseList) + default: + s.errorf("unknown node: %s", n) + } +} + +// walkIfOrWith walks an 'if' or 'with' node. The two control structures +// are identical in behavior except that 'with' sets dot. +func (s *state) walkIfOrWith(typ nodeType, data reflect.Value, pipe []*commandNode, list, elseList *listNode) { + val := s.evalPipeline(data, pipe) + truth, ok := isTrue(val) + if !ok { + s.errorf("if/with can't use value of type %T", val.Interface()) + } + if truth { + if typ == nodeWith { + data = val + } + s.walk(data, list) + } else if elseList != nil { + s.walk(data, elseList) + } +} + +// isTrue returns whether the value is 'true', in the sense of not the zero of its type, +// and whether the value has a meaningful truth value. +func isTrue(val reflect.Value) (truth, ok bool) { + switch val.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + truth = val.Len() > 0 + case reflect.Bool: + truth = val.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + truth = val.Int() != 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + truth = val.Uint() != 0 + case reflect.Float32, reflect.Float64: + truth = val.Float() != 0 + case reflect.Complex64, reflect.Complex128: + truth = val.Complex() != 0 + case reflect.Chan, reflect.Func, reflect.Ptr: + truth = !val.IsNil() + default: + return + } + return truth, true +} + +func (s *state) walkRange(data reflect.Value, r *rangeNode) { + val := s.evalPipeline(data, r.pipeline) + switch val.Kind() { + case reflect.Array, reflect.Slice: + if val.Len() == 0 { + break + } + for i := 0; i < val.Len(); i++ { + s.walk(val.Index(i), r.list) + } + return + case reflect.Map: + if val.Len() == 0 { + break + } + for _, key := range val.MapKeys() { + s.walk(val.MapIndex(key), r.list) + } + return + default: + s.errorf("range can't iterate over value of type %T", val.Interface()) + } + if r.elseList != nil { + s.walk(data, r.elseList) + } +} + +func (s *state) walkTemplate(data reflect.Value, t *templateNode) { + name := s.evalArg(data, reflect.TypeOf("string"), t.name).String() + if s.set == nil { + s.errorf("no set defined in which to invoke template named %q", name) + } + tmpl := s.set.tmpl[name] + if tmpl == nil { + s.errorf("template %q not in set", name) + } + data = s.evalPipeline(data, t.pipeline) + newState := *s + newState.tmpl = tmpl + newState.walk(data, tmpl.root) +} + +// Eval functions evaluate pipelines, commands, and their elements and extract +// values from the data structure by examining fields, calling methods, and so on. +// The printing of those values happens only through walk functions. + +func (s *state) evalPipeline(data reflect.Value, pipe []*commandNode) reflect.Value { + value := reflect.Value{} + for _, cmd := range pipe { + value = s.evalCommand(data, cmd, value) // previous value is this one's final arg. + // If the object has type interface{}, dig down one level to the thing inside. + if value.Kind() == reflect.Interface && value.Type().NumMethod() == 0 { + value = reflect.ValueOf(value.Interface()) // lovely! + } + } + return value +} + +func (s *state) evalCommand(data reflect.Value, cmd *commandNode, final reflect.Value) reflect.Value { + firstWord := cmd.args[0] + switch n := firstWord.(type) { + case *fieldNode: + return s.evalFieldNode(data, n, cmd.args, final) + case *identifierNode: + return s.evalFieldOrCall(data, n.ident, cmd.args, final) + } + if len(cmd.args) > 1 || final.IsValid() { + s.errorf("can't give argument to non-function %s", cmd.args[0]) + } + switch word := cmd.args[0].(type) { + case *dotNode: + return data + case *boolNode: + return reflect.ValueOf(word.true) + case *numberNode: + // These are ideal constants but we don't know the type + // and we have no context. (If it was a method argument, + // we'd know what we need.) The syntax guides us to some extent. + switch { + case word.isComplex: + return reflect.ValueOf(word.complex128) // incontrovertible. + case word.isFloat && strings.IndexAny(word.text, ".eE") >= 0: + return reflect.ValueOf(word.float64) + case word.isInt: + return reflect.ValueOf(word.int64) + case word.isUint: + return reflect.ValueOf(word.uint64) + } + case *stringNode: + return reflect.ValueOf(word.text) + } + s.errorf("can't handle command %q", firstWord) + panic("not reached") +} + +func (s *state) evalFieldNode(data reflect.Value, field *fieldNode, args []node, final reflect.Value) reflect.Value { + // Up to the last entry, it must be a field. + n := len(field.ident) + for i := 0; i < n-1; i++ { + data = s.evalField(data, field.ident[i]) + } + // Now it can be a field or method and if a method, gets arguments. + return s.evalFieldOrCall(data, field.ident[n-1], args, final) +} + +// Is this an exported - upper case - name? +func isExported(name string) bool { + rune, _ := utf8.DecodeRuneInString(name) + return unicode.IsUpper(rune) +} + +func (s *state) evalField(data reflect.Value, fieldName string) reflect.Value { + var isNil bool + if data, isNil = indirect(data); isNil { + s.errorf("%s is nil pointer", fieldName) + } + switch data.Kind() { + case reflect.Struct: + // Is it a field? + field := data.FieldByName(fieldName) + // TODO: look higher up the tree if we can't find it here. Also unexported fields + // might succeed higher up, as map keys. + if field.IsValid() && isExported(fieldName) { // valid and exported + return field + } + s.errorf("%s has no exported field %q", data.Type(), fieldName) + default: + s.errorf("can't evaluate field %s of type %s", fieldName, data.Type()) + } + panic("not reached") +} + +func (s *state) evalFieldOrCall(data reflect.Value, fieldName string, args []node, final reflect.Value) reflect.Value { + // Is it a function? + if function, ok := findFunction(fieldName, s.tmpl, s.set); ok { + return s.evalCall(data, function, fieldName, false, args, final) + } + ptr := data + for data.Kind() == reflect.Ptr && !data.IsNil() { + ptr, data = data, reflect.Indirect(data) + } + // Is it a method? We use the pointer because it has value methods too. + if method, ok := methodByName(ptr.Type(), fieldName); ok { + return s.evalCall(ptr, method.Func, fieldName, true, args, final) + } + if len(args) > 1 || final.IsValid() { + s.errorf("%s is not a method but has arguments", fieldName) + } + switch data.Kind() { + case reflect.Struct: + return s.evalField(data, fieldName) + default: + s.errorf("can't handle evaluation of field %s of type %s", fieldName, data.Type()) + } + panic("not reached") +} + +// TODO: delete when reflect's own MethodByName is released. +func methodByName(typ reflect.Type, name string) (reflect.Method, bool) { + for i := 0; i < typ.NumMethod(); i++ { + if typ.Method(i).Name == name { + return typ.Method(i), true + } + } + return reflect.Method{}, false +} + +var ( + osErrorType = reflect.TypeOf(new(os.Error)).Elem() +) + +func (s *state) evalCall(v, fun reflect.Value, name string, isMethod bool, args []node, final reflect.Value) reflect.Value { + typ := fun.Type() + if !isMethod && len(args) > 0 { // Args will be nil if it's a niladic call in an argument list + args = args[1:] // first arg is name of function; not used in call. + } + numIn := len(args) + if final.IsValid() { + numIn++ + } + numFixed := len(args) + if typ.IsVariadic() { + numFixed = typ.NumIn() - 1 // last arg is the variadic one. + if numIn < numFixed { + s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args)) + } + } else if numIn < typ.NumIn()-1 || !typ.IsVariadic() && numIn != typ.NumIn() { + s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), len(args)) + } + if !goodFunc(typ) { + s.errorf("can't handle multiple results from method/function %q", name) + } + // Build the arg list. + argv := make([]reflect.Value, numIn) + // First arg is the receiver. + i := 0 + if isMethod { + argv[0] = v + i++ + } + // Others must be evaluated. Fixed args first. + for ; i < numFixed; i++ { + argv[i] = s.evalArg(v, typ.In(i), args[i]) + } + // And now the ... args. + if typ.IsVariadic() { + argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice. + for ; i < len(args); i++ { + argv[i] = s.evalArg(v, argType, args[i]) + } + } + // Add final value if necessary. + if final.IsValid() { + argv[len(args)] = final + } + result := fun.Call(argv) + // If we have an os.Error that is not nil, stop execution and return that error to the caller. + if len(result) == 2 && !result[1].IsNil() { + s.error(result[1].Interface().(os.Error)) + } + return result[0] +} + +func (s *state) evalArg(data reflect.Value, typ reflect.Type, n node) reflect.Value { + if field, ok := n.(*fieldNode); ok { + value := s.evalFieldNode(data, field, []node{n}, reflect.Value{}) + if !value.Type().AssignableTo(typ) { + s.errorf("wrong type for value; expected %s; got %s", typ, value.Type()) + } + return value + } + switch typ.Kind() { + case reflect.Bool: + return s.evalBool(typ, n) + case reflect.String: + return s.evalString(typ, n) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return s.evalInteger(typ, n) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return s.evalUnsignedInteger(typ, n) + case reflect.Float32, reflect.Float64: + return s.evalFloat(typ, n) + case reflect.Complex64, reflect.Complex128: + return s.evalComplex(typ, n) + case reflect.Interface: + if typ.NumMethod() == 0 { + return s.evalEmptyInterface(data, typ, n) + } + } + s.errorf("can't handle %s for arg of type %s", n, typ) + panic("not reached") +} + +func (s *state) evalBool(typ reflect.Type, n node) reflect.Value { + if n, ok := n.(*boolNode); ok { + value := reflect.New(typ).Elem() + value.SetBool(n.true) + return value + } + s.errorf("expected bool; found %s", n) + panic("not reached") +} + +func (s *state) evalString(typ reflect.Type, n node) reflect.Value { + if n, ok := n.(*stringNode); ok { + value := reflect.New(typ).Elem() + value.SetString(n.text) + return value + } + s.errorf("expected string; found %s", n) + panic("not reached") +} + +func (s *state) evalInteger(typ reflect.Type, n node) reflect.Value { + if n, ok := n.(*numberNode); ok && n.isInt { + value := reflect.New(typ).Elem() + value.SetInt(n.int64) + return value + } + s.errorf("expected integer; found %s", n) + panic("not reached") +} + +func (s *state) evalUnsignedInteger(typ reflect.Type, n node) reflect.Value { + if n, ok := n.(*numberNode); ok && n.isUint { + value := reflect.New(typ).Elem() + value.SetUint(n.uint64) + return value + } + s.errorf("expected unsigned integer; found %s", n) + panic("not reached") +} + +func (s *state) evalFloat(typ reflect.Type, n node) reflect.Value { + if n, ok := n.(*numberNode); ok && n.isFloat { + value := reflect.New(typ).Elem() + value.SetFloat(n.float64) + return value + } + s.errorf("expected float; found %s", n) + panic("not reached") +} + +func (s *state) evalComplex(typ reflect.Type, n node) reflect.Value { + if n, ok := n.(*numberNode); ok && n.isComplex { + value := reflect.New(typ).Elem() + value.SetComplex(n.complex128) + return value + } + s.errorf("expected complex; found %s", n) + panic("not reached") +} + +func (s *state) evalEmptyInterface(data reflect.Value, typ reflect.Type, n node) reflect.Value { + switch n := n.(type) { + case *boolNode: + return reflect.ValueOf(n.true) + case *dotNode: + return data + case *fieldNode: + return s.evalFieldNode(data, n, nil, reflect.Value{}) + case *identifierNode: + return s.evalFieldOrCall(data, n.ident, nil, reflect.Value{}) + case *numberNode: + if n.isComplex { + return reflect.ValueOf(n.complex128) + } + if n.isInt { + return reflect.ValueOf(n.int64) + } + if n.isUint { + return reflect.ValueOf(n.uint64) + } + if n.isFloat { + return reflect.ValueOf(n.float64) + } + case *stringNode: + return reflect.ValueOf(n.text) + } + s.errorf("can't handle assignment of %s to empty interface argument", n) + panic("not reached") +} + +// indirect returns the item at the end of indirection, and a bool to indicate if it's nil. +func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { + for v.Kind() == reflect.Ptr { + if v.IsNil() { + return v, true + } + v = v.Elem() + } + return v, false +} + +// printValue writes the textual representation of the value to the output of +// the template. +func (s *state) printValue(n node, v reflect.Value) { + if !v.IsValid() { + fmt.Fprint(s.wr, "<no value>") + return + } + switch v.Kind() { + case reflect.Ptr: + var isNil bool + if v, isNil = indirect(v); isNil { + fmt.Fprint(s.wr, "<nil>") + return + } + case reflect.Chan, reflect.Func, reflect.Interface: + s.errorf("can't print %s of type %s", n, v.Type()) + } + fmt.Fprint(s.wr, v.Interface()) +} diff --git a/src/pkg/exp/template/exec_test.go b/src/pkg/exp/template/exec_test.go new file mode 100644 index 000000000..86b958e84 --- /dev/null +++ b/src/pkg/exp/template/exec_test.go @@ -0,0 +1,342 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package template + +import ( + "bytes" + "fmt" + "os" + "sort" + "strings" + "testing" +) + +// T has lots of interesting pieces to use to test execution. +type T struct { + // Basics + I int + U16 uint16 + X string + FloatZero float64 + ComplexZero float64 + // Nested structs. + U *U + // Slices + SI []int + SIEmpty []int + SB []bool + // Maps + MSI map[string]int + MSIone map[string]int // one element, for deterministic output + MSIEmpty map[string]int + SMSI []map[string]int + // Empty interfaces; used to see if we can dig inside one. + Empty0 interface{} // nil + Empty1 interface{} + Empty2 interface{} + Empty3 interface{} + Empty4 interface{} + // Pointers + PI *int + PSI *[]int + NIL *int +} + +var tVal = &T{ + I: 17, + U16: 16, + X: "x", + U: &U{"v"}, + SI: []int{3, 4, 5}, + SB: []bool{true, false}, + MSI: map[string]int{"one": 1, "two": 2, "three": 3}, + MSIone: map[string]int{"one": 1}, + SMSI: []map[string]int{ + {"one": 1, "two": 2}, + {"eleven": 11, "twelve": 12}, + }, + Empty1: 3, + Empty2: "empty2", + Empty3: []int{7, 8}, + Empty4: &U{"v"}, + PI: newInt(23), + PSI: newIntSlice(21, 22, 23), +} + +// Helpers for creation. +func newInt(n int) *int { + p := new(int) + *p = n + return p +} + +func newIntSlice(n ...int) *[]int { + p := new([]int) + *p = make([]int, len(n)) + copy(*p, n) + return p +} + +// Simple methods with and without arguments. +func (t *T) Method0() string { + return "resultOfMethod0" +} + +func (t *T) Method1(a int) int { + return a +} + +func (t *T) Method2(a uint16, b string) string { + return fmt.Sprintf("Method2: %d %s", a, b) +} + +func (t *T) MAdd(a int, b []int) []int { + v := make([]int, len(b)) + for i, x := range b { + v[i] = x + a + } + return v +} + +// MSort is used to sort map keys for stable output. (Nice trick!) +func (t *T) MSort(m map[string]int) []string { + keys := make([]string, len(m)) + i := 0 + for k := range m { + keys[i] = k + i++ + } + sort.Strings(keys) + return keys +} + +// EPERM returns a value and an os.Error according to its argument. +func (t *T) EPERM(error bool) (bool, os.Error) { + if error { + return true, os.EPERM + } + return false, nil +} + +type U struct { + V string +} + +type execTest struct { + name string + input string + output string + data interface{} + ok bool +} + +var execTests = []execTest{ + // Trivial cases. + {"empty", "", "", nil, true}, + {"text", "some text", "some text", nil, true}, + + // Fields of structs. + {".X", "-{{.X}}-", "-x-", tVal, true}, + {".U.V", "-{{.U.V}}-", "-v-", tVal, true}, + + // Dots of all kinds to test basic evaluation. + {"dot int", "<{{.}}>", "<13>", 13, true}, + {"dot uint", "<{{.}}>", "<14>", uint(14), true}, + {"dot float", "<{{.}}>", "<15.1>", 15.1, true}, + {"dot bool", "<{{.}}>", "<true>", true, true}, + {"dot complex", "<{{.}}>", "<(16.2-17i)>", 16.2 - 17i, true}, + {"dot string", "<{{.}}>", "<hello>", "hello", true}, + {"dot slice", "<{{.}}>", "<[-1 -2 -3]>", []int{-1, -2, -3}, true}, + {"dot map", "<{{.}}>", "<map[two:22 one:11]>", map[string]int{"one": 11, "two": 22}, true}, + {"dot struct", "<{{.}}>", "<{7 seven}>", struct { + a int + b string + }{7, "seven"}, true}, + + // Pointers. + {"*int", "{{.PI}}", "23", tVal, true}, + {"*[]int", "{{.PSI}}", "[21 22 23]", tVal, true}, + {"*[]int[1]", "{{index .PSI 1}}", "22", tVal, true}, + {"NIL", "{{.NIL}}", "<nil>", tVal, true}, + + // Emtpy interfaces holding values. + {"empty nil", "{{.Empty0}}", "<no value>", tVal, true}, + {"empty with int", "{{.Empty1}}", "3", tVal, true}, + {"empty with string", "{{.Empty2}}", "empty2", tVal, true}, + {"empty with slice", "{{.Empty3}}", "[7 8]", tVal, true}, + {"empty with struct", "{{.Empty4}}", "{v}", tVal, true}, + + // Method calls. + {".Method0", "-{{.Method0}}-", "-resultOfMethod0-", tVal, true}, + {".Method1(1234)", "-{{.Method1 1234}}-", "-1234-", tVal, true}, + {".Method1(.I)", "-{{.Method1 .I}}-", "-17-", tVal, true}, + {".Method2(3, .X)", "-{{.Method2 3 .X}}-", "-Method2: 3 x-", tVal, true}, + {".Method2(.U16, `str`)", "-{{.Method2 .U16 `str`}}-", "-Method2: 16 str-", tVal, true}, + + // Pipelines. + {"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 resultOfMethod0-", tVal, true}, + + // If. + {"if true", "{{if true}}TRUE{{end}}", "TRUE", tVal, true}, + {"if false", "{{if false}}TRUE{{else}}FALSE{{end}}", "FALSE", tVal, true}, + {"if 1", "{{if 1}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, + {"if 0", "{{if 0}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"if 1.5", "{{if 1.5}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, + {"if 0.0", "{{if .FloatZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"if 1.5i", "{{if 1.5i}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, + {"if 0.0i", "{{if .ComplexZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"if emptystring", "{{if ``}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"if string", "{{if `notempty`}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, + {"if emptyslice", "{{if .SIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"if slice", "{{if .SI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, + {"if emptymap", "{{if .MSIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"if map", "{{if .MSI}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, + + // Printf. + {"printf", `{{printf "hello, printf"}}`, "hello, printf", tVal, true}, + {"printf int", `{{printf "%04x" 127}}`, "007f", tVal, true}, + {"printf float", `{{printf "%g" 3.5}}`, "3.5", tVal, true}, + {"printf complex", `{{printf "%g" 1+7i}}`, "(1+7i)", tVal, true}, + {"printf string", `{{printf "%s" "hello"}}`, "hello", tVal, true}, + {"printf function", `{{printf "%#q" gopher}}`, "`gopher`", tVal, true}, + {"printf field", `{{printf "%s" .U.V}}`, "v", tVal, true}, + {"printf method", `{{printf "%s" .Method0}}`, "resultOfMethod0", tVal, true}, + {"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) resultOfMethod0", tVal, true}, + + // HTML. + {"html", `{{html "<script>alert(\"XSS\");</script>"}}`, + "<script>alert("XSS");</script>", nil, true}, + {"html pipeline", `{{printf "<script>alert(\"XSS\");</script>" | html}}`, + "<script>alert("XSS");</script>", nil, true}, + + // JavaScript. + {"js", `{{js .}}`, `It\'d be nice.`, `It'd be nice.`, true}, + + // Booleans + {"not", "{{not true}} {{not false}}", "false true", nil, true}, + {"and", "{{and 0 0}} {{and 1 0}} {{and 0 1}} {{and 1 1}}", "false false false true", nil, true}, + {"or", "{{or 0 0}} {{or 1 0}} {{or 0 1}} {{or 1 1}}", "false true true true", nil, true}, + {"boolean if", "{{if and true 1 `hi`}}TRUE{{else}}FALSE{{end}}", "TRUE", tVal, true}, + {"boolean if not", "{{if and true 1 `hi` | not}}TRUE{{else}}FALSE{{end}}", "FALSE", nil, true}, + + // Indexing. + {"slice[0]", "{{index .SI 0}}", "3", tVal, true}, + {"slice[1]", "{{index .SI 1}}", "4", tVal, true}, + {"slice[HUGE]", "{{index .SI 10}}", "", tVal, false}, + {"slice[WRONG]", "{{index .SI `hello`}}", "", tVal, false}, + {"map[one]", "{{index .MSI `one`}}", "1", tVal, true}, + {"map[two]", "{{index .MSI `two`}}", "2", tVal, true}, + {"map[NO]", "{{index .MSI `XXX`}}", "", tVal, false}, + {"map[WRONG]", "{{index .MSI 10}}", "", tVal, false}, + {"double index", "{{index .SMSI 1 `eleven`}}", "11", tVal, true}, + + // With. + {"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true}, + {"with false", "{{with false}}{{.}}{{else}}FALSE{{end}}", "FALSE", tVal, true}, + {"with 1", "{{with 1}}{{.}}{{else}}ZERO{{end}}", "1", tVal, true}, + {"with 0", "{{with 0}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"with 1.5", "{{with 1.5}}{{.}}{{else}}ZERO{{end}}", "1.5", tVal, true}, + {"with 0.0", "{{with .FloatZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"with 1.5i", "{{with 1.5i}}{{.}}{{else}}ZERO{{end}}", "(0+1.5i)", tVal, true}, + {"with 0.0i", "{{with .ComplexZero}}{{.}}{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"with emptystring", "{{with ``}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"with string", "{{with `notempty`}}{{.}}{{else}}EMPTY{{end}}", "notempty", tVal, true}, + {"with emptyslice", "{{with .SIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"with slice", "{{with .SI}}{{.}}{{else}}EMPTY{{end}}", "[3 4 5]", tVal, true}, + {"with emptymap", "{{with .MSIEmpty}}{{.}}{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"with map", "{{with .MSIone}}{{.}}{{else}}EMPTY{{end}}", "map[one:1]", tVal, true}, + {"with empty interface, struct field", "{{with .Empty4}}{{.V}}{{end}}", "v", tVal, true}, + + // Range. + {"range []int", "{{range .SI}}-{{.}}-{{end}}", "-3--4--5-", tVal, true}, + {"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true}, + {"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true}, + {"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true}, + {"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true}, + {"range map", "{{range .MSI | .MSort}}-{{.}}-{{end}}", "-one--three--two-", tVal, true}, + {"range empty map no else", "{{range .MSIEmpty}}-{{.}}-{{end}}", "", tVal, true}, + {"range map else", "{{range .MSI | .MSort}}-{{.}}-{{else}}EMPTY{{end}}", "-one--three--two-", tVal, true}, + {"range empty map else", "{{range .MSIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, + {"range empty interface", "{{range .Empty3}}-{{.}}-{{else}}EMPTY{{end}}", "-7--8-", tVal, true}, + + // Error handling. + {"error method, error", "{{.EPERM true}}", "", tVal, false}, + {"error method, no error", "{{.EPERM false}}", "false", tVal, true}, +} + +func gopher() string { + return "gopher" +} + +func testExecute(execTests []execTest, set *Set, t *testing.T) { + b := new(bytes.Buffer) + funcs := FuncMap{"gopher": gopher} + for _, test := range execTests { + tmpl := New(test.name).Funcs(funcs) + err := tmpl.Parse(test.input) + if err != nil { + t.Errorf("%s: parse error: %s", test.name, err) + continue + } + b.Reset() + err = tmpl.ExecuteInSet(b, test.data, set) + switch { + case !test.ok && err == nil: + t.Errorf("%s: expected error; got none", test.name) + continue + case test.ok && err != nil: + t.Errorf("%s: unexpected execute error: %s", test.name, err) + continue + case !test.ok && err != nil: + // expected error, got one + if *debug { + fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) + } + } + result := b.String() + if result != test.output { + t.Errorf("%s: expected\n\t%q\ngot\n\t%q", test.name, test.output, result) + } + } +} + +func TestExecute(t *testing.T) { + testExecute(execTests, nil, t) +} + +// Check that an error from a method flows back to the top. +func TestExecuteError(t *testing.T) { + b := new(bytes.Buffer) + tmpl := New("error") + err := tmpl.Parse("{{.EPERM true}}") + if err != nil { + t.Fatalf("parse error: %s", err) + } + err = tmpl.Execute(b, tVal) + if err == nil { + t.Errorf("expected error; got none") + } else if !strings.Contains(err.String(), os.EPERM.String()) { + t.Errorf("expected os.EPERM; got %s", err) + } +} + +func TestJSEscaping(t *testing.T) { + testCases := []struct { + in, exp string + }{ + {`a`, `a`}, + {`'foo`, `\'foo`}, + {`Go "jump" \`, `Go \"jump\" \\`}, + {`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`}, + {"unprintable \uFDFF", `unprintable \uFDFF`}, + } + for _, tc := range testCases { + s := JSEscapeString(tc.in) + if s != tc.exp { + t.Errorf("JS escaping [%s] got [%s] want [%s]", tc.in, s, tc.exp) + } + } +} diff --git a/src/pkg/exp/template/funcs.go b/src/pkg/exp/template/funcs.go new file mode 100644 index 000000000..66be40fd4 --- /dev/null +++ b/src/pkg/exp/template/funcs.go @@ -0,0 +1,294 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package template + +import ( + "bytes" + "fmt" + "io" + "os" + "reflect" + "strings" + "unicode" + "utf8" +) + +// FuncMap is the type of the map defining the mapping from names to functions. +// Each function must have either a single return value, or two return values of +// which the second has type os.Error. +type FuncMap map[string]interface{} + +var funcs = map[string]reflect.Value{ + "and": reflect.ValueOf(and), + "html": reflect.ValueOf(HTMLEscaper), + "index": reflect.ValueOf(index), + "js": reflect.ValueOf(JSEscaper), + "not": reflect.ValueOf(not), + "or": reflect.ValueOf(or), + "printf": reflect.ValueOf(fmt.Sprintf), +} + +// addFuncs adds to values the functions in funcs, converting them to reflect.Values. +func addFuncs(values map[string]reflect.Value, funcMap FuncMap) { + for name, fn := range funcMap { + v := reflect.ValueOf(fn) + if v.Kind() != reflect.Func { + panic("value for " + name + " not a function") + } + if !goodFunc(v.Type()) { + panic(fmt.Errorf("can't handle multiple results from method/function %q", name)) + } + values[name] = v + } +} + +// goodFunc checks that the function or method has the right result signature. +func goodFunc(typ reflect.Type) bool { + // We allow functions with 1 result or 2 results where the second is an os.Error. + switch { + case typ.NumOut() == 1: + return true + case typ.NumOut() == 2 && typ.Out(1) == osErrorType: + return true + } + return false +} + +// findFunction looks for a function in the template, set, and global map. +func findFunction(name string, tmpl *Template, set *Set) (reflect.Value, bool) { + if tmpl != nil { + if fn := tmpl.funcs[name]; fn.IsValid() { + return fn, true + } + } + if set != nil { + if fn := set.funcs[name]; fn.IsValid() { + return fn, true + } + } + if fn := funcs[name]; fn.IsValid() { + return fn, true + } + return reflect.Value{}, false +} + +// Indexing. + +// index returns the result of indexing its first argument by the following +// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each +// indexed item must be a map, slice, or array. +func index(item interface{}, indices ...interface{}) (interface{}, os.Error) { + v := reflect.ValueOf(item) + for _, i := range indices { + index := reflect.ValueOf(i) + var isNil bool + if v, isNil = indirect(v); isNil { + return nil, fmt.Errorf("index of nil pointer") + } + switch v.Kind() { + case reflect.Array, reflect.Slice: + var x int64 + switch index.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + x = index.Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + x = int64(index.Uint()) + default: + return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type()) + } + if x < 0 || x >= int64(v.Len()) { + return nil, fmt.Errorf("index out of range: %d", x) + } + v = v.Index(int(x)) + case reflect.Map: + if !index.Type().AssignableTo(v.Type().Key()) { + return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type()) + } + v = v.MapIndex(index) + if !v.IsValid() { + return nil, fmt.Errorf("index %v not present in map", index.Interface()) + } + default: + return nil, fmt.Errorf("can't index item of type %s", index.Type()) + } + } + return v.Interface(), nil +} + +// Boolean logic. + +// and returns the Boolean AND of its arguments. +func and(arg0 interface{}, args ...interface{}) (truth bool) { + truth, _ = isTrue(reflect.ValueOf(arg0)) + for i := 0; truth && i < len(args); i++ { + truth, _ = isTrue(reflect.ValueOf(args[i])) + } + return +} + +// or returns the Boolean OR of its arguments. +func or(arg0 interface{}, args ...interface{}) (truth bool) { + truth, _ = isTrue(reflect.ValueOf(arg0)) + for i := 0; !truth && i < len(args); i++ { + truth, _ = isTrue(reflect.ValueOf(args[i])) + } + return +} + +// not returns the Boolean negation of its argument. +func not(arg interface{}) (truth bool) { + truth, _ = isTrue(reflect.ValueOf(arg)) + return !truth +} + +// HTML escaping. + +var ( + htmlQuot = []byte(""") // shorter than """ + htmlApos = []byte("'") // shorter than "'" + htmlAmp = []byte("&") + htmlLt = []byte("<") + htmlGt = []byte(">") +) + +// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b. +func HTMLEscape(w io.Writer, b []byte) { + last := 0 + for i, c := range b { + var html []byte + switch c { + case '"': + html = htmlQuot + case '\'': + html = htmlApos + case '&': + html = htmlAmp + case '<': + html = htmlLt + case '>': + html = htmlGt + default: + continue + } + w.Write(b[last:i]) + w.Write(html) + last = i + 1 + } + w.Write(b[last:]) +} + +// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s. +func HTMLEscapeString(s string) string { + // Avoid allocation if we can. + if strings.IndexAny(s, `'"&<>`) < 0 { + return s + } + var b bytes.Buffer + HTMLEscape(&b, []byte(s)) + return b.String() +} + +// HTMLEscaper returns the escaped HTML equivalent of the textual +// representation of its arguments. +func HTMLEscaper(args ...interface{}) string { + ok := false + var s string + if len(args) == 1 { + s, ok = args[0].(string) + } + if !ok { + s = fmt.Sprint(args...) + } + return HTMLEscapeString(s) +} + +// JavaScript escaping. + +var ( + jsLowUni = []byte(`\u00`) + hex = []byte("0123456789ABCDEF") + + jsBackslash = []byte(`\\`) + jsApos = []byte(`\'`) + jsQuot = []byte(`\"`) +) + + +// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b. +func JSEscape(w io.Writer, b []byte) { + last := 0 + for i := 0; i < len(b); i++ { + c := b[i] + + if ' ' <= c && c < utf8.RuneSelf && c != '\\' && c != '"' && c != '\'' { + // fast path: nothing to do + continue + } + w.Write(b[last:i]) + + if c < utf8.RuneSelf { + // Quotes and slashes get quoted. + // Control characters get written as \u00XX. + switch c { + case '\\': + w.Write(jsBackslash) + case '\'': + w.Write(jsApos) + case '"': + w.Write(jsQuot) + default: + w.Write(jsLowUni) + t, b := c>>4, c&0x0f + w.Write(hex[t : t+1]) + w.Write(hex[b : b+1]) + } + } else { + // Unicode rune. + rune, size := utf8.DecodeRune(b[i:]) + if unicode.IsPrint(rune) { + w.Write(b[i : i+size]) + } else { + // TODO(dsymonds): Do this without fmt? + fmt.Fprintf(w, "\\u%04X", rune) + } + i += size - 1 + } + last = i + 1 + } + w.Write(b[last:]) +} + +// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s. +func JSEscapeString(s string) string { + // Avoid allocation if we can. + if strings.IndexFunc(s, jsIsSpecial) < 0 { + return s + } + var b bytes.Buffer + JSEscape(&b, []byte(s)) + return b.String() +} + +func jsIsSpecial(rune int) bool { + switch rune { + case '\\', '\'', '"': + return true + } + return rune < ' ' || utf8.RuneSelf <= rune +} + +// JSEscaper returns the escaped JavaScript equivalent of the textual +// representation of its arguments. +func JSEscaper(args ...interface{}) string { + ok := false + var s string + if len(args) == 1 { + s, ok = args[0].(string) + } + if !ok { + s = fmt.Sprint(args...) + } + return JSEscapeString(s) +} diff --git a/src/pkg/exp/template/lex.go b/src/pkg/exp/template/lex.go new file mode 100644 index 000000000..d78152979 --- /dev/null +++ b/src/pkg/exp/template/lex.go @@ -0,0 +1,431 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package template + +import ( + "fmt" + "strings" + "unicode" + "utf8" +) + +// item represents a token or text string returned from the scanner. +type item struct { + typ itemType + val string +} + +func (i item) String() string { + switch { + case i.typ == itemEOF: + return "EOF" + case i.typ == itemError: + return i.val + case i.typ > itemKeyword: + return fmt.Sprintf("<%s>", i.val) + case len(i.val) > 10: + return fmt.Sprintf("%.10q...", i.val) + } + return fmt.Sprintf("%q", i.val) +} + +// itemType identifies the type of lex items. +type itemType int + +const ( + itemError itemType = iota // error occurred; value is text of error + itemBool // boolean constant + itemComplex // complex constant (1+2i); imaginary is just a number + itemEOF + itemField // alphanumeric identifier, starting with '.', possibly chained ('.x.y') + itemIdentifier // alphanumeric identifier + itemLeftDelim // left action delimiter + itemNumber // simple number, including imaginary + itemPipe // pipe symbol + itemRawString // raw quoted string (includes quotes) + itemRightDelim // right action delimiter + itemString // quoted string (includes quotes) + itemText // plain text + // Keywords appear after all the rest. + itemKeyword // used only to delimit the keywords + itemDot // the cursor, spelled '.'. + itemDefine // define keyword + itemElse // else keyword + itemEnd // end keyword + itemIf // if keyword + itemRange // range keyword + itemTemplate // template keyword + itemWith // with keyword +) + +// Make the types prettyprint. +var itemName = map[itemType]string{ + itemError: "error", + itemBool: "bool", + itemComplex: "complex", + itemEOF: "EOF", + itemField: "field", + itemIdentifier: "identifier", + itemLeftDelim: "left delim", + itemNumber: "number", + itemPipe: "pipe", + itemRawString: "raw string", + itemRightDelim: "right delim", + itemString: "string", + // keywords + itemDot: ".", + itemDefine: "define", + itemElse: "else", + itemIf: "if", + itemEnd: "end", + itemRange: "range", + itemTemplate: "template", + itemWith: "with", +} + +func (i itemType) String() string { + s := itemName[i] + if s == "" { + return fmt.Sprintf("item%d", int(i)) + } + return s +} + +var key = map[string]itemType{ + ".": itemDot, + "define": itemDefine, + "else": itemElse, + "end": itemEnd, + "if": itemIf, + "range": itemRange, + "template": itemTemplate, + "with": itemWith, +} + +const eof = -1 + +// stateFn represents the state of the scanner as a function that returns the next state. +type stateFn func(*lexer) stateFn + +// lexer holds the state of the scanner. +type lexer struct { + name string // the name of the input; used only for error reports. + input string // the string being scanned. + state stateFn // the next lexing function to enter + pos int // current position in the input. + start int // start position of this item. + width int // width of last rune read from input. + items chan item // channel of scanned items. +} + +// next returns the next rune in the input. +func (l *lexer) next() (rune int) { + if l.pos >= len(l.input) { + l.width = 0 + return eof + } + rune, l.width = utf8.DecodeRuneInString(l.input[l.pos:]) + l.pos += l.width + return rune +} + +// peek returns but does not consume the next rune in the input. +func (l *lexer) peek() int { + rune := l.next() + l.backup() + return rune +} + +// backup steps back one rune. Can only be called once per call of next. +func (l *lexer) backup() { + l.pos -= l.width +} + +// emit passes an item back to the client. +func (l *lexer) emit(t itemType) { + l.items <- item{t, l.input[l.start:l.pos]} + l.start = l.pos +} + +// ignore skips over the pending input before this point. +func (l *lexer) ignore() { + l.start = l.pos +} + +// accept consumes the next rune if it's from the valid set. +func (l *lexer) accept(valid string) bool { + if strings.IndexRune(valid, l.next()) >= 0 { + return true + } + l.backup() + return false +} + +// acceptRun consumes a run of runes from the valid set. +func (l *lexer) acceptRun(valid string) { + for strings.IndexRune(valid, l.next()) >= 0 { + } + l.backup() +} + +// lineNumber reports which line we're on. Doing it this way +// means we don't have to worry about peek double counting. +func (l *lexer) lineNumber() int { + return 1 + strings.Count(l.input[:l.pos], "\n") +} + +// error returns an error token and terminates the scan by passing +// back a nil pointer that will be the next state, terminating l.run. +func (l *lexer) errorf(format string, args ...interface{}) stateFn { + l.items <- item{itemError, fmt.Sprintf(format, args...)} + return nil +} + +// nextItem returns the next item from the input. +func (l *lexer) nextItem() item { + for { + select { + case item := <-l.items: + return item + default: + l.state = l.state(l) + } + } + panic("not reached") +} + +// lex creates a new scanner for the input string. +func lex(name, input string) *lexer { + l := &lexer{ + name: name, + input: input, + state: lexText, + items: make(chan item, 2), // Two items of buffering is sufficient for all state functions + } + return l +} + +// state functions + +const ( + leftDelim = "{{" + rightDelim = "}}" + leftComment = "{{/*" + rightComment = "*/}}" +) + +// lexText scans until an opening action delimiter, "{{". +func lexText(l *lexer) stateFn { + for { + if strings.HasPrefix(l.input[l.pos:], leftDelim) { + if l.pos > l.start { + l.emit(itemText) + } + return lexLeftDelim + } + if l.next() == eof { + break + } + } + // Correctly reached EOF. + if l.pos > l.start { + l.emit(itemText) + } + l.emit(itemEOF) + return nil +} + +// lexLeftDelim scans the left delimiter, which is known to be present. +func lexLeftDelim(l *lexer) stateFn { + if strings.HasPrefix(l.input[l.pos:], leftComment) { + return lexComment + } + l.pos += len(leftDelim) + l.emit(itemLeftDelim) + return lexInsideAction +} + +// lexComment scans a comment. The left comment marker is known to be present. +func lexComment(l *lexer) stateFn { + i := strings.Index(l.input[l.pos:], rightComment) + if i < 0 { + return l.errorf("unclosed comment") + } + l.pos += i + len(rightComment) + l.ignore() + return lexText +} + +// lexRightDelim scans the right delimiter, which is known to be present. +func lexRightDelim(l *lexer) stateFn { + l.pos += len(rightDelim) + l.emit(itemRightDelim) + return lexText +} + +// lexInsideAction scans the elements inside action delimiters. +func lexInsideAction(l *lexer) stateFn { + // Either number, quoted string, or identifier. + // Spaces separate and are ignored. + // Pipe symbols separate and are emitted. + for { + if strings.HasPrefix(l.input[l.pos:], rightDelim) { + return lexRightDelim + } + switch r := l.next(); { + case r == eof || r == '\n': + return l.errorf("unclosed action") + case isSpace(r): + l.ignore() + case r == '|': + l.emit(itemPipe) + case r == '"': + return lexQuote + case r == '`': + return lexRawQuote + case r == '.': + // special look-ahead for ".field" so we don't break l.backup(). + if l.pos < len(l.input) { + r := l.input[l.pos] + if r < '0' || '9' < r { + return lexIdentifier // itemDot comes from the keyword table. + } + } + fallthrough // '.' can start a number. + case r == '+' || r == '-' || ('0' <= r && r <= '9'): + l.backup() + return lexNumber + case isAlphaNumeric(r): + l.backup() + return lexIdentifier + default: + return l.errorf("unrecognized character in action: %#U", r) + } + } + return nil +} + +// lexIdentifier scans an alphanumeric or field. +func lexIdentifier(l *lexer) stateFn { +Loop: + for { + switch r := l.next(); { + case isAlphaNumeric(r): + // absorb. + case r == '.' && l.input[l.start] == '.': + // field chaining; absorb into one token. + default: + l.backup() + word := l.input[l.start:l.pos] + switch { + case key[word] > itemKeyword: + l.emit(key[word]) + case word[0] == '.': + l.emit(itemField) + case word == "true", word == "false": + l.emit(itemBool) + default: + l.emit(itemIdentifier) + } + break Loop + } + } + return lexInsideAction +} + +// lexNumber scans a number: decimal, octal, hex, float, or imaginary. This +// isn't a perfect number scanner - for instance it accepts "." and "0x0.2" +// and "089" - but when it's wrong the input is invalid and the parser (via +// strconv) will notice. +func lexNumber(l *lexer) stateFn { + if !l.scanNumber() { + return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) + } + if sign := l.peek(); sign == '+' || sign == '-' { + // Complex: 1+2i. No spaces, must end in 'i'. + if !l.scanNumber() || l.input[l.pos-1] != 'i' { + return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) + } + l.emit(itemComplex) + } else { + l.emit(itemNumber) + } + return lexInsideAction +} + +func (l *lexer) scanNumber() bool { + // Optional leading sign. + l.accept("+-") + // Is it hex? + digits := "0123456789" + if l.accept("0") && l.accept("xX") { + digits = "0123456789abcdefABCDEF" + } + l.acceptRun(digits) + if l.accept(".") { + l.acceptRun(digits) + } + if l.accept("eE") { + l.accept("+-") + l.acceptRun("0123456789") + } + // Is it imaginary? + l.accept("i") + // Next thing mustn't be alphanumeric. + if isAlphaNumeric(l.peek()) { + l.next() + return false + } + return true +} + +// lexQuote scans a quoted string. +func lexQuote(l *lexer) stateFn { +Loop: + for { + switch l.next() { + case '\\': + if r := l.next(); r != eof && r != '\n' { + break + } + fallthrough + case eof, '\n': + return l.errorf("unterminated quoted string") + case '"': + break Loop + } + } + l.emit(itemString) + return lexInsideAction +} + +// lexRawQuote scans a raw quoted string. +func lexRawQuote(l *lexer) stateFn { +Loop: + for { + switch l.next() { + case eof, '\n': + return l.errorf("unterminated raw quoted string") + case '`': + break Loop + } + } + l.emit(itemRawString) + return lexInsideAction +} + +// isSpace reports whether r is a space character. +func isSpace(r int) bool { + switch r { + case ' ', '\t', '\n', '\r': + return true + } + return false +} + +// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore. +func isAlphaNumeric(r int) bool { + return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) +} diff --git a/src/pkg/exp/template/lex_test.go b/src/pkg/exp/template/lex_test.go new file mode 100644 index 000000000..256ec04d8 --- /dev/null +++ b/src/pkg/exp/template/lex_test.go @@ -0,0 +1,153 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package template + +import ( + "reflect" + "testing" +) + +type lexTest struct { + name string + input string + items []item +} + +var ( + tEOF = item{itemEOF, ""} + tLeft = item{itemLeftDelim, "{{"} + tRight = item{itemRightDelim, "}}"} + tRange = item{itemRange, "range"} + tPipe = item{itemPipe, "|"} + tFor = item{itemIdentifier, "for"} + tQuote = item{itemString, `"abc \n\t\" "`} + raw = "`" + `abc\n\t\" ` + "`" + tRawQuote = item{itemRawString, raw} +) + +var lexTests = []lexTest{ + {"empty", "", []item{tEOF}}, + {"spaces", " \t\n", []item{{itemText, " \t\n"}, tEOF}}, + {"text", `now is the time`, []item{{itemText, "now is the time"}, tEOF}}, + {"text with comment", "hello-{{/* this is a comment */}}-world", []item{ + {itemText, "hello-"}, + {itemText, "-world"}, + tEOF, + }}, + {"empty action", `{{}}`, []item{tLeft, tRight, tEOF}}, + {"for", `{{for }}`, []item{tLeft, tFor, tRight, tEOF}}, + {"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}}, + {"raw quote", "{{" + raw + "}}", []item{tLeft, tRawQuote, tRight, tEOF}}, + {"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4 4.2i 1+2i}}", []item{ + tLeft, + {itemNumber, "1"}, + {itemNumber, "02"}, + {itemNumber, "0x14"}, + {itemNumber, "-7.2i"}, + {itemNumber, "1e3"}, + {itemNumber, "+1.2e-4"}, + {itemNumber, "4.2i"}, + {itemComplex, "1+2i"}, + tRight, + tEOF, + }}, + {"bools", "{{true false}}", []item{ + tLeft, + {itemBool, "true"}, + {itemBool, "false"}, + tRight, + tEOF, + }}, + {"dot", "{{.}}", []item{ + tLeft, + {itemDot, "."}, + tRight, + tEOF, + }}, + {"dots", "{{.x . .2 .x.y}}", []item{ + tLeft, + {itemField, ".x"}, + {itemDot, "."}, + {itemNumber, ".2"}, + {itemField, ".x.y"}, + tRight, + tEOF, + }}, + {"keywords", "{{range if else end with}}", []item{ + tLeft, + {itemRange, "range"}, + {itemIf, "if"}, + {itemElse, "else"}, + {itemEnd, "end"}, + {itemWith, "with"}, + tRight, + tEOF, + }}, + {"pipeline", `intro {{echo hi 1.2 |noargs|args 1 "hi"}} outro`, []item{ + {itemText, "intro "}, + tLeft, + {itemIdentifier, "echo"}, + {itemIdentifier, "hi"}, + {itemNumber, "1.2"}, + tPipe, + {itemIdentifier, "noargs"}, + tPipe, + {itemIdentifier, "args"}, + {itemNumber, "1"}, + {itemString, `"hi"`}, + tRight, + {itemText, " outro"}, + tEOF, + }}, + // errors + {"badchar", "#{{#}}", []item{ + {itemText, "#"}, + tLeft, + {itemError, "unrecognized character in action: U+0023 '#'"}, + }}, + {"unclosed action", "{{\n}}", []item{ + tLeft, + {itemError, "unclosed action"}, + }}, + {"EOF in action", "{{range", []item{ + tLeft, + tRange, + {itemError, "unclosed action"}, + }}, + {"unclosed quote", "{{\"\n\"}}", []item{ + tLeft, + {itemError, "unterminated quoted string"}, + }}, + {"unclosed raw quote", "{{`xx\n`}}", []item{ + tLeft, + {itemError, "unterminated raw quoted string"}, + }}, + {"bad number", "{{3k}}", []item{ + tLeft, + {itemError, `bad number syntax: "3k"`}, + }}, +} + +// collect gathers the emitted items into a slice. +func collect(t *lexTest) (items []item) { + l := lex(t.name, t.input) + for { + item := l.nextItem() + items = append(items, item) + if item.typ == itemEOF || item.typ == itemError { + break + } + } + return +} + +func TestLex(t *testing.T) { + for _, test := range lexTests { + items := collect(&test) + if !reflect.DeepEqual(items, test.items) { + t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items) + } + } +} diff --git a/src/pkg/exp/template/parse.go b/src/pkg/exp/template/parse.go new file mode 100644 index 000000000..8b2d60207 --- /dev/null +++ b/src/pkg/exp/template/parse.go @@ -0,0 +1,783 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package template + +import ( + "bytes" + "fmt" + "os" + "reflect" + "runtime" + "strconv" + "strings" + "unicode" +) + +// Template is the representation of a parsed template. +type Template struct { + name string + root *listNode + funcs map[string]reflect.Value + // Parsing only; cleared after parse. + set *Set + lex *lexer + token item // token lookahead for parser + havePeek bool +} + +// next returns the next token. +func (t *Template) next() item { + if t.havePeek { + t.havePeek = false + } else { + t.token = t.lex.nextItem() + } + return t.token +} + +// backup backs the input stream up one token. +func (t *Template) backup() { + t.havePeek = true +} + +// peek returns but does not consume the next token. +func (t *Template) peek() item { + if t.havePeek { + return t.token + } + t.token = t.lex.nextItem() + t.havePeek = true + return t.token +} + +// A node is an element in the parse tree. The interface is trivial. +type node interface { + typ() nodeType + String() string +} + +type nodeType int + +func (t nodeType) typ() nodeType { + return t +} + +const ( + nodeText nodeType = iota + nodeAction + nodeCommand + nodeDot + nodeElse + nodeEnd + nodeField + nodeIdentifier + nodeIf + nodeList + nodeNumber + nodeRange + nodeString + nodeTemplate + nodeWith +) + +// Nodes. + +// listNode holds a sequence of nodes. +type listNode struct { + nodeType + nodes []node +} + +func newList() *listNode { + return &listNode{nodeType: nodeList} +} + +func (l *listNode) append(n node) { + l.nodes = append(l.nodes, n) +} + +func (l *listNode) String() string { + b := new(bytes.Buffer) + fmt.Fprint(b, "[") + for _, n := range l.nodes { + fmt.Fprint(b, n) + } + fmt.Fprint(b, "]") + return b.String() +} + +// textNode holds plain text. +type textNode struct { + nodeType + text []byte +} + +func newText(text string) *textNode { + return &textNode{nodeType: nodeText, text: []byte(text)} +} + +func (t *textNode) String() string { + return fmt.Sprintf("(text: %q)", t.text) +} + +// actionNode holds an action (something bounded by delimiters). +type actionNode struct { + nodeType + line int + pipeline []*commandNode +} + +func newAction(line int, pipeline []*commandNode) *actionNode { + return &actionNode{nodeType: nodeAction, line: line, pipeline: pipeline} +} + +func (a *actionNode) append(command *commandNode) { + a.pipeline = append(a.pipeline, command) +} + +func (a *actionNode) String() string { + return fmt.Sprintf("(action: %v)", a.pipeline) +} + +// commandNode holds a command (a pipeline inside an evaluating action). +type commandNode struct { + nodeType + args []node // identifier, string, or number +} + +func newCommand() *commandNode { + return &commandNode{nodeType: nodeCommand} +} + +func (c *commandNode) append(arg node) { + c.args = append(c.args, arg) +} + +func (c *commandNode) String() string { + return fmt.Sprintf("(command: %v)", c.args) +} + +// identifierNode holds an identifier. +type identifierNode struct { + nodeType + ident string +} + +func newIdentifier(ident string) *identifierNode { + return &identifierNode{nodeType: nodeIdentifier, ident: ident} +} + +func (i *identifierNode) String() string { + return fmt.Sprintf("I=%s", i.ident) +} + +// dotNode holds the special identifier '.'. It is represented by a nil pointer. +type dotNode bool + +func newDot() *dotNode { + return nil +} + +func (d *dotNode) typ() nodeType { + return nodeDot +} + +func (d *dotNode) String() string { + return "{{<.>}}" +} + +// fieldNode holds a field (identifier starting with '.'). +// The names may be chained ('.x.y'). +// The period is dropped from each ident. +type fieldNode struct { + nodeType + ident []string +} + +func newField(ident string) *fieldNode { + return &fieldNode{nodeType: nodeField, ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period +} + +func (f *fieldNode) String() string { + return fmt.Sprintf("F=%s", f.ident) +} + +// boolNode holds a boolean constant. +type boolNode struct { + nodeType + true bool +} + +func newBool(true bool) *boolNode { + return &boolNode{nodeType: nodeString, true: true} +} + +func (b *boolNode) String() string { + if b.true { + return fmt.Sprintf("B=true") + } + return fmt.Sprintf("B=false") +} + +// numberNode holds a number, signed or unsigned integer, floating, or complex. +// The value is parsed and stored under all the types that can represent the value. +// This simulates in a small amount of code the behavior of Go's ideal constants. +type numberNode struct { + nodeType + isInt bool // number has an integral value + isUint bool // number has an unsigned integral value + isFloat bool // number has a floating-point value + isComplex bool // number is complex + int64 // the signed integer value + uint64 // the unsigned integer value + float64 // the floating-point value + complex128 // the complex value + text string +} + +func newNumber(text string, isComplex bool) (*numberNode, os.Error) { + n := &numberNode{nodeType: nodeNumber, text: text} + if isComplex { + // fmt.Sscan can parse the pair, so let it do the work. + if _, err := fmt.Sscan(text, &n.complex128); err != nil { + return nil, err + } + n.isComplex = true + n.simplifyComplex() + return n, nil + } + // Imaginary constants can only be complex unless they are zero. + if len(text) > 0 && text[len(text)-1] == 'i' { + f, err := strconv.Atof64(text[:len(text)-1]) + if err == nil { + n.isComplex = true + n.complex128 = complex(0, f) + n.simplifyComplex() + return n, nil + } + } + // Do integer test first so we get 0x123 etc. + u, err := strconv.Btoui64(text, 0) // will fail for -0; fixed below. + if err == nil { + n.isUint = true + n.uint64 = u + } + i, err := strconv.Btoi64(text, 0) + if err == nil { + n.isInt = true + n.int64 = i + if i == 0 { + n.isUint = true // in case of -0. + n.uint64 = u + } + } + // If an integer extraction succeeded, promote the float. + if n.isInt { + n.isFloat = true + n.float64 = float64(n.int64) + } else if n.isUint { + n.isFloat = true + n.float64 = float64(n.uint64) + } else { + f, err := strconv.Atof64(text) + if err == nil { + n.isFloat = true + n.float64 = f + // If a floating-point extraction succeeded, extract the int if needed. + if !n.isInt && float64(int64(f)) == f { + n.isInt = true + n.int64 = int64(f) + } + if !n.isUint && float64(uint64(f)) == f { + n.isUint = true + n.uint64 = uint64(f) + } + } + } + if !n.isInt && !n.isUint && !n.isFloat { + return nil, fmt.Errorf("illegal number syntax: %q", text) + } + return n, nil +} + +// simplifyComplex pulls out any other types that are represented by the complex number. +// These all require that the imaginary part be zero. +func (n *numberNode) simplifyComplex() { + n.isFloat = imag(n.complex128) == 0 + if n.isFloat { + n.float64 = real(n.complex128) + n.isInt = float64(int64(n.float64)) == n.float64 + if n.isInt { + n.int64 = int64(n.float64) + } + n.isUint = float64(uint64(n.float64)) == n.float64 + if n.isUint { + n.uint64 = uint64(n.float64) + } + } +} + +func (n *numberNode) String() string { + return fmt.Sprintf("N=%s", n.text) +} + +// stringNode holds a quoted string. +type stringNode struct { + nodeType + text string +} + +func newString(text string) *stringNode { + return &stringNode{nodeType: nodeString, text: text} +} + +func (s *stringNode) String() string { + return fmt.Sprintf("S=%#q", s.text) +} + +// endNode represents an {{end}} action. It is represented by a nil pointer. +type endNode bool + +func newEnd() *endNode { + return nil +} + +func (e *endNode) typ() nodeType { + return nodeEnd +} + +func (e *endNode) String() string { + return "{{end}}" +} + +// elseNode represents an {{else}} action. +type elseNode struct { + nodeType + line int +} + +func newElse(line int) *elseNode { + return &elseNode{nodeType: nodeElse, line: line} +} + +func (e *elseNode) typ() nodeType { + return nodeElse +} + +func (e *elseNode) String() string { + return "{{else}}" +} +// ifNode represents an {{if}} action and its commands. +// TODO: what should evaluation look like? is a pipeline enough? +type ifNode struct { + nodeType + line int + pipeline []*commandNode + list *listNode + elseList *listNode +} + +func newIf(line int, pipeline []*commandNode, list, elseList *listNode) *ifNode { + return &ifNode{nodeType: nodeIf, line: line, pipeline: pipeline, list: list, elseList: elseList} +} + +func (i *ifNode) String() string { + if i.elseList != nil { + return fmt.Sprintf("({{if %s}} %s {{else}} %s)", i.pipeline, i.list, i.elseList) + } + return fmt.Sprintf("({{if %s}} %s)", i.pipeline, i.list) +} + +// rangeNode represents a {{range}} action and its commands. +type rangeNode struct { + nodeType + line int + pipeline []*commandNode + list *listNode + elseList *listNode +} + +func newRange(line int, pipeline []*commandNode, list, elseList *listNode) *rangeNode { + return &rangeNode{nodeType: nodeRange, line: line, pipeline: pipeline, list: list, elseList: elseList} +} + +func (r *rangeNode) String() string { + if r.elseList != nil { + return fmt.Sprintf("({{range %s}} %s {{else}} %s)", r.pipeline, r.list, r.elseList) + } + return fmt.Sprintf("({{range %s}} %s)", r.pipeline, r.list) +} + +// templateNode represents a {{template}} action. +type templateNode struct { + nodeType + line int + name node + pipeline []*commandNode +} + +func newTemplate(line int, name node, pipeline []*commandNode) *templateNode { + return &templateNode{nodeType: nodeTemplate, line: line, name: name, pipeline: pipeline} +} + +func (t *templateNode) String() string { + return fmt.Sprintf("{{template %s %s}}", t.name, t.pipeline) +} + +// withNode represents a {{with}} action and its commands. +type withNode struct { + nodeType + line int + pipeline []*commandNode + list *listNode + elseList *listNode +} + +func newWith(line int, pipeline []*commandNode, list, elseList *listNode) *withNode { + return &withNode{nodeType: nodeWith, line: line, pipeline: pipeline, list: list, elseList: elseList} +} + +func (w *withNode) String() string { + if w.elseList != nil { + return fmt.Sprintf("({{with %s}} %s {{else}} %s)", w.pipeline, w.list, w.elseList) + } + return fmt.Sprintf("({{with %s}} %s)", w.pipeline, w.list) +} + + +// Parsing. + +// New allocates a new template with the given name. +func New(name string) *Template { + return &Template{ + name: name, + funcs: make(map[string]reflect.Value), + } +} + +// Funcs adds to the template's function map the elements of the +// argument map. It panics if a value in the map is not a function +// with appropriate return type. +// The return value is the template, so calls can be chained. +func (t *Template) Funcs(funcMap FuncMap) *Template { + addFuncs(t.funcs, funcMap) + return t +} + +// errorf formats the error and terminates processing. +func (t *Template) errorf(format string, args ...interface{}) { + t.root = nil + format = fmt.Sprintf("template: %s:%d: %s", t.name, t.lex.lineNumber(), format) + panic(fmt.Errorf(format, args...)) +} + +// error terminates processing. +func (t *Template) error(err os.Error) { + t.errorf("%s", err) +} + +// expect consumes the next token and guarantees it has the required type. +func (t *Template) expect(expected itemType, context string) item { + token := t.next() + if token.typ != expected { + t.errorf("expected %s in %s; got %s", expected, context, token) + } + return token +} + +// unexpected complains about the token and terminates processing. +func (t *Template) unexpected(token item, context string) { + t.errorf("unexpected %s in %s", token, context) +} + +// recover is the handler that turns panics into returns from the top +// level of Parse or Execute. +func (t *Template) recover(errp *os.Error) { + e := recover() + if e != nil { + if _, ok := e.(runtime.Error); ok { + panic(e) + } + t.stopParse() + *errp = e.(os.Error) + } + return +} + +// startParse starts the template parsing from the lexer. +func (t *Template) startParse(set *Set, lex *lexer) { + t.root = nil + t.set = set + t.lex = lex +} + +// stopParse terminates parsing. +func (t *Template) stopParse() { + t.set, t.lex = nil, nil +} + +// atEOF returns true if, possibly after spaces, we're at EOF. +func (t *Template) atEOF() bool { + for { + token := t.peek() + switch token.typ { + case itemEOF: + return true + case itemText: + for _, r := range token.val { + if !unicode.IsSpace(r) { + return false + } + } + t.next() // skip spaces. + continue + } + break + } + return false +} + +// Parse parses the template definition string to construct an internal representation +// of the template for execution. +func (t *Template) Parse(s string) (err os.Error) { + t.startParse(nil, lex(t.name, s)) + defer t.recover(&err) + t.parse(true) + t.stopParse() + return +} + +// ParseInSet parses the template definition string to construct an internal representation +// of the template for execution. Function bindings are checked against those in the set. +func (t *Template) ParseInSet(s string, set *Set) (err os.Error) { + t.startParse(set, lex(t.name, s)) + defer t.recover(&err) + t.parse(true) + t.stopParse() + return +} + +// parse is the helper for Parse. It triggers an error if we expect EOF but don't reach it. +func (t *Template) parse(toEOF bool) (next node) { + t.root, next = t.itemList(true) + if toEOF && next != nil { + t.errorf("unexpected %s", next) + } + return next +} + +// itemList: +// textOrAction* +// Terminates at EOF and at {{end}} or {{else}}, which is returned separately. +// The toEOF flag tells whether we expect to reach EOF. +func (t *Template) itemList(toEOF bool) (list *listNode, next node) { + list = newList() + for t.peek().typ != itemEOF { + n := t.textOrAction() + switch n.typ() { + case nodeEnd, nodeElse: + return list, n + } + list.append(n) + } + if !toEOF { + t.unexpected(t.next(), "input") + } + return list, nil +} + +// textOrAction: +// text | action +func (t *Template) textOrAction() node { + switch token := t.next(); token.typ { + case itemText: + return newText(token.val) + case itemLeftDelim: + return t.action() + default: + t.unexpected(token, "input") + } + return nil +} + +// Action: +// control +// command ("|" command)* +// Left delim is past. Now get actions. +// First word could be a keyword such as range. +func (t *Template) action() (n node) { + switch token := t.next(); token.typ { + case itemElse: + return t.elseControl() + case itemEnd: + return t.endControl() + case itemIf: + return t.ifControl() + case itemRange: + return t.rangeControl() + case itemTemplate: + return t.templateControl() + case itemWith: + return t.withControl() + } + t.backup() + return newAction(t.lex.lineNumber(), t.pipeline("command")) +} + +// Pipeline: +// field or command +// pipeline "|" pipeline +func (t *Template) pipeline(context string) (pipe []*commandNode) { + for { + switch token := t.next(); token.typ { + case itemRightDelim: + if len(pipe) == 0 { + t.errorf("missing value for %s", context) + } + return + case itemBool, itemComplex, itemDot, itemField, itemIdentifier, itemNumber, itemRawString, itemString: + t.backup() + pipe = append(pipe, t.command()) + default: + t.unexpected(token, context) + } + } + return +} + +func (t *Template) parseControl(context string) (lineNum int, pipe []*commandNode, list, elseList *listNode) { + lineNum = t.lex.lineNumber() + pipe = t.pipeline(context) + var next node + list, next = t.itemList(false) + switch next.typ() { + case nodeEnd: //done + case nodeElse: + elseList, next = t.itemList(false) + if next.typ() != nodeEnd { + t.errorf("expected end; found %s", next) + } + elseList = elseList + } + return lineNum, pipe, list, elseList +} + +// If: +// {{if pipeline}} itemList {{end}} +// {{if pipeline}} itemList {{else}} itemList {{end}} +// If keyword is past. +func (t *Template) ifControl() node { + return newIf(t.parseControl("if")) +} + +// Range: +// {{range pipeline}} itemList {{end}} +// {{range pipeline}} itemList {{else}} itemList {{end}} +// Range keyword is past. +func (t *Template) rangeControl() node { + return newRange(t.parseControl("range")) +} + +// With: +// {{with pipeline}} itemList {{end}} +// {{with pipeline}} itemList {{else}} itemList {{end}} +// If keyword is past. +func (t *Template) withControl() node { + return newWith(t.parseControl("with")) +} + + +// End: +// {{end}} +// End keyword is past. +func (t *Template) endControl() node { + t.expect(itemRightDelim, "end") + return newEnd() +} + +// Else: +// {{else}} +// Else keyword is past. +func (t *Template) elseControl() node { + t.expect(itemRightDelim, "else") + return newElse(t.lex.lineNumber()) +} + +// Template: +// {{template stringValue pipeline}} +// Template keyword is past. The name must be something that can evaluate +// to a string. +func (t *Template) templateControl() node { + var name node + switch token := t.next(); token.typ { + case itemIdentifier: + if _, ok := findFunction(token.val, t, t.set); !ok { + t.errorf("function %q not defined", token.val) + } + name = newIdentifier(token.val) + case itemDot: + name = newDot() + case itemField: + name = newField(token.val) + case itemString, itemRawString: + s, err := strconv.Unquote(token.val) + if err != nil { + t.error(err) + } + name = newString(s) + default: + t.unexpected(token, "template invocation") + } + pipeline := t.pipeline("template") + return newTemplate(t.lex.lineNumber(), name, pipeline) +} + +// command: +// space-separated arguments up to a pipeline character or right delimiter. +// we consume the pipe character but leave the right delim to terminate the action. +func (t *Template) command() *commandNode { + cmd := newCommand() +Loop: + for { + switch token := t.next(); token.typ { + case itemRightDelim: + t.backup() + break Loop + case itemPipe: + break Loop + case itemError: + t.errorf("%s", token.val) + case itemIdentifier: + if _, ok := findFunction(token.val, t, t.set); !ok { + t.errorf("function %q not defined", token.val) + } + cmd.append(newIdentifier(token.val)) + case itemDot: + cmd.append(newDot()) + case itemField: + cmd.append(newField(token.val)) + case itemBool: + cmd.append(newBool(token.val == "true")) + case itemComplex, itemNumber: + number, err := newNumber(token.val, token.typ == itemComplex) + if err != nil { + t.error(err) + } + cmd.append(number) + case itemString, itemRawString: + s, err := strconv.Unquote(token.val) + if err != nil { + t.error(err) + } + cmd.append(newString(s)) + default: + t.unexpected(token, "command") + } + } + if len(cmd.args) == 0 { + t.errorf("empty command") + } + return cmd +} diff --git a/src/pkg/exp/template/parse_test.go b/src/pkg/exp/template/parse_test.go new file mode 100644 index 000000000..71580f8b6 --- /dev/null +++ b/src/pkg/exp/template/parse_test.go @@ -0,0 +1,207 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package template + +import ( + "flag" + "fmt" + "testing" +) + +var debug = flag.Bool("debug", false, "show the errors produced by the tests") + +type numberTest struct { + text string + isInt bool + isUint bool + isFloat bool + isComplex bool + int64 + uint64 + float64 + complex128 +} + +var numberTests = []numberTest{ + // basics + {"0", true, true, true, false, 0, 0, 0, 0}, + {"-0", true, true, true, false, 0, 0, 0, 0}, // check that -0 is a uint. + {"73", true, true, true, false, 73, 73, 73, 0}, + {"-73", true, false, true, false, -73, 0, -73, 0}, + {"+73", true, false, true, false, 73, 0, 73, 0}, + {"100", true, true, true, false, 100, 100, 100, 0}, + {"1e9", true, true, true, false, 1e9, 1e9, 1e9, 0}, + {"-1e9", true, false, true, false, -1e9, 0, -1e9, 0}, + {"-1.2", false, false, true, false, 0, 0, -1.2, 0}, + {"1e19", false, true, true, false, 0, 1e19, 1e19, 0}, + {"-1e19", false, false, true, false, 0, 0, -1e19, 0}, + {"4i", false, false, false, true, 0, 0, 0, 4i}, + {"-1.2+4.2i", false, false, false, true, 0, 0, 0, -1.2 + 4.2i}, + // complex with 0 imaginary are float (and maybe integer) + {"0i", true, true, true, true, 0, 0, 0, 0}, + {"-1.2+0i", false, false, true, true, 0, 0, -1.2, -1.2}, + {"-12+0i", true, false, true, true, -12, 0, -12, -12}, + {"13+0i", true, true, true, true, 13, 13, 13, 13}, + // funny bases + {"0123", true, true, true, false, 0123, 0123, 0123, 0}, + {"-0x0", true, true, true, false, 0, 0, 0, 0}, + {"0xdeadbeef", true, true, true, false, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0}, + // some broken syntax + {text: "+-2"}, + {text: "0x123."}, + {text: "1e."}, + {text: "0xi."}, + {text: "1+2."}, +} + +func TestNumberParse(t *testing.T) { + for _, test := range numberTests { + // If fmt.Sscan thinks it's complex, it's complex. We can't trust the output + // because imaginary comes out as a number. + var c complex128 + _, err := fmt.Sscan(test.text, &c) + n, err := newNumber(test.text, err == nil) + ok := test.isInt || test.isUint || test.isFloat || test.isComplex + if ok && err != nil { + t.Errorf("unexpected error for %q", test.text) + continue + } + if !ok && err == nil { + t.Errorf("expected error for %q", test.text) + continue + } + if !ok { + continue + } + if n.isComplex != test.isComplex { + t.Errorf("complex incorrect for %q; should be %t", test.text, test.isComplex) + } + if test.isInt { + if !n.isInt { + t.Errorf("expected integer for %q", test.text) + } + if n.int64 != test.int64 { + t.Errorf("int64 for %q should be %d is %d", test.text, test.int64, n.int64) + } + } else if n.isInt { + t.Errorf("did not expect integer for %q", test.text) + } + if test.isUint { + if !n.isUint { + t.Errorf("expected unsigned integer for %q", test.text) + } + if n.uint64 != test.uint64 { + t.Errorf("uint64 for %q should be %d is %d", test.text, test.uint64, n.uint64) + } + } else if n.isUint { + t.Errorf("did not expect unsigned integer for %q", test.text) + } + if test.isFloat { + if !n.isFloat { + t.Errorf("expected float for %q", test.text) + } + if n.float64 != test.float64 { + t.Errorf("float64 for %q should be %g is %g", test.text, test.float64, n.float64) + } + } else if n.isFloat { + t.Errorf("did not expect float for %q", test.text) + } + if test.isComplex { + if !n.isComplex { + t.Errorf("expected complex for %q", test.text) + } + if n.complex128 != test.complex128 { + t.Errorf("complex128 for %q should be %g is %g", test.text, test.complex128, n.complex128) + } + } else if n.isComplex { + t.Errorf("did not expect complex for %q", test.text) + } + } +} + +type parseTest struct { + name string + input string + ok bool + result string +} + +const ( + noError = true + hasError = false +) + +var parseTests = []parseTest{ + {"empty", "", noError, + `[]`}, + {"spaces", " \t\n", noError, + `[(text: " \t\n")]`}, + {"text", "some text", noError, + `[(text: "some text")]`}, + {"emptyAction", "{{}}", hasError, + `[(action: [])]`}, + {"field", "{{.X}}", noError, + `[(action: [(command: [F=[X]])])]`}, + {"simple command", "{{printf}}", noError, + `[(action: [(command: [I=printf])])]`}, + {"multi-word command", "{{printf `%d` 23}}", noError, + "[(action: [(command: [I=printf S=`%d` N=23])])]"}, + {"pipeline", "{{.X|.Y}}", noError, + `[(action: [(command: [F=[X]]) (command: [F=[Y]])])]`}, + {"simple if", "{{if .X}}hello{{end}}", noError, + `[({{if [(command: [F=[X]])]}} [(text: "hello")])]`}, + {"if with else", "{{if .X}}true{{else}}false{{end}}", noError, + `[({{if [(command: [F=[X]])]}} [(text: "true")] {{else}} [(text: "false")])]`}, + {"simple range", "{{range .X}}hello{{end}}", noError, + `[({{range [(command: [F=[X]])]}} [(text: "hello")])]`}, + {"chained field range", "{{range .X.Y.Z}}hello{{end}}", noError, + `[({{range [(command: [F=[X Y Z]])]}} [(text: "hello")])]`}, + {"nested range", "{{range .X}}hello{{range .Y}}goodbye{{end}}{{end}}", noError, + `[({{range [(command: [F=[X]])]}} [(text: "hello")({{range [(command: [F=[Y]])]}} [(text: "goodbye")])])]`}, + {"range with else", "{{range .X}}true{{else}}false{{end}}", noError, + `[({{range [(command: [F=[X]])]}} [(text: "true")] {{else}} [(text: "false")])]`}, + {"range over pipeline", "{{range .X|.M}}true{{else}}false{{end}}", noError, + `[({{range [(command: [F=[X]]) (command: [F=[M]])]}} [(text: "true")] {{else}} [(text: "false")])]`}, + {"range []int", "{{range .SI}}{{.}}{{end}}", noError, + `[({{range [(command: [F=[SI]])]}} [(action: [(command: [{{<.>}}])])])]`}, + {"constants", "{{range .SI 1 -3.2i true false }}{{end}}", noError, + `[({{range [(command: [F=[SI] N=1 N=-3.2i B=true B=false])]}} [])]`}, + {"template", "{{template `x` .Y}}", noError, + "[{{template S=`x` [(command: [F=[Y]])]}}]"}, + {"with", "{{with .X}}hello{{end}}", noError, + `[({{with [(command: [F=[X]])]}} [(text: "hello")])]`}, + {"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError, + `[({{with [(command: [F=[X]])]}} [(text: "hello")] {{else}} [(text: "goodbye")])]`}, + // Errors. + {"unclosed action", "hello{{range", hasError, ""}, + {"missing end", "hello{{range .x}}", hasError, ""}, + {"missing end after else", "hello{{range .x}}{{else}}", hasError, ""}, + {"undefined function", "hello{{undefined}}", hasError, ""}, +} + +func TestParse(t *testing.T) { + for _, test := range parseTests { + tmpl := New(test.name) + err := tmpl.Parse(test.input) + switch { + case err == nil && !test.ok: + t.Errorf("%q: expected error; got none", test.name) + continue + case err != nil && test.ok: + t.Errorf("%q: unexpected error: %v", test.name, err) + continue + case err != nil && !test.ok: + // expected error, got one + if *debug { + fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) + } + continue + } + result := tmpl.root.String() + if result != test.result { + t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.result) + } + } +} diff --git a/src/pkg/exp/template/set.go b/src/pkg/exp/template/set.go new file mode 100644 index 000000000..492e270e1 --- /dev/null +++ b/src/pkg/exp/template/set.go @@ -0,0 +1,115 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package template + +import ( + "fmt" + "io" + "os" + "reflect" + "runtime" + "strconv" +) + +// Set holds a set of related templates that can refer to one another by name. +// A template may be a member of multiple sets. +type Set struct { + tmpl map[string]*Template + funcs map[string]reflect.Value +} + +// NewSet allocates a new, empty template set. +func NewSet() *Set { + return &Set{ + tmpl: make(map[string]*Template), + funcs: make(map[string]reflect.Value), + } +} + +// Funcs adds to the set's function map the elements of the +// argument map. It panics if a value in the map is not a function +// with appropriate return type. +// The return value is the set, so calls can be chained. +func (s *Set) Funcs(funcMap FuncMap) *Set { + addFuncs(s.funcs, funcMap) + return s +} + +// Add adds the argument templates to the set. It panics if the call +// attempts to reuse a name defined in the template. +// The return value is the set, so calls can be chained. +func (s *Set) Add(templates ...*Template) *Set { + for _, t := range templates { + if _, ok := s.tmpl[t.name]; ok { + panic(fmt.Errorf("template: %q already defined in set", t.name)) + } + s.tmpl[t.name] = t + } + return s +} + +// Template returns the template with the given name in the set, +// or nil if there is no such template. +func (s *Set) Template(name string) *Template { + return s.tmpl[name] +} + +// Execute looks for the named template in the set and then applies that +// template to the specified data object, writing the output to wr. Nested +// template invocations will be resolved from the set. +func (s *Set) Execute(name string, wr io.Writer, data interface{}) os.Error { + tmpl := s.tmpl[name] + if tmpl == nil { + return fmt.Errorf("template: no template %q in set", name) + } + return tmpl.ExecuteInSet(wr, data, s) +} + +// recover is the handler that turns panics into returns from the top +// level of Parse. +func (s *Set) recover(errp *os.Error) { + e := recover() + if e != nil { + if _, ok := e.(runtime.Error); ok { + panic(e) + } + s.tmpl = nil + *errp = e.(os.Error) + } + return +} + +// Parse parses the file into a set of named templates. +func (s *Set) Parse(text string) (err os.Error) { + defer s.recover(&err) + lex := lex("set", text) + const context = "define clause" + for { + t := New("set") // name will be updated once we know it. + t.startParse(s, lex) + // Expect EOF or "{{ define name }}". + if t.atEOF() { + return + } + t.expect(itemLeftDelim, context) + t.expect(itemDefine, context) + name := t.expect(itemString, context) + t.name, err = strconv.Unquote(name.val) + if err != nil { + t.error(err) + } + t.expect(itemRightDelim, context) + end := t.parse(false) + if end == nil { + t.errorf("unexpected EOF in %s", context) + } + if end.typ() != nodeEnd { + t.errorf("unexpected %s in %s", end, context) + } + t.stopParse() + s.tmpl[t.name] = t + } + return nil +} diff --git a/src/pkg/exp/template/set_test.go b/src/pkg/exp/template/set_test.go new file mode 100644 index 000000000..c0115ec0a --- /dev/null +++ b/src/pkg/exp/template/set_test.go @@ -0,0 +1,101 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package template + +import ( + "fmt" + "testing" +) + +type setParseTest struct { + name string + input string + ok bool + names []string + results []string +} + +var setParseTests = []setParseTest{ + {"empty", "", noError, + nil, + nil}, + {"one", `{{define "foo"}} FOO {{end}}`, noError, + []string{"foo"}, + []string{`[(text: " FOO ")]`}}, + {"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError, + []string{"foo", "bar"}, + []string{`[(text: " FOO ")]`, `[(text: " BAR ")]`}}, + // errors + {"missing end", `{{define "foo"}} FOO `, hasError, + nil, + nil}, + {"malformed name", `{{define "foo}} FOO `, hasError, + nil, + nil}, +} + +func TestSetParse(t *testing.T) { + for _, test := range setParseTests { + set := NewSet() + err := set.Parse(test.input) + switch { + case err == nil && !test.ok: + t.Errorf("%q: expected error; got none", test.name) + continue + case err != nil && test.ok: + t.Errorf("%q: unexpected error: %v", test.name, err) + continue + case err != nil && !test.ok: + // expected error, got one + if *debug { + fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err) + } + continue + } + if len(set.tmpl) != len(test.names) { + t.Errorf("%s: wrong number of templates; wanted %d got %d", test.name, len(test.names), len(set.tmpl)) + continue + } + for i, name := range test.names { + tmpl, ok := set.tmpl[name] + if !ok { + t.Errorf("%s: can't find template %q", test.name, name) + continue + } + result := tmpl.root.String() + if result != test.results[i] { + t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.results[i]) + } + } + } +} + + +var setExecTests = []execTest{ + {"empty", "", "", nil, true}, + {"text", "some text", "some text", nil, true}, + {"invoke text", `{{template "text" .SI}}`, "TEXT", tVal, true}, + {"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true}, + {"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true}, + {"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true}, + {"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true}, +} + +const setText = ` + {{define "text"}}TEXT{{end}} + {{define "dotV"}}{{.V}}{{end}} + {{define "dot"}}{{.}}{{end}} + {{define "nested"}}{{template "dot" .}}{{end}} +` + +func TestSetExecute(t *testing.T) { + // Declare a set with a couple of templates first. + set := NewSet() + err := set.Parse(setText) + if err != nil { + t.Fatalf("error parsing set: %s", err) + } + testExecute(setExecTests, set, t) +} diff --git a/src/pkg/flag/flag.go b/src/pkg/flag/flag.go index e5d2f94e9..f9b852c0f 100644 --- a/src/pkg/flag/flag.go +++ b/src/pkg/flag/flag.go @@ -218,7 +218,7 @@ type Flag struct { // sortFlags returns the flags as a slice in lexicographical sorted order. func sortFlags(flags map[string]*Flag) []*Flag { - list := make(sort.StringArray, len(flags)) + list := make(sort.StringSlice, len(flags)) i := 0 for _, f := range flags { list[i] = f.Name diff --git a/src/pkg/fmt/doc.go b/src/pkg/fmt/doc.go index 79fe5758c..35a11e19f 100644 --- a/src/pkg/fmt/doc.go +++ b/src/pkg/fmt/doc.go @@ -63,11 +63,13 @@ number of characters to output, truncating if necessary. Other flags: - + always print a sign for numeric values + + always print a sign for numeric values; + guarantee ASCII-only output for %q (%+q) - pad with spaces on the right rather than the left (left-justify the field) # alternate format: add leading 0 for octal (%#o), 0x for hex (%#x); 0X for hex (%#X); suppress 0x for %p (%#p); - print a raw (backquoted) string if possible for %q (%#q) + print a raw (backquoted) string if possible for %q (%#q); + write e.g. U+0078 'x' if the character is printable for %U (%#U). ' ' (space) leave a space for elided sign in numbers (% d); put spaces between bytes printing strings or slices in hex (% x, % X) 0 pad with leading zeros rather than spaces diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go index 122b9516b..9a8024528 100644 --- a/src/pkg/fmt/fmt_test.go +++ b/src/pkg/fmt/fmt_test.go @@ -133,6 +133,7 @@ var fmttests = []struct { {"%q", "\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`}, {"%q", "abc\xffdef", `"abc\xffdef"`}, {"%q", "\u263a", `"☺"`}, + {"%+q", "\u263a", `"\u263a"`}, {"%q", "\U0010ffff", `"\U0010ffff"`}, // escaped characters @@ -145,6 +146,8 @@ var fmttests = []struct { {"%q", uint64(0xFFFFFFFF), `%!q(uint64=4294967295)`}, {"%q", '"', `'"'`}, {"%q", '\'', `'\''`}, + {"%q", "\u263a", `"☺"`}, + {"%+q", "\u263a", `"\u263a"`}, // width {"%5s", "abc", " abc"}, @@ -187,6 +190,12 @@ var fmttests = []struct { {"%U", 0x12345, "U+12345"}, {"%10.6U", 0xABC, " U+000ABC"}, {"%-10.6U", 0xABC, "U+000ABC "}, + {"%U", '\n', `U+000A`}, + {"%#U", '\n', `U+000A`}, + {"%U", 'x', `U+0078`}, + {"%#U", 'x', `U+0078 'x'`}, + {"%U", '\u263a', `U+263A`}, + {"%#U", '\u263a', `U+263A '☺'`}, // floats {"%+.3e", 0.0, "+0.000e+00"}, @@ -674,3 +683,56 @@ func TestWidthAndPrecision(t *testing.T) { } } } + +// A type that panics in String. +type Panic struct { + message interface{} +} + +// Value receiver. +func (p Panic) GoString() string { + panic(p.message) +} + +// Value receiver. +func (p Panic) String() string { + panic(p.message) +} + +// A type that panics in Format. +type PanicF struct { + message interface{} +} + +// Value receiver. +func (p PanicF) Format(f State, c int) { + panic(p.message) +} + +var panictests = []struct { + fmt string + in interface{} + out string +}{ + // String + {"%d", (*Panic)(nil), "<nil>"}, // nil pointer special case + {"%d", Panic{io.ErrUnexpectedEOF}, "%d(PANIC=unexpected EOF)"}, + {"%d", Panic{3}, "%d(PANIC=3)"}, + // GoString + {"%#v", (*Panic)(nil), "<nil>"}, // nil pointer special case + {"%#v", Panic{io.ErrUnexpectedEOF}, "%v(PANIC=unexpected EOF)"}, + {"%#v", Panic{3}, "%v(PANIC=3)"}, + // Format + {"%s", (*PanicF)(nil), "<nil>"}, // nil pointer special case + {"%s", PanicF{io.ErrUnexpectedEOF}, "%s(PANIC=unexpected EOF)"}, + {"%s", PanicF{3}, "%s(PANIC=3)"}, +} + +func TestPanics(t *testing.T) { + for _, tt := range panictests { + s := Sprintf(tt.fmt, tt.in) + if s != tt.out { + t.Errorf("%q: got %q expected %q", tt.fmt, s, tt.out) + } + } +} diff --git a/src/pkg/fmt/format.go b/src/pkg/fmt/format.go index 5dcfb9677..bec55f75b 100644 --- a/src/pkg/fmt/format.go +++ b/src/pkg/fmt/format.go @@ -7,6 +7,7 @@ package fmt import ( "bytes" "strconv" + "unicode" "utf8" ) @@ -50,6 +51,7 @@ type fmt struct { sharp bool space bool unicode bool + uniQuote bool // Use 'x'= prefix for %U if printable. zero bool } @@ -63,6 +65,7 @@ func (f *fmt) clearflags() { f.sharp = false f.space = false f.unicode = false + f.uniQuote = false f.zero = false } @@ -232,6 +235,24 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) { i-- buf[i] = ' ' } + + // If we want a quoted char for %#U, move the data up to make room. + if f.unicode && f.uniQuote && a >= 0 && a <= unicode.MaxRune && unicode.IsPrint(int(a)) { + runeWidth := utf8.RuneLen(int(a)) + width := 1 + 1 + runeWidth + 1 // space, quote, rune, quote + copy(buf[i-width:], buf[i:]) // guaranteed to have enough room. + i -= width + // Now put " 'x'" at the end. + j := len(buf) - width + buf[j] = ' ' + j++ + buf[j] = '\'' + j++ + utf8.EncodeRune(buf[j:], int(a)) + j += runeWidth + buf[j] = '\'' + } + f.pad(buf[i:]) } @@ -291,7 +312,11 @@ func (f *fmt) fmt_q(s string) { if f.sharp && strconv.CanBackquote(s) { quoted = "`" + s + "`" } else { - quoted = strconv.Quote(s) + if f.plus { + quoted = strconv.QuoteToASCII(s) + } else { + quoted = strconv.Quote(s) + } } f.padString(quoted) } @@ -299,7 +324,12 @@ func (f *fmt) fmt_q(s string) { // fmt_qc formats the integer as a single-quoted, escaped Go character constant. // If the character is not valid Unicode, it will print '\ufffd'. func (f *fmt) fmt_qc(c int64) { - quoted := strconv.QuoteRune(int(c)) + var quoted string + if f.plus { + quoted = strconv.QuoteRuneToASCII(int(c)) + } else { + quoted = strconv.QuoteRune(int(c)) + } f.padString(quoted) } diff --git a/src/pkg/fmt/print.go b/src/pkg/fmt/print.go index c18a8ea38..5c083e5e9 100644 --- a/src/pkg/fmt/print.go +++ b/src/pkg/fmt/print.go @@ -22,6 +22,7 @@ var ( nilBytes = []byte("nil") mapBytes = []byte("map[") missingBytes = []byte("(MISSING)") + panicBytes = []byte("(PANIC=") extraBytes = []byte("%!(EXTRA ") irparenBytes = []byte("i)") bytesBytes = []byte("[]byte{") @@ -69,10 +70,11 @@ type GoStringer interface { } type pp struct { - n int - buf bytes.Buffer - runeBuf [utf8.UTFMax]byte - fmt fmt + n int + panicking bool + buf bytes.Buffer + runeBuf [utf8.UTFMax]byte + fmt fmt } // A cache holds a set of reusable objects. @@ -111,6 +113,7 @@ var ppFree = newCache(func() interface{} { return new(pp) }) // Allocate a new pp struct or grab a cached one. func newPrinter() *pp { p := ppFree.get().(*pp) + p.panicking = false p.fmt.init(&p.buf) return p } @@ -159,19 +162,18 @@ func (p *pp) Write(b []byte) (ret int, err os.Error) { // Fprintf formats according to a format specifier and writes to w. // It returns the number of bytes written and any write error encountered. -func Fprintf(w io.Writer, format string, a ...interface{}) (n int, error os.Error) { +func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err os.Error) { p := newPrinter() p.doPrintf(format, a) - n64, error := p.buf.WriteTo(w) + n64, err := p.buf.WriteTo(w) p.free() - return int(n64), error + return int(n64), err } // Printf formats according to a format specifier and writes to standard output. // It returns the number of bytes written and any write error encountered. -func Printf(format string, a ...interface{}) (n int, errno os.Error) { - n, errno = Fprintf(os.Stdout, format, a...) - return n, errno +func Printf(format string, a ...interface{}) (n int, err os.Error) { + return Fprintf(os.Stdout, format, a...) } // Sprintf formats according to a format specifier and returns the resulting string. @@ -186,7 +188,7 @@ func Sprintf(format string, a ...interface{}) string { // Errorf formats according to a format specifier and returns the string // converted to an os.ErrorString, which satisfies the os.Error interface. func Errorf(format string, a ...interface{}) os.Error { - return os.ErrorString(Sprintf(format, a...)) + return os.NewError(Sprintf(format, a...)) } // These routines do not take a format string @@ -194,20 +196,19 @@ func Errorf(format string, a ...interface{}) os.Error { // Fprint formats using the default formats for its operands and writes to w. // Spaces are added between operands when neither is a string. // It returns the number of bytes written and any write error encountered. -func Fprint(w io.Writer, a ...interface{}) (n int, error os.Error) { +func Fprint(w io.Writer, a ...interface{}) (n int, err os.Error) { p := newPrinter() p.doPrint(a, false, false) - n64, error := p.buf.WriteTo(w) + n64, err := p.buf.WriteTo(w) p.free() - return int(n64), error + return int(n64), err } // Print formats using the default formats for its operands and writes to standard output. // Spaces are added between operands when neither is a string. // It returns the number of bytes written and any write error encountered. -func Print(a ...interface{}) (n int, errno os.Error) { - n, errno = Fprint(os.Stdout, a...) - return n, errno +func Print(a ...interface{}) (n int, err os.Error) { + return Fprint(os.Stdout, a...) } // Sprint formats using the default formats for its operands and returns the resulting string. @@ -227,20 +228,19 @@ func Sprint(a ...interface{}) string { // Fprintln formats using the default formats for its operands and writes to w. // Spaces are always added between operands and a newline is appended. // It returns the number of bytes written and any write error encountered. -func Fprintln(w io.Writer, a ...interface{}) (n int, error os.Error) { +func Fprintln(w io.Writer, a ...interface{}) (n int, err os.Error) { p := newPrinter() p.doPrint(a, true, true) - n64, error := p.buf.WriteTo(w) + n64, err := p.buf.WriteTo(w) p.free() - return int(n64), error + return int(n64), err } // Println formats using the default formats for its operands and writes to standard output. // Spaces are always added between operands and a newline is appended. // It returns the number of bytes written and any write error encountered. -func Println(a ...interface{}) (n int, errno os.Error) { - n, errno = Fprintln(os.Stdout, a...) - return n, errno +func Println(a ...interface{}) (n int, err os.Error) { + return Fprintln(os.Stdout, a...) } // Sprintln formats using the default formats for its operands and returns the resulting string. @@ -363,6 +363,8 @@ func (p *pp) fmt0x64(v uint64, leading0x bool) { // temporarily turning on the unicode flag and tweaking the precision. func (p *pp) fmtUnicode(v int64) { precPresent := p.fmt.precPresent + sharp := p.fmt.sharp + p.fmt.sharp = false prec := p.fmt.prec if !precPresent { // If prec is already set, leave it alone; otherwise 4 is minimum. @@ -370,10 +372,13 @@ func (p *pp) fmtUnicode(v int64) { p.fmt.precPresent = true } p.fmt.unicode = true // turn on U+ + p.fmt.uniQuote = sharp p.fmt.integer(int64(v), 16, unsigned, udigits) p.fmt.unicode = false + p.fmt.uniQuote = false p.fmt.prec = prec p.fmt.precPresent = precPresent + p.fmt.sharp = sharp } func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) { @@ -561,6 +566,31 @@ var ( uintptrBits = reflect.TypeOf(uintptr(0)).Bits() ) +func (p *pp) catchPanic(val interface{}, verb int) { + if err := recover(); err != nil { + // If it's a nil pointer, just say "<nil>". The likeliest causes are a + // Stringer that fails to guard against nil or a nil pointer for a + // value receiver, and in either case, "<nil>" is a nice result. + if v := reflect.ValueOf(val); v.Kind() == reflect.Ptr && v.IsNil() { + p.buf.Write(nilAngleBytes) + return + } + // Otherwise print a concise panic message. Most of the time the panic + // value will print itself nicely. + if p.panicking { + // Nested panics; the recursion in printField cannot succeed. + panic(err) + } + p.buf.WriteByte('%') + p.add(verb) + p.buf.Write(panicBytes) + p.panicking = true + p.printField(err, 'v', false, false, 0) + p.panicking = false + p.buf.WriteByte(')') + } +} + func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth int) (wasString bool) { if field == nil { if verb == 'T' || verb == 'v' { @@ -583,6 +613,7 @@ func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth } // Is it a Formatter? if formatter, ok := field.(Formatter); ok { + defer p.catchPanic(field, verb) formatter.Format(p, verb) return false // this value is not a string @@ -595,6 +626,7 @@ func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth if goSyntax { p.fmt.sharp = false if stringer, ok := field.(GoStringer); ok { + defer p.catchPanic(field, verb) // Print the result of GoString unadorned. p.fmtString(stringer.GoString(), 's', false, field) return false // this value is not a string @@ -602,6 +634,7 @@ func (p *pp) printField(field interface{}, verb int, plus, goSyntax bool, depth } else { // Is it a Stringer? if stringer, ok := field.(Stringer); ok { + defer p.catchPanic(field, verb) p.printField(stringer.String(), verb, plus, false, depth) return false // this value is not a string } diff --git a/src/pkg/fmt/scan.go b/src/pkg/fmt/scan.go index dd8548ceb..d93a8c1da 100644 --- a/src/pkg/fmt/scan.go +++ b/src/pkg/fmt/scan.go @@ -35,6 +35,10 @@ type ScanState interface { ReadRune() (rune int, size int, err os.Error) // UnreadRune causes the next call to ReadRune to return the same rune. UnreadRune() os.Error + // SkipSpace skips space in the input. Newlines are treated as space + // unless the scan operation is Scanln, Fscanln or Sscanln, in which case + // a newline is treated as EOF. + SkipSpace() // Token skips space in the input if skipSpace is true, then returns the // run of Unicode code points c satisfying f(c). If f is nil, // !unicode.IsSpace(c) is used; that is, the token will hold non-space @@ -167,7 +171,7 @@ type ssave struct { // satisfies io.Reader. It will never be called when used as // intended, so there is no need to make it actually work. func (s *ss) Read(buf []byte) (n int, err os.Error) { - return 0, os.ErrorString("ScanState's Read should not be called. Use ReadRune") + return 0, os.NewError("ScanState's Read should not be called. Use ReadRune") } func (s *ss) ReadRune() (rune int, size int, err os.Error) { @@ -241,7 +245,7 @@ func (s *ss) error(err os.Error) { } func (s *ss) errorString(err string) { - panic(scanError{os.ErrorString(err)}) + panic(scanError{os.NewError(err)}) } func (s *ss) Token(skipSpace bool, f func(int) bool) (tok []byte, err os.Error) { @@ -267,6 +271,14 @@ func notSpace(r int) bool { return !unicode.IsSpace(r) } + +// skipSpace provides Scan() methods the ability to skip space and newline characters +// in keeping with the current scanning mode set by format strings and Scan()/Scanln(). +func (s *ss) SkipSpace() { + s.skipSpace(false) +} + + // readRune is a structure to enable reading UTF-8 encoded code points // from an io.Reader. It is used if the Reader given to the scanner does // not already implement io.RuneReader. @@ -427,8 +439,8 @@ func (s *ss) typeError(field interface{}, expected string) { s.errorString("expected field of type pointer to " + expected + "; found " + reflect.TypeOf(field).String()) } -var complexError = os.ErrorString("syntax error scanning complex number") -var boolError = os.ErrorString("syntax error scanning boolean") +var complexError = os.NewError("syntax error scanning complex number") +var boolError = os.NewError("syntax error scanning boolean") // consume reads the next rune in the input and reports whether it is in the ok string. // If accept is true, it puts the character into the input token. @@ -945,7 +957,7 @@ func (s *ss) scanOne(verb int, field interface{}) { // For now, can only handle (renamed) []byte. typ := v.Type() if typ.Elem().Kind() != reflect.Uint8 { - goto CantHandle + s.errorString("Scan: can't handle type: " + val.Type().String()) } str := s.convertString(verb) v.Set(reflect.MakeSlice(typ, len(str), len(str))) @@ -959,7 +971,6 @@ func (s *ss) scanOne(verb int, field interface{}) { case reflect.Complex64, reflect.Complex128: v.SetComplex(s.scanComplex(verb, v.Type().Bits())) default: - CantHandle: s.errorString("Scan: can't handle type: " + val.Type().String()) } } diff --git a/src/pkg/fmt/scan_test.go b/src/pkg/fmt/scan_test.go index a4de8adb1..98b3b5493 100644 --- a/src/pkg/fmt/scan_test.go +++ b/src/pkg/fmt/scan_test.go @@ -94,7 +94,7 @@ func (x *Xs) Scan(state ScanState, verb int) os.Error { } s := string(tok) if !regexp.MustCompile("^" + string(verb) + "+$").MatchString(s) { - return os.ErrorString("syntax error for xs") + return os.NewError("syntax error for xs") } *x = Xs(s) return nil @@ -818,7 +818,7 @@ func (r *RecursiveInt) Scan(state ScanState, verb int) (err os.Error) { next := new(RecursiveInt) _, err = Fscanf(state, ".%v", next) if err != nil { - if err == os.ErrorString("input does not match format") || err == io.ErrUnexpectedEOF { + if err == os.NewError("input does not match format") || err == io.ErrUnexpectedEOF { err = nil } return diff --git a/src/pkg/go/ast/print_test.go b/src/pkg/go/ast/print_test.go index 0820dcfce..30b396fcf 100644 --- a/src/pkg/go/ast/print_test.go +++ b/src/pkg/go/ast/print_test.go @@ -53,7 +53,7 @@ var tests = []struct { // Split s into lines, trim whitespace from all lines, and return // the concatenated non-empty lines. func trim(s string) string { - lines := strings.Split(s, "\n", -1) + lines := strings.Split(s, "\n") i := 0 for _, line := range lines { line = strings.TrimSpace(line) diff --git a/src/pkg/go/build/Makefile b/src/pkg/go/build/Makefile index 4411940ae..349e00e80 100644 --- a/src/pkg/go/build/Makefile +++ b/src/pkg/go/build/Makefile @@ -11,7 +11,7 @@ GOFILES=\ path.go\ syslist.go\ -CLEANFILES+=syslist.go +CLEANFILES+=syslist.go pkgtest/_obj cmdtest/_obj cgotest/_obj include ../../../Make.pkg diff --git a/src/pkg/go/build/build.go b/src/pkg/go/build/build.go index 3cb8efe47..d83a6666e 100644 --- a/src/pkg/go/build/build.go +++ b/src/pkg/go/build/build.go @@ -11,34 +11,61 @@ import ( "fmt" "os" "path/filepath" + "regexp" "runtime" "strings" ) -func (d *DirInfo) Build(targ string) ([]*Cmd, os.Error) { - b := &build{obj: "_obj/"} +// Build produces a build Script for the given package. +func Build(tree *Tree, pkg string, info *DirInfo) (*Script, os.Error) { + s := &Script{} + b := &build{ + script: s, + path: filepath.Join(tree.SrcDir(), pkg), + } + b.obj = b.abs("_obj") + string(filepath.Separator) - goarch := runtime.GOARCH + b.goarch = runtime.GOARCH if g := os.Getenv("GOARCH"); g != "" { - goarch = g + b.goarch = g } var err os.Error - b.arch, err = ArchChar(goarch) + b.arch, err = ArchChar(b.goarch) if err != nil { return nil, err } - var gofiles = d.GoFiles // .go files to be built with gc - var ofiles []string // *.GOARCH files to be linked or packed + // add import object files to list of Inputs + for _, pkg := range info.Imports { + t, p, err := FindTree(pkg) + if err != nil && err != ErrNotFound { + // FindTree should always be able to suggest an import + // path and tree. The path must be malformed + // (for example, an absolute or relative path). + return nil, os.NewError("build: invalid import: " + pkg) + } + s.addInput(filepath.Join(t.PkgDir(), p+".a")) + } + + // .go files to be built with gc + gofiles := b.abss(info.GoFiles...) + s.addInput(gofiles...) + + var ofiles []string // object files to be linked or packed // make build directory b.mkdir(b.obj) + s.addIntermediate(b.obj) // cgo - if len(d.CgoFiles) > 0 { - outGo, outObj := b.cgo(d.CgoFiles) + if len(info.CgoFiles) > 0 { + cgoFiles := b.abss(info.CgoFiles...) + s.addInput(cgoFiles...) + outGo, outObj := b.cgo(cgoFiles) gofiles = append(gofiles, outGo...) ofiles = append(ofiles, outObj...) + s.addIntermediate(outGo...) + s.addIntermediate(outObj...) } // compile @@ -46,31 +73,136 @@ func (d *DirInfo) Build(targ string) ([]*Cmd, os.Error) { ofile := b.obj + "_go_." + b.arch b.gc(ofile, gofiles...) ofiles = append(ofiles, ofile) + s.addIntermediate(ofile) } // assemble - for _, sfile := range d.SFiles { + for _, sfile := range info.SFiles { ofile := b.obj + sfile[:len(sfile)-1] + b.arch + sfile = b.abs(sfile) + s.addInput(sfile) b.asm(ofile, sfile) ofiles = append(ofiles, ofile) + s.addIntermediate(ofile) } if len(ofiles) == 0 { return nil, os.NewError("make: no object files to build") } - if d.IsCommand() { + // choose target file + var targ string + if info.IsCommand() { + // use the last part of the import path as binary name + _, bin := filepath.Split(pkg) + if runtime.GOOS == "windows" { + bin += ".exe" + } + targ = filepath.Join(tree.BinDir(), bin) + } else { + targ = filepath.Join(tree.PkgDir(), pkg+".a") + } + + // make target directory + targDir, _ := filepath.Split(targ) + b.mkdir(targDir) + + // link binary or pack object + if info.IsCommand() { b.ld(targ, ofiles...) } else { b.gopack(targ, ofiles...) } + s.Output = append(s.Output, targ) + + return b.script, nil +} + +// A Script describes the build process for a Go package. +// The Input, Intermediate, and Output fields are lists of absolute paths. +type Script struct { + Cmd []*Cmd + Input []string + Intermediate []string + Output []string +} + +func (s *Script) addInput(file ...string) { + s.Input = append(s.Input, file...) +} + +func (s *Script) addIntermediate(file ...string) { + s.Intermediate = append(s.Intermediate, file...) +} + +// Run runs the Script's Cmds in order. +func (s *Script) Run() os.Error { + for _, c := range s.Cmd { + if err := c.Run(); err != nil { + return err + } + } + return nil +} + +// Stale returns true if the build's inputs are newer than its outputs. +func (s *Script) Stale() bool { + var latest int64 + // get latest mtime of outputs + for _, file := range s.Output { + fi, err := os.Stat(file) + if err != nil { + // any error reading output files means stale + return true + } + if m := fi.Mtime_ns; m > latest { + latest = m + } + } + for _, file := range s.Input { + fi, err := os.Stat(file) + if err != nil || fi.Mtime_ns > latest { + // any error reading input files means stale + // (attempt to rebuild to figure out why) + return true + } + } + return false +} + +// Clean removes the Script's Intermediate files. +// It tries to remove every file and returns the first error it encounters. +func (s *Script) Clean() (err os.Error) { + // Reverse order so that directories get removed after the files they contain. + for i := len(s.Intermediate) - 1; i >= 0; i-- { + if e := os.Remove(s.Intermediate[i]); err == nil { + err = e + } + } + return +} - return b.cmds, nil +// Clean removes the Script's Intermediate and Output files. +// It tries to remove every file and returns the first error it encounters. +func (s *Script) Nuke() (err os.Error) { + // Reverse order so that directories get removed after the files they contain. + for i := len(s.Output) - 1; i >= 0; i-- { + if e := os.Remove(s.Output[i]); err == nil { + err = e + } + } + if e := s.Clean(); err == nil { + err = e + } + return } +// A Cmd describes an individual build command. type Cmd struct { Args []string // command-line Stdout string // write standard output to this file, "" is passthrough + Dir string // working directory + Env []string // environment Input []string // file paths (dependencies) Output []string // file paths } @@ -79,14 +211,16 @@ func (c *Cmd) String() string { return strings.Join(c.Args, " ") } -func (c *Cmd) Run(dir string) os.Error { +// Run executes the Cmd. +func (c *Cmd) Run() os.Error { out := new(bytes.Buffer) cmd := exec.Command(c.Args[0], c.Args[1:]...) - cmd.Dir = dir + cmd.Dir = c.Dir + cmd.Env = c.Env cmd.Stdout = out cmd.Stderr = out if c.Stdout != "" { - f, err := os.Create(filepath.Join(dir, c.Stdout)) + f, err := os.Create(c.Stdout) if err != nil { return err } @@ -99,15 +233,6 @@ func (c *Cmd) Run(dir string) os.Error { return nil } -func (c *Cmd) Clean(dir string) (err os.Error) { - for _, fn := range c.Output { - if e := os.RemoveAll(fn); err == nil { - err = e - } - } - return -} - // ArchChar returns the architecture character for the given goarch. // For example, ArchChar("amd64") returns "6". func ArchChar(goarch string) (string, os.Error) { @@ -123,13 +248,30 @@ func ArchChar(goarch string) (string, os.Error) { } type build struct { - cmds []*Cmd - obj string - arch string + script *Script + path string + obj string + goarch string + arch string +} + +func (b *build) abs(file string) string { + if filepath.IsAbs(file) { + return file + } + return filepath.Join(b.path, file) +} + +func (b *build) abss(file ...string) []string { + s := make([]string, len(file)) + for i, f := range file { + s[i] = b.abs(f) + } + return s } func (b *build) add(c Cmd) { - b.cmds = append(b.cmds, &c) + b.script.Cmd = append(b.script.Cmd, &c) } func (b *build) mkdir(name string) { @@ -192,7 +334,7 @@ func (b *build) cc(ofile string, cfiles ...string) { func (b *build) gccCompile(ofile, cfile string) { b.add(Cmd{ - Args: gccArgs(b.arch, "-o", ofile, "-c", cfile), + Args: b.gccArgs("-o", ofile, "-c", cfile), Input: []string{cfile}, Output: []string{ofile}, }) @@ -200,21 +342,26 @@ func (b *build) gccCompile(ofile, cfile string) { func (b *build) gccLink(ofile string, ofiles ...string) { b.add(Cmd{ - Args: append(gccArgs(b.arch, "-o", ofile), ofiles...), + Args: append(b.gccArgs("-o", ofile), ofiles...), Input: ofiles, Output: []string{ofile}, }) } -func gccArgs(arch string, args ...string) []string { +func (b *build) gccArgs(args ...string) []string { // TODO(adg): HOST_CC - m := "-m32" - if arch == "6" { - m = "-m64" + a := []string{"gcc", "-I", b.path, "-g", "-fPIC", "-O2"} + switch b.arch { + case "8": + a = append(a, "-m32") + case "6": + a = append(a, "-m64") } - return append([]string{"gcc", m, "-I", ".", "-g", "-fPIC", "-O2"}, args...) + return append(a, args...) } +var cgoRe = regexp.MustCompile(`[/\\:]`) + func (b *build) cgo(cgofiles []string) (outGo, outObj []string) { // cgo // TODO(adg): CGOPKGPATH @@ -222,19 +369,24 @@ func (b *build) cgo(cgofiles []string) (outGo, outObj []string) { gofiles := []string{b.obj + "_cgo_gotypes.go"} cfiles := []string{b.obj + "_cgo_main.c", b.obj + "_cgo_export.c"} for _, fn := range cgofiles { - f := b.obj + fn[:len(fn)-2] + f := b.obj + cgoRe.ReplaceAllString(fn[:len(fn)-2], "_") gofiles = append(gofiles, f+"cgo1.go") cfiles = append(cfiles, f+"cgo2.c") } defunC := b.obj + "_cgo_defun.c" - output := append([]string{defunC}, gofiles...) - output = append(output, cfiles...) + output := append([]string{defunC}, cfiles...) + output = append(output, gofiles...) b.add(Cmd{ Args: append([]string{"cgo", "--"}, cgofiles...), + Dir: b.path, + Env: append(os.Environ(), "GOARCH="+b.goarch), Input: cgofiles, Output: output, }) outGo = append(outGo, gofiles...) + exportH := filepath.Join(b.path, "_cgo_export.h") + b.script.addIntermediate(defunC, exportH, b.obj+"_cgo_flags") + b.script.addIntermediate(cfiles...) // cc _cgo_defun.c defunObj := b.obj + "_cgo_defun." + b.arch @@ -249,10 +401,13 @@ func (b *build) cgo(cgofiles []string) (outGo, outObj []string) { linkobj = append(linkobj, ofile) if !strings.HasSuffix(ofile, "_cgo_main.o") { outObj = append(outObj, ofile) + } else { + b.script.addIntermediate(ofile) } } - dynObj := b.obj + "_cgo1_.o" + dynObj := b.obj + "_cgo_.o" b.gccLink(dynObj, linkobj...) + b.script.addIntermediate(dynObj) // cgo -dynimport importC := b.obj + "_cgo_import.c" @@ -262,6 +417,7 @@ func (b *build) cgo(cgofiles []string) (outGo, outObj []string) { Input: []string{dynObj}, Output: []string{importC}, }) + b.script.addIntermediate(importC) // cc _cgo_import.ARCH importObj := b.obj + "_cgo_import." + b.arch diff --git a/src/pkg/go/build/build_test.go b/src/pkg/go/build/build_test.go index c543eddbd..e59d87672 100644 --- a/src/pkg/go/build/build_test.go +++ b/src/pkg/go/build/build_test.go @@ -5,53 +5,57 @@ package build import ( - "os" + "exec" "path/filepath" - "runtime" - "strings" "testing" ) -var buildDirs = []string{ - "pkg/path", - "cmd/gofix", - "pkg/big", - "pkg/go/build/cgotest", +var buildPkgs = []string{ + "go/build/pkgtest", + "go/build/cmdtest", + "go/build/cgotest", } +const cmdtestOutput = "3" + func TestBuild(t *testing.T) { - out, err := filepath.Abs("_test/out") - if err != nil { - t.Fatal(err) - } - for _, d := range buildDirs { - if runtime.GOARCH == "arm" && strings.Contains(d, "/cgo") { - // no cgo for arm, yet. + for _, pkg := range buildPkgs { + tree := Path[0] // Goroot + dir := filepath.Join(tree.SrcDir(), pkg) + + info, err := ScanDir(dir, true) + if err != nil { + t.Error("ScanDir:", err) continue } - dir := filepath.Join(runtime.GOROOT(), "src", d) - testBuild(t, dir, out) - } -} -func testBuild(t *testing.T, dir, targ string) { - d, err := ScanDir(dir, true) - if err != nil { - t.Error(err) - return - } - defer os.Remove(targ) - cmds, err := d.Build(targ) - if err != nil { - t.Error(err) - return - } - for _, c := range cmds { - t.Log("Run:", c) - err = c.Run(dir) + s, err := Build(tree, pkg, info) if err != nil { - t.Error(c, err) - return + t.Error("Build:", err) + continue + } + + if err := s.Run(); err != nil { + t.Error("Run:", err) + continue } + + if pkg == "go/build/cmdtest" { + bin := s.Output[0] + b, err := exec.Command(bin).CombinedOutput() + if err != nil { + t.Errorf("exec: %s: %v", bin, err) + continue + } + if string(b) != cmdtestOutput { + t.Errorf("cmdtest output: %s want: %s", b, cmdtestOutput) + } + } + + defer func(s *Script) { + if err := s.Nuke(); err != nil { + t.Errorf("nuking: %v", err) + } + }(s) } } diff --git a/src/pkg/go/build/cgotest/cgotest.go b/src/pkg/go/build/cgotest/cgotest.go new file mode 100644 index 000000000..32b931861 --- /dev/null +++ b/src/pkg/go/build/cgotest/cgotest.go @@ -0,0 +1,12 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +/* +char* greeting = "hello, world"; +*/ +import "C" + +var Greeting = C.GoString(C.greeting) diff --git a/src/pkg/go/build/cgotest/file.go b/src/pkg/go/build/cgotest/file.go deleted file mode 100644 index 3b2a2e7d9..000000000 --- a/src/pkg/go/build/cgotest/file.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -A trivial example of wrapping a C library in Go. -For a more complex example and explanation, -see ../gmp/gmp.go. -*/ - -package stdio - -/* -#include <stdio.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <errno.h> - -char* greeting = "hello, world"; -*/ -import "C" -import "unsafe" - -type File C.FILE - -// TODO(brainman): uncomment once stdout and stderr references are working on Windows. -//var Stdout = (*File)(C.stdout) -//var Stderr = (*File)(C.stderr) - -// Test reference to library symbol. -// Stdout and stderr are too special to be a reliable test. -var myerr = C.sys_errlist - -func (f *File) WriteString(s string) { - p := C.CString(s) - C.fputs(p, (*C.FILE)(f)) - C.free(unsafe.Pointer(p)) - f.Flush() -} - -func (f *File) Flush() { - C.fflush((*C.FILE)(f)) -} - -var Greeting = C.GoString(C.greeting) diff --git a/src/pkg/go/build/cmdtest/main.go b/src/pkg/go/build/cmdtest/main.go new file mode 100644 index 000000000..bed4f485a --- /dev/null +++ b/src/pkg/go/build/cmdtest/main.go @@ -0,0 +1,12 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "go/build/pkgtest" + +func main() { + pkgtest.Foo() + print(int(pkgtest.Sqrt(9))) +} diff --git a/src/pkg/go/build/dir.go b/src/pkg/go/build/dir.go index 77e80bff0..e0000b534 100644 --- a/src/pkg/go/build/dir.go +++ b/src/pkg/go/build/dir.go @@ -50,7 +50,6 @@ func ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) { var di DirInfo imported := make(map[string]bool) - pkgName := "" fset := token.NewFileSet() for i := range dirs { d := &dirs[i] @@ -89,17 +88,17 @@ func ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) { if s == "documentation" { continue } - if pkgName == "" { - pkgName = s - } else if pkgName != s { + if di.PkgName == "" { + di.PkgName = s + } else if di.PkgName != s { // Only if all files in the directory are in package main - // do we return pkgName=="main". + // do we return PkgName=="main". // A mix of main and another package reverts // to the original (allowMain=false) behaviour. - if s == "main" || pkgName == "main" { + if s == "main" || di.PkgName == "main" { return ScanDir(dir, false) } - return nil, os.ErrorString("multiple package names in " + dir) + return nil, os.NewError("multiple package names in " + dir) } isCgo := false for _, spec := range pf.Imports { @@ -140,7 +139,7 @@ func goodOSArch(filename string) bool { if dot := strings.Index(filename, "."); dot != -1 { filename = filename[:dot] } - l := strings.Split(filename, "_", -1) + l := strings.Split(filename, "_") n := len(l) if n == 0 { return true diff --git a/src/pkg/go/build/path.go b/src/pkg/go/build/path.go index 8ad39fb0f..ea588abbd 100644 --- a/src/pkg/go/build/path.go +++ b/src/pkg/go/build/path.go @@ -88,6 +88,9 @@ func FindTree(path string) (tree *Tree, pkg string, err os.Error) { if path, err = filepath.Abs(path); err != nil { return } + if path, err = filepath.EvalSymlinks(path); err != nil { + return + } for _, t := range Path { tpath := t.SrcDir() + string(filepath.Separator) if !strings.HasPrefix(path, tpath) { diff --git a/src/pkg/go/build/pkgtest/pkgtest.go b/src/pkg/go/build/pkgtest/pkgtest.go new file mode 100644 index 000000000..9322f5ebd --- /dev/null +++ b/src/pkg/go/build/pkgtest/pkgtest.go @@ -0,0 +1,9 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pkgtest + +func Foo() {} + +func Sqrt(x float64) float64 diff --git a/src/pkg/go/build/pkgtest/sqrt_386.s b/src/pkg/go/build/pkgtest/sqrt_386.s new file mode 100644 index 000000000..d0a428d52 --- /dev/null +++ b/src/pkg/go/build/pkgtest/sqrt_386.s @@ -0,0 +1,10 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// func Sqrt(x float64) float64 +TEXT ·Sqrt(SB),7,$0 + FMOVD x+0(FP),F0 + FSQRT + FMOVDP F0,r+8(FP) + RET diff --git a/src/pkg/go/build/pkgtest/sqrt_amd64.s b/src/pkg/go/build/pkgtest/sqrt_amd64.s new file mode 100644 index 000000000..f5b329e70 --- /dev/null +++ b/src/pkg/go/build/pkgtest/sqrt_amd64.s @@ -0,0 +1,9 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// func Sqrt(x float64) float64 +TEXT ·Sqrt(SB),7,$0 + SQRTSD x+0(FP), X0 + MOVSD X0, r+8(FP) + RET diff --git a/src/pkg/go/build/pkgtest/sqrt_arm.s b/src/pkg/go/build/pkgtest/sqrt_arm.s new file mode 100644 index 000000000..befbb8a89 --- /dev/null +++ b/src/pkg/go/build/pkgtest/sqrt_arm.s @@ -0,0 +1,10 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// func Sqrt(x float64) float64 +TEXT ·Sqrt(SB),7,$0 + MOVD x+0(FP),F0 + SQRTD F0,F0 + MOVD F0,r+8(FP) + RET diff --git a/src/pkg/go/doc/comment.go b/src/pkg/go/doc/comment.go index f1ebfa97b..85640af79 100644 --- a/src/pkg/go/doc/comment.go +++ b/src/pkg/go/doc/comment.go @@ -58,7 +58,7 @@ func CommentText(comment *ast.CommentGroup) string { } // Split on newlines. - cl := strings.Split(c, "\n", -1) + cl := strings.Split(c, "\n") // Walk lines, stripping trailing white space and adding to list. for _, l := range cl { diff --git a/src/pkg/go/doc/doc.go b/src/pkg/go/doc/doc.go index a7a7e0a32..b26cd2bed 100644 --- a/src/pkg/go/doc/doc.go +++ b/src/pkg/go/doc/doc.go @@ -551,7 +551,7 @@ func (doc *docReader) newDoc(importpath string, filenames []string) *PackageDoc p := new(PackageDoc) p.PackageName = doc.pkgName p.ImportPath = importpath - sort.SortStrings(filenames) + sort.Strings(filenames) p.Filenames = filenames p.Doc = CommentText(doc.doc) // makeTypeDocs may extend the list of doc.values and diff --git a/src/pkg/go/parser/interface.go b/src/pkg/go/parser/interface.go index b4780e057..1764c38e4 100644 --- a/src/pkg/go/parser/interface.go +++ b/src/pkg/go/parser/interface.go @@ -42,7 +42,7 @@ func readSource(filename string, src interface{}) ([]byte, os.Error) { } return buf.Bytes(), nil default: - return nil, os.ErrorString("invalid source") + return nil, os.NewError("invalid source") } } diff --git a/src/pkg/go/printer/nodes.go b/src/pkg/go/printer/nodes.go index 0fca8a161..f2b79d810 100644 --- a/src/pkg/go/printer/nodes.go +++ b/src/pkg/go/printer/nodes.go @@ -1158,8 +1158,14 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) { case *ast.SelectStmt: p.print(token.SELECT, blank) - p.block(s.Body, 0) - *multiLine = true + body := s.Body + if len(body.List) == 0 && !p.commentBefore(p.fset.Position(body.Rbrace)) { + // print empty select statement w/o comments on one line + p.print(body.Lbrace, token.LBRACE, body.Rbrace, token.RBRACE) + } else { + p.block(body, 0) + *multiLine = true + } case *ast.ForStmt: p.print(token.FOR) diff --git a/src/pkg/go/printer/testdata/statements.golden b/src/pkg/go/printer/testdata/statements.golden index 290060269..0e4840441 100644 --- a/src/pkg/go/printer/testdata/statements.golden +++ b/src/pkg/go/printer/testdata/statements.golden @@ -111,6 +111,21 @@ func _() { } +// Formatting of selected select statements. +func _() { + select {} + select { /* this comment should not be tab-aligned because the closing } is on the same line */ + } + select { /* this comment should be tab-aligned */ + } + select { // this comment should be tab-aligned + } + select { + case <-c: + } +} + + // Formatting of for-statement headers. func _() { for { diff --git a/src/pkg/go/printer/testdata/statements.input b/src/pkg/go/printer/testdata/statements.input index 21e61efc4..86a753c5a 100644 --- a/src/pkg/go/printer/testdata/statements.input +++ b/src/pkg/go/printer/testdata/statements.input @@ -91,6 +91,19 @@ func _() { } +// Formatting of selected select statements. +func _() { + select { + } + select { /* this comment should not be tab-aligned because the closing } is on the same line */ } + select { /* this comment should be tab-aligned */ + } + select { // this comment should be tab-aligned + } + select { case <-c: } +} + + // Formatting of for-statement headers. func _() { for{} diff --git a/src/pkg/go/scanner/scanner.go b/src/pkg/go/scanner/scanner.go index 509abeca5..795f0ac15 100644 --- a/src/pkg/go/scanner/scanner.go +++ b/src/pkg/go/scanner/scanner.go @@ -22,6 +22,7 @@ package scanner import ( "bytes" + "fmt" "go/token" "path/filepath" "strconv" @@ -674,7 +675,7 @@ scanAgain: tok = S.switch3(token.OR, token.OR_ASSIGN, '|', token.LOR) default: if S.mode&AllowIllegalChars == 0 { - S.error(offs, "illegal character "+strconv.QuoteRune(ch)) + S.error(offs, fmt.Sprintf("illegal character %#U", ch)) } insertSemi = S.insertSemi // preserve insertSemi info } diff --git a/src/pkg/go/scanner/scanner_test.go b/src/pkg/go/scanner/scanner_test.go index ee1e830a1..c096e2725 100644 --- a/src/pkg/go/scanner/scanner_test.go +++ b/src/pkg/go/scanner/scanner_test.go @@ -650,9 +650,9 @@ var errors = []struct { pos int err string }{ - {"\a", token.ILLEGAL, 0, "illegal character '\\a'"}, - {`#`, token.ILLEGAL, 0, "illegal character '#'"}, - {`…`, token.ILLEGAL, 0, "illegal character '…'"}, + {"\a", token.ILLEGAL, 0, "illegal character U+0007"}, + {`#`, token.ILLEGAL, 0, "illegal character U+0023 '#'"}, + {`…`, token.ILLEGAL, 0, "illegal character U+2026 '…'"}, {`' '`, token.CHAR, 0, ""}, {`''`, token.CHAR, 0, "illegal character literal"}, {`'\8'`, token.CHAR, 2, "unknown escape sequence"}, diff --git a/src/pkg/go/types/exportdata.go b/src/pkg/go/types/exportdata.go index cb08ffe18..f68133761 100644 --- a/src/pkg/go/types/exportdata.go +++ b/src/pkg/go/types/exportdata.go @@ -29,7 +29,7 @@ func readGopackHeader(buf *bufio.Reader) (name string, size int, err os.Error) { s := strings.TrimSpace(string(hdr[64+12+6+6+8:][:10])) size, err = strconv.Atoi(s) if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' { - err = os.ErrorString("invalid archive header") + err = os.NewError("invalid archive header") return } name = strings.TrimSpace(string(hdr[:64])) @@ -80,7 +80,7 @@ func ExportData(filename string) (rc io.ReadCloser, err os.Error) { return } if name != "__.SYMDEF" { - err = os.ErrorString("go archive does not begin with __.SYMDEF") + err = os.NewError("go archive does not begin with __.SYMDEF") return } const block = 4096 @@ -102,7 +102,7 @@ func ExportData(filename string) (rc io.ReadCloser, err os.Error) { return } if name != "__.PKGDEF" { - err = os.ErrorString("go archive is missing __.PKGDEF") + err = os.NewError("go archive is missing __.PKGDEF") return } @@ -117,7 +117,7 @@ func ExportData(filename string) (rc io.ReadCloser, err os.Error) { // Now at __.PKGDEF in archive or still at beginning of file. // Either way, line should begin with "go object ". if !strings.HasPrefix(string(line), "go object ") { - err = os.ErrorString("not a go object file") + err = os.NewError("not a go object file") return } diff --git a/src/pkg/go/types/gcimporter.go b/src/pkg/go/types/gcimporter.go index 2cfed7726..aa0bb9160 100644 --- a/src/pkg/go/types/gcimporter.go +++ b/src/pkg/go/types/gcimporter.go @@ -124,7 +124,7 @@ func GcImporter(imports map[string]*ast.Object, path string) (pkg *ast.Object, e filename, id := findPkg(path) if filename == "" { - err = os.ErrorString("can't find import: " + id) + err = os.NewError("can't find import: " + id) return } @@ -166,7 +166,7 @@ func (e importError) String() string { func (p *gcParser) error(err interface{}) { if s, ok := err.(string); ok { - err = os.ErrorString(s) + err = os.NewError(s) } // panic with a runtime.Error if err is not an os.Error panic(importError{p.scanner.Pos(), err.(os.Error)}) diff --git a/src/pkg/go/types/testdata/exports.go b/src/pkg/go/types/testdata/exports.go index 1de2e00ad..035a13fb7 100644 --- a/src/pkg/go/types/testdata/exports.go +++ b/src/pkg/go/types/testdata/exports.go @@ -38,7 +38,7 @@ type ( T9 struct { a int b, c float32 - d []string "tag" + d []string `go:"tag"` } T10 struct { T8 diff --git a/src/pkg/gob/codec_test.go b/src/pkg/gob/codec_test.go index 8961336cd..da8e59c74 100644 --- a/src/pkg/gob/codec_test.go +++ b/src/pkg/gob/codec_test.go @@ -330,7 +330,7 @@ func newDecodeStateFromData(data []byte) *decoderState { // Test instruction execution for decoding. // Do not run the machine yet; instead do individual instructions crafted by hand. func TestScalarDecInstructions(t *testing.T) { - ovfl := os.ErrorString("overflow") + ovfl := os.NewError("overflow") // bool { diff --git a/src/pkg/gob/decode.go b/src/pkg/gob/decode.go index 381d44c05..bf7cb95f2 100644 --- a/src/pkg/gob/decode.go +++ b/src/pkg/gob/decode.go @@ -17,9 +17,9 @@ import ( ) var ( - errBadUint = os.ErrorString("gob: encoded unsigned integer out of range") - errBadType = os.ErrorString("gob: unknown type id or corrupted data") - errRange = os.ErrorString("gob: bad data: field numbers out of bounds") + errBadUint = os.NewError("gob: encoded unsigned integer out of range") + errBadType = os.NewError("gob: unknown type id or corrupted data") + errRange = os.NewError("gob: bad data: field numbers out of bounds") ) // decoderState is the execution state of an instance of the decoder. A new state @@ -54,8 +54,8 @@ func (dec *Decoder) freeDecoderState(d *decoderState) { dec.freeList = d } -func overflow(name string) os.ErrorString { - return os.ErrorString(`value for "` + name + `" out of range`) +func overflow(name string) os.Error { + return os.NewError(`value for "` + name + `" out of range`) } // decodeUintReader reads an encoded unsigned integer from an io.Reader. @@ -135,10 +135,10 @@ type decOp func(i *decInstr, state *decoderState, p unsafe.Pointer) // The 'instructions' of the decoding machine type decInstr struct { op decOp - field int // field number of the wire type - indir int // how many pointer indirections to reach the value in the struct - offset uintptr // offset in the structure of the field to encode - ovfl os.ErrorString // error message for overflow/underflow (for arrays, of the elements) + field int // field number of the wire type + indir int // how many pointer indirections to reach the value in the struct + offset uintptr // offset in the structure of the field to encode + ovfl os.Error // error message for overflow/underflow (for arrays, of the elements) } // Since the encoder writes no zeros, if we arrive at a decoder we have @@ -367,7 +367,7 @@ func decComplex64(i *decInstr, state *decoderState, p unsafe.Pointer) { p = *(*unsafe.Pointer)(p) } storeFloat32(i, state, p) - storeFloat32(i, state, unsafe.Pointer(uintptr(p)+uintptr(unsafe.Sizeof(float32(0))))) + storeFloat32(i, state, unsafe.Pointer(uintptr(p)+unsafe.Sizeof(float32(0)))) } // decComplex128 decodes a pair of unsigned integers, treats them as a @@ -552,7 +552,7 @@ func (dec *Decoder) ignoreSingle(engine *decEngine) { } // decodeArrayHelper does the work for decoding arrays and slices. -func (dec *Decoder) decodeArrayHelper(state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, length, elemIndir int, ovfl os.ErrorString) { +func (dec *Decoder) decodeArrayHelper(state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, length, elemIndir int, ovfl os.Error) { instr := &decInstr{elemOp, 0, elemIndir, 0, ovfl} for i := 0; i < length; i++ { up := unsafe.Pointer(p) @@ -567,7 +567,7 @@ func (dec *Decoder) decodeArrayHelper(state *decoderState, p uintptr, elemOp dec // decodeArray decodes an array and stores it through p, that is, p points to the zeroth element. // The length is an unsigned integer preceding the elements. Even though the length is redundant // (it's part of the type), it's a useful check and is included in the encoding. -func (dec *Decoder) decodeArray(atyp reflect.Type, state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, length, indir, elemIndir int, ovfl os.ErrorString) { +func (dec *Decoder) decodeArray(atyp reflect.Type, state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, length, indir, elemIndir int, ovfl os.Error) { if indir > 0 { p = allocate(atyp, p, 1) // All but the last level has been allocated by dec.Indirect } @@ -579,7 +579,7 @@ func (dec *Decoder) decodeArray(atyp reflect.Type, state *decoderState, p uintpt // decodeIntoValue is a helper for map decoding. Since maps are decoded using reflection, // unlike the other items we can't use a pointer directly. -func decodeIntoValue(state *decoderState, op decOp, indir int, v reflect.Value, ovfl os.ErrorString) reflect.Value { +func decodeIntoValue(state *decoderState, op decOp, indir int, v reflect.Value, ovfl os.Error) reflect.Value { instr := &decInstr{op, 0, indir, 0, ovfl} up := unsafe.Pointer(unsafeAddr(v)) if indir > 1 { @@ -593,7 +593,7 @@ func decodeIntoValue(state *decoderState, op decOp, indir int, v reflect.Value, // Maps are encoded as a length followed by key:value pairs. // Because the internals of maps are not visible to us, we must // use reflection rather than pointer magic. -func (dec *Decoder) decodeMap(mtyp reflect.Type, state *decoderState, p uintptr, keyOp, elemOp decOp, indir, keyIndir, elemIndir int, ovfl os.ErrorString) { +func (dec *Decoder) decodeMap(mtyp reflect.Type, state *decoderState, p uintptr, keyOp, elemOp decOp, indir, keyIndir, elemIndir int, ovfl os.Error) { if indir > 0 { p = allocate(mtyp, p, 1) // All but the last level has been allocated by dec.Indirect } @@ -616,7 +616,7 @@ func (dec *Decoder) decodeMap(mtyp reflect.Type, state *decoderState, p uintptr, // ignoreArrayHelper does the work for discarding arrays and slices. func (dec *Decoder) ignoreArrayHelper(state *decoderState, elemOp decOp, length int) { - instr := &decInstr{elemOp, 0, 0, 0, os.ErrorString("no error")} + instr := &decInstr{elemOp, 0, 0, 0, os.NewError("no error")} for i := 0; i < length; i++ { elemOp(instr, state, nil) } @@ -633,8 +633,8 @@ func (dec *Decoder) ignoreArray(state *decoderState, elemOp decOp, length int) { // ignoreMap discards the data for a map value with no destination. func (dec *Decoder) ignoreMap(state *decoderState, keyOp, elemOp decOp) { n := int(state.decodeUint()) - keyInstr := &decInstr{keyOp, 0, 0, 0, os.ErrorString("no error")} - elemInstr := &decInstr{elemOp, 0, 0, 0, os.ErrorString("no error")} + keyInstr := &decInstr{keyOp, 0, 0, 0, os.NewError("no error")} + elemInstr := &decInstr{elemOp, 0, 0, 0, os.NewError("no error")} for i := 0; i < n; i++ { keyOp(keyInstr, state, nil) elemOp(elemInstr, state, nil) @@ -643,7 +643,7 @@ func (dec *Decoder) ignoreMap(state *decoderState, keyOp, elemOp decOp) { // decodeSlice decodes a slice and stores the slice header through p. // Slices are encoded as an unsigned length followed by the elements. -func (dec *Decoder) decodeSlice(atyp reflect.Type, state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, indir, elemIndir int, ovfl os.ErrorString) { +func (dec *Decoder) decodeSlice(atyp reflect.Type, state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, indir, elemIndir int, ovfl os.Error) { n := int(uintptr(state.decodeUint())) if indir > 0 { up := unsafe.Pointer(p) @@ -741,7 +741,7 @@ func (dec *Decoder) ignoreInterface(state *decoderState) { // decodeGobDecoder decodes something implementing the GobDecoder interface. // The data is encoded as a byte slice. -func (dec *Decoder) decodeGobDecoder(state *decoderState, v reflect.Value, index int) { +func (dec *Decoder) decodeGobDecoder(state *decoderState, v reflect.Value) { // Read the bytes for the value. b := make([]byte, state.decodeUint()) _, err := state.b.Read(b) @@ -969,7 +969,7 @@ func (dec *Decoder) gobDecodeOpFor(ut *userTypeInfo) (*decOp, int) { } else { v = reflect.ValueOf(unsafe.Unreflect(rcvrType, p)) } - state.dec.decodeGobDecoder(state, v, methodIndex(rcvrType, gobDecodeMethodName)) + state.dec.decodeGobDecoder(state, v) } return &op, int(ut.indir) @@ -1064,10 +1064,10 @@ func (dec *Decoder) compileSingle(remoteId typeId, ut *userTypeInfo) (engine *de engine.instr = make([]decInstr, 1) // one item name := rt.String() // best we can do if !dec.compatibleType(rt, remoteId, make(map[reflect.Type]typeId)) { - return nil, os.ErrorString("gob: wrong type received for local value " + name + ": " + dec.typeString(remoteId)) + return nil, os.NewError("gob: wrong type received for local value " + name + ": " + dec.typeString(remoteId)) } op, indir := dec.decOpFor(remoteId, rt, name, make(map[reflect.Type]*decOp)) - ovfl := os.ErrorString(`value for "` + name + `" out of range`) + ovfl := os.NewError(`value for "` + name + `" out of range`) engine.instr[singletonField] = decInstr{*op, singletonField, indir, 0, ovfl} engine.numInstr = 1 return diff --git a/src/pkg/gob/decoder.go b/src/pkg/gob/decoder.go index b83904a71..281947132 100644 --- a/src/pkg/gob/decoder.go +++ b/src/pkg/gob/decoder.go @@ -44,7 +44,7 @@ func NewDecoder(r io.Reader) *Decoder { func (dec *Decoder) recvType(id typeId) { // Have we already seen this type? That's an error if id < firstUserId || dec.wireType[id] != nil { - dec.err = os.ErrorString("gob: duplicate type received") + dec.err = os.NewError("gob: duplicate type received") return } @@ -143,7 +143,7 @@ func (dec *Decoder) decodeTypeSequence(isInterface bool) typeId { // will be absorbed by recvMessage.) if dec.buf.Len() > 0 { if !isInterface { - dec.err = os.ErrorString("extra data in buffer") + dec.err = os.NewError("extra data in buffer") break } dec.nextUint() @@ -165,7 +165,7 @@ func (dec *Decoder) Decode(e interface{}) os.Error { // If e represents a value as opposed to a pointer, the answer won't // get back to the caller. Make sure it's a pointer. if value.Type().Kind() != reflect.Ptr { - dec.err = os.ErrorString("gob: attempt to decode into a non-pointer") + dec.err = os.NewError("gob: attempt to decode into a non-pointer") return dec.err } return dec.DecodeValue(value) @@ -180,7 +180,7 @@ func (dec *Decoder) DecodeValue(v reflect.Value) os.Error { if v.Kind() == reflect.Ptr && !v.IsNil() { // That's okay, we'll store through the pointer. } else if !v.CanSet() { - return os.ErrorString("gob: DecodeValue of unassignable value") + return os.NewError("gob: DecodeValue of unassignable value") } } // Make sure we're single-threaded through here. diff --git a/src/pkg/gob/doc.go b/src/pkg/gob/doc.go index 850759bbd..aaf429c43 100644 --- a/src/pkg/gob/doc.go +++ b/src/pkg/gob/doc.go @@ -29,29 +29,29 @@ receiver and transmitter will do all necessary indirection and dereferencing to convert between gobs and actual Go values. For instance, a gob type that is schematically, - struct { a, b int } + struct { A, B int } can be sent from or received into any of these Go types: - struct { a, b int } // the same - *struct { a, b int } // extra indirection of the struct - struct { *a, **b int } // extra indirection of the fields - struct { a, b int64 } // different concrete value type; see below + struct { A, B int } // the same + *struct { A, B int } // extra indirection of the struct + struct { *A, **B int } // extra indirection of the fields + struct { A, B int64 } // different concrete value type; see below It may also be received into any of these: - struct { a, b int } // the same - struct { b, a int } // ordering doesn't matter; matching is by name - struct { a, b, c int } // extra field (c) ignored - struct { b int } // missing field (a) ignored; data will be dropped - struct { b, c int } // missing field (a) ignored; extra field (c) ignored. + struct { A, B int } // the same + struct { B, A int } // ordering doesn't matter; matching is by name + struct { A, B, C int } // extra field (C) ignored + struct { B int } // missing field (A) ignored; data will be dropped + struct { B, C int } // missing field (A) ignored; extra field (C) ignored. Attempting to receive into these types will draw a decode error: - struct { a int; b uint } // change of signedness for b - struct { a int; b float } // change of type for b + struct { A int; B uint } // change of signedness for B + struct { A int; B float } // change of type for B struct { } // no field names in common - struct { c, d int } // no field names in common + struct { C, D int } // no field names in common Integers are transmitted two ways: arbitrary precision signed integers or arbitrary precision unsigned integers. There is no int8, int16 etc. @@ -269,12 +269,12 @@ StructValue: /* For implementers and the curious, here is an encoded example. Given - type Point struct {x, y int} + type Point struct {X, Y int} and the value p := Point{22, 33} the bytes transmitted that encode p will be: 1f ff 81 03 01 01 05 50 6f 69 6e 74 01 ff 82 00 - 01 02 01 01 78 01 04 00 01 01 79 01 04 00 00 00 + 01 02 01 01 58 01 04 00 01 01 59 01 04 00 00 00 07 ff 82 01 2c 01 42 00 They are determined as follows. @@ -310,13 +310,13 @@ reserved). 02 // There are two fields in the type (len(structType.field)) 01 // Start of first field structure; add 1 to get field number 0: field[0].name 01 // 1 byte - 78 // structType.field[0].name = "x" + 58 // structType.field[0].name = "X" 01 // Add 1 to get field number 1: field[0].id 04 // structType.field[0].typeId is 2 (signed int). 00 // End of structType.field[0]; start structType.field[1]; set field number to -1. 01 // Add 1 to get field number 0: field[1].name 01 // 1 byte - 79 // structType.field[1].name = "y" + 59 // structType.field[1].name = "Y" 01 // Add 1 to get field number 1: field[0].id 04 // struct.Type.field[1].typeId is 2 (signed int). 00 // End of structType.field[1]; end of structType.field. diff --git a/src/pkg/gob/encode.go b/src/pkg/gob/encode.go index f9e691a2f..941e26052 100644 --- a/src/pkg/gob/encode.go +++ b/src/pkg/gob/encode.go @@ -11,7 +11,7 @@ import ( "unsafe" ) -const uint64Size = unsafe.Sizeof(uint64(0)) +const uint64Size = int(unsafe.Sizeof(uint64(0))) // encoderState is the global execution state of an instance of the encoder. // Field numbers are delta encoded and always increase. The field @@ -468,7 +468,7 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) { // encGobEncoder encodes a value that implements the GobEncoder interface. // The data is sent as a byte array. -func (enc *Encoder) encodeGobEncoder(b *bytes.Buffer, v reflect.Value, index int) { +func (enc *Encoder) encodeGobEncoder(b *bytes.Buffer, v reflect.Value) { // TODO: should we catch panics from the called method? // We know it's a GobEncoder, so just call the method directly. data, err := v.Interface().(GobEncoder).GobEncode() @@ -592,17 +592,6 @@ func (enc *Encoder) encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp return &op, indir } -// methodIndex returns which method of rt implements the method. -func methodIndex(rt reflect.Type, method string) int { - for i := 0; i < rt.NumMethod(); i++ { - if rt.Method(i).Name == method { - return i - } - } - errorf("internal error: can't find method %s", method) - return 0 -} - // gobEncodeOpFor returns the op for a type that is known to implement // GobEncoder. func (enc *Encoder) gobEncodeOpFor(ut *userTypeInfo) (*encOp, int) { @@ -624,7 +613,7 @@ func (enc *Encoder) gobEncodeOpFor(ut *userTypeInfo) (*encOp, int) { v = reflect.ValueOf(unsafe.Unreflect(rt, p)) } state.update(i) - state.enc.encodeGobEncoder(state.b, v, methodIndex(rt, gobEncodeMethodName)) + state.enc.encodeGobEncoder(state.b, v) } return &op, int(ut.encIndir) // encIndir: op will get called with p == address of receiver. } diff --git a/src/pkg/gob/encoder.go b/src/pkg/gob/encoder.go index 65ee5bf67..96101d92b 100644 --- a/src/pkg/gob/encoder.go +++ b/src/pkg/gob/encoder.go @@ -50,7 +50,7 @@ func (enc *Encoder) popWriter() { } func (enc *Encoder) badType(rt reflect.Type) { - enc.setError(os.ErrorString("gob: can't encode type " + rt.String())) + enc.setError(os.NewError("gob: can't encode type " + rt.String())) } func (enc *Encoder) setError(err os.Error) { diff --git a/src/pkg/gob/gobencdec_test.go b/src/pkg/gob/gobencdec_test.go index 3e1906020..25cb5d11b 100644 --- a/src/pkg/gob/gobencdec_test.go +++ b/src/pkg/gob/gobencdec_test.go @@ -44,7 +44,7 @@ func (g *ByteStruct) GobEncode() ([]byte, os.Error) { func (g *ByteStruct) GobDecode(data []byte) os.Error { if g == nil { - return os.ErrorString("NIL RECEIVER") + return os.NewError("NIL RECEIVER") } // Expect N sequential-valued bytes. if len(data) == 0 { @@ -53,7 +53,7 @@ func (g *ByteStruct) GobDecode(data []byte) os.Error { g.a = data[0] for i, c := range data { if c != g.a+byte(i) { - return os.ErrorString("invalid data sequence") + return os.NewError("invalid data sequence") } } return nil @@ -71,7 +71,7 @@ func (g *StringStruct) GobDecode(data []byte) os.Error { a := data[0] for i, c := range data { if c != a+byte(i) { - return os.ErrorString("invalid data sequence") + return os.NewError("invalid data sequence") } } g.s = string(data) @@ -84,7 +84,7 @@ func (a *ArrayStruct) GobEncode() ([]byte, os.Error) { func (a *ArrayStruct) GobDecode(data []byte) os.Error { if len(data) != len(a.a) { - return os.ErrorString("wrong length in array decode") + return os.NewError("wrong length in array decode") } copy(a.a[:], data) return nil diff --git a/src/pkg/gob/type.go b/src/pkg/gob/type.go index c6542633a..552faa4d6 100644 --- a/src/pkg/gob/type.go +++ b/src/pkg/gob/type.go @@ -67,7 +67,7 @@ func validUserType(rt reflect.Type) (ut *userTypeInfo, err os.Error) { ut.base = pt.Elem() if ut.base == slowpoke { // ut.base lapped slowpoke // recursive pointer type. - return nil, os.ErrorString("can't represent recursive pointer type " + ut.base.String()) + return nil, os.NewError("can't represent recursive pointer type " + ut.base.String()) } if ut.indir%2 == 0 { slowpoke = slowpoke.Elem() @@ -80,11 +80,6 @@ func validUserType(rt reflect.Type) (ut *userTypeInfo, err os.Error) { return } -const ( - gobEncodeMethodName = "GobEncode" - gobDecodeMethodName = "GobDecode" -) - var ( gobEncoderInterfaceType = reflect.TypeOf(new(GobEncoder)).Elem() gobDecoderInterfaceType = reflect.TypeOf(new(GobDecoder)).Elem() @@ -508,7 +503,7 @@ func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, os. return st, nil default: - return nil, os.ErrorString("gob NewTypeObject can't handle type: " + rt.String()) + return nil, os.NewError("gob NewTypeObject can't handle type: " + rt.String()) } return nil, nil } diff --git a/src/pkg/hash/fnv/fnv.go b/src/pkg/hash/fnv/fnv.go index 9a1c6a0f2..3ff7d7c75 100644 --- a/src/pkg/hash/fnv/fnv.go +++ b/src/pkg/hash/fnv/fnv.go @@ -11,7 +11,6 @@ import ( "encoding/binary" "hash" "os" - "unsafe" ) type ( @@ -102,31 +101,31 @@ func (s *sum64a) Write(data []byte) (int, os.Error) { return len(data), nil } -func (s *sum32) Size() int { return unsafe.Sizeof(*s) } -func (s *sum32a) Size() int { return unsafe.Sizeof(*s) } -func (s *sum64) Size() int { return unsafe.Sizeof(*s) } -func (s *sum64a) Size() int { return unsafe.Sizeof(*s) } +func (s *sum32) Size() int { return 4 } +func (s *sum32a) Size() int { return 4 } +func (s *sum64) Size() int { return 8 } +func (s *sum64a) Size() int { return 8 } func (s *sum32) Sum() []byte { - a := make([]byte, unsafe.Sizeof(*s)) + a := make([]byte, 4) binary.BigEndian.PutUint32(a, uint32(*s)) return a } func (s *sum32a) Sum() []byte { - a := make([]byte, unsafe.Sizeof(*s)) + a := make([]byte, 4) binary.BigEndian.PutUint32(a, uint32(*s)) return a } func (s *sum64) Sum() []byte { - a := make([]byte, unsafe.Sizeof(*s)) + a := make([]byte, 8) binary.BigEndian.PutUint64(a, uint64(*s)) return a } func (s *sum64a) Sum() []byte { - a := make([]byte, unsafe.Sizeof(*s)) + a := make([]byte, 8) binary.BigEndian.PutUint64(a, uint64(*s)) return a } diff --git a/src/pkg/html/parse.go b/src/pkg/html/parse.go index 2ef90a873..6a2bc1ea6 100644 --- a/src/pkg/html/parse.go +++ b/src/pkg/html/parse.go @@ -400,6 +400,7 @@ func inBodyIM(p *parser) (insertionMode, bool) { p.framesetOK = false default: // TODO. + p.addElement(p.tok.Data, p.tok.Attr) } case EndTagToken: switch p.tok.Data { @@ -413,7 +414,10 @@ func inBodyIM(p *parser) (insertionMode, bool) { p.pop() } default: - // TODO. + // TODO: any other end tag + if p.tok.Data == p.top().Data { + p.pop() + } } } if endP { diff --git a/src/pkg/html/token_test.go b/src/pkg/html/token_test.go index c17b436aa..c794612ab 100644 --- a/src/pkg/html/token_test.go +++ b/src/pkg/html/token_test.go @@ -161,7 +161,7 @@ func TestTokenizer(t *testing.T) { loop: for _, tt := range tokenTests { z := NewTokenizer(bytes.NewBuffer([]byte(tt.html))) - for i, s := range strings.Split(tt.golden, "$", -1) { + for i, s := range strings.Split(tt.golden, "$") { if z.Next() == ErrorToken { t.Errorf("%s token %d: want %q got error %v", tt.desc, i, s, z.Error()) continue loop diff --git a/src/pkg/http/cgi/child.go b/src/pkg/http/cgi/child.go index e1ad7ad32..8b74d7054 100644 --- a/src/pkg/http/cgi/child.go +++ b/src/pkg/http/cgi/child.go @@ -45,13 +45,6 @@ func envMap(env []string) map[string]string { return m } -// These environment variables are manually copied into Request -var skipHeader = map[string]bool{ - "HTTP_HOST": true, - "HTTP_REFERER": true, - "HTTP_USER_AGENT": true, -} - // RequestFromMap creates an http.Request from CGI variables. // The returned Request's Body field is not populated. func RequestFromMap(params map[string]string) (*http.Request, os.Error) { @@ -73,8 +66,6 @@ func RequestFromMap(params map[string]string) (*http.Request, os.Error) { r.Header = http.Header{} r.Host = params["HTTP_HOST"] - r.Referer = params["HTTP_REFERER"] - r.UserAgent = params["HTTP_USER_AGENT"] if lenstr := params["CONTENT_LENGTH"]; lenstr != "" { clen, err := strconv.Atoi64(lenstr) @@ -90,7 +81,7 @@ func RequestFromMap(params map[string]string) (*http.Request, os.Error) { // Copy "HTTP_FOO_BAR" variables to "Foo-Bar" Headers for k, v := range params { - if !strings.HasPrefix(k, "HTTP_") || skipHeader[k] { + if !strings.HasPrefix(k, "HTTP_") || k == "HTTP_HOST" { continue } r.Header.Add(strings.Replace(k[5:], "_", "-", -1), v) diff --git a/src/pkg/http/cgi/child_test.go b/src/pkg/http/cgi/child_test.go index d12947814..eee043bc9 100644 --- a/src/pkg/http/cgi/child_test.go +++ b/src/pkg/http/cgi/child_test.go @@ -28,23 +28,19 @@ func TestRequest(t *testing.T) { if err != nil { t.Fatalf("RequestFromMap: %v", err) } - if g, e := req.UserAgent, "goclient"; e != g { + if g, e := req.UserAgent(), "goclient"; e != g { t.Errorf("expected UserAgent %q; got %q", e, g) } if g, e := req.Method, "GET"; e != g { t.Errorf("expected Method %q; got %q", e, g) } - if g, e := req.Header.Get("User-Agent"), ""; e != g { - // Tests that we don't put recognized headers in the map - t.Errorf("expected User-Agent %q; got %q", e, g) - } if g, e := req.Header.Get("Content-Type"), "text/xml"; e != g { t.Errorf("expected Content-Type %q; got %q", e, g) } if g, e := req.ContentLength, int64(123); e != g { t.Errorf("expected ContentLength %d; got %d", e, g) } - if g, e := req.Referer, "elsewhere"; e != g { + if g, e := req.Referer(), "elsewhere"; e != g { t.Errorf("expected Referer %q; got %q", e, g) } if req.Header == nil { diff --git a/src/pkg/http/cgi/host.go b/src/pkg/http/cgi/host.go index 7ab3f9247..059fc758e 100644 --- a/src/pkg/http/cgi/host.go +++ b/src/pkg/http/cgi/host.go @@ -16,7 +16,6 @@ package cgi import ( "bufio" - "bytes" "exec" "fmt" "http" @@ -47,6 +46,12 @@ type Handler struct { Path string // path to the CGI executable Root string // root URI prefix of handler or empty for "/" + // Dir specifies the CGI executable's working directory. + // If Dir is empty, the base directory of Path is used. + // If Path has no base directory, the current working + // directory is used. + Dir string + Env []string // extra environment variables to set, if any, as "key=value" InheritEnv []string // environment variables to inherit from host, as "key" Logger *log.Logger // optional log for errors or nil to use log.Print @@ -106,20 +111,13 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { env = append(env, "HTTPS=on") } - if len(req.Cookie) > 0 { - b := new(bytes.Buffer) - for idx, c := range req.Cookie { - if idx > 0 { - b.Write([]byte("; ")) - } - fmt.Fprintf(b, "%s=%s", c.Name, c.Value) - } - env = append(env, "HTTP_COOKIE="+b.String()) - } - for k, v := range req.Header { k = strings.Map(upperCaseAndUnderscore, k) - env = append(env, "HTTP_"+k+"="+strings.Join(v, ", ")) + joinStr := ", " + if k == "COOKIE" { + joinStr = "; " + } + env = append(env, "HTTP_"+k+"="+strings.Join(v, joinStr)) } if req.ContentLength > 0 { @@ -133,11 +131,11 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { env = append(env, h.Env...) } - path := os.Getenv("PATH") - if path == "" { - path = "/bin:/usr/bin:/usr/ucb:/usr/bsd:/usr/local/bin" + envPath := os.Getenv("PATH") + if envPath == "" { + envPath = "/bin:/usr/bin:/usr/ucb:/usr/bsd:/usr/local/bin" } - env = append(env, "PATH="+path) + env = append(env, "PATH="+envPath) for _, e := range h.InheritEnv { if v := os.Getenv(e); v != "" { @@ -151,7 +149,13 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { } } - cwd, pathBase := filepath.Split(h.Path) + var cwd, path string + if h.Dir != "" { + path = h.Path + cwd = h.Dir + } else { + cwd, path = filepath.Split(h.Path) + } if cwd == "" { cwd = "." } @@ -162,7 +166,7 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { } cmd := &exec.Cmd{ - Path: pathBase, + Path: path, Args: append([]string{h.Path}, h.Args...), Dir: cwd, Env: env, @@ -205,7 +209,7 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if len(line) == 0 { break } - parts := strings.Split(string(line), ":", 2) + parts := strings.SplitN(string(line), ":", 2) if len(parts) < 2 { h.printf("cgi: bogus header line: %s", string(line)) continue diff --git a/src/pkg/http/cgi/host_test.go b/src/pkg/http/cgi/host_test.go index bbdb715cf..b08d8bbf6 100644 --- a/src/pkg/http/cgi/host_test.go +++ b/src/pkg/http/cgi/host_test.go @@ -13,8 +13,10 @@ import ( "http" "http/httptest" "os" + "path/filepath" "strings" "testing" + "runtime" ) func newRequest(httpreq string) *http.Request { @@ -46,7 +48,7 @@ readlines: } linesRead++ trimmedLine := strings.TrimRight(line, "\r\n") - split := strings.Split(trimmedLine, "=", 2) + split := strings.SplitN(trimmedLine, "=", 2) if len(split) != 2 { t.Fatalf("Unexpected %d parts from invalid line number %v: %q; existing map=%v", len(split), linesRead, line, m) @@ -301,3 +303,77 @@ func TestInternalRedirect(t *testing.T) { } runCgiTest(t, h, "GET /test.cgi?loc=/foo HTTP/1.0\nHost: example.com\n\n", expectedMap) } + +func TestDirUnix(t *testing.T) { + if runtime.GOOS == "windows" { + return + } + + cwd, _ := os.Getwd() + h := &Handler{ + Path: "testdata/test.cgi", + Root: "/test.cgi", + Dir: cwd, + } + expectedMap := map[string]string{ + "cwd": cwd, + } + runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap) + + cwd, _ = os.Getwd() + cwd = filepath.Join(cwd, "testdata") + h = &Handler{ + Path: "testdata/test.cgi", + Root: "/test.cgi", + } + expectedMap = map[string]string{ + "cwd": cwd, + } + runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap) +} + +func TestDirWindows(t *testing.T) { + if runtime.GOOS != "windows" { + return + } + + cgifile, _ := filepath.Abs("testdata/test.cgi") + + var perl string + var err os.Error + perl, err = exec.LookPath("perl") + if err != nil { + return + } + perl, _ = filepath.Abs(perl) + + cwd, _ := os.Getwd() + h := &Handler{ + Path: perl, + Root: "/test.cgi", + Dir: cwd, + Args: []string{cgifile}, + Env: []string{"SCRIPT_FILENAME=" + cgifile}, + } + expectedMap := map[string]string{ + "cwd": cwd, + } + runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap) + + // If not specify Dir on windows, working directory should be + // base directory of perl. + cwd, _ = filepath.Split(perl) + if cwd != "" && cwd[len(cwd)-1] == filepath.Separator { + cwd = cwd[:len(cwd)-1] + } + h = &Handler{ + Path: perl, + Root: "/test.cgi", + Args: []string{cgifile}, + Env: []string{"SCRIPT_FILENAME=" + cgifile}, + } + expectedMap = map[string]string{ + "cwd": cwd, + } + runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap) +} diff --git a/src/pkg/http/cgi/testdata/test.cgi b/src/pkg/http/cgi/testdata/test.cgi index a1b2ff893..36c107f76 100755 --- a/src/pkg/http/cgi/testdata/test.cgi +++ b/src/pkg/http/cgi/testdata/test.cgi @@ -6,9 +6,9 @@ # Test script run as a child process under cgi_test.go use strict; -use CGI; +use Cwd; -my $q = CGI->new; +my $q = MiniCGI->new; my $params = $q->Vars; if ($params->{"loc"}) { @@ -39,3 +39,50 @@ foreach my $k (sort keys %ENV) { $clean_env =~ s/[\n\r]//g; print "env-$k=$clean_env\n"; } + +# NOTE: don't call getcwd() for windows. +# msys return /c/go/src/... not C:\go\... +my $dir; +if ($^O eq 'MSWin32' || $^O eq 'msys') { + my $cmd = $ENV{'COMSPEC'} || 'c:\\windows\\system32\\cmd.exe'; + $cmd =~ s!\\!/!g; + $dir = `$cmd /c cd`; + chomp $dir; +} else { + $dir = getcwd(); +} +print "cwd=$dir\n"; + + +# A minimal version of CGI.pm, for people without the perl-modules +# package installed. (CGI.pm used to be part of the Perl core, but +# some distros now bundle perl-base and perl-modules separately...) +package MiniCGI; + +sub new { + my $class = shift; + return bless {}, $class; +} + +sub Vars { + my $self = shift; + my $pairs; + if ($ENV{CONTENT_LENGTH}) { + $pairs = do { local $/; <STDIN> }; + } else { + $pairs = $ENV{QUERY_STRING}; + } + my $vars = {}; + foreach my $kv (split(/&/, $pairs)) { + my ($k, $v) = split(/=/, $kv, 2); + $vars->{_urldecode($k)} = _urldecode($v); + } + return $vars; +} + +sub _urldecode { + my $v = shift; + $v =~ tr/+/ /; + $v =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; + return $v; +} diff --git a/src/pkg/http/chunked.go b/src/pkg/http/chunked.go index 59121c5a2..6c23e691f 100644 --- a/src/pkg/http/chunked.go +++ b/src/pkg/http/chunked.go @@ -9,6 +9,7 @@ import ( "log" "os" "strconv" + "bufio" ) // NewChunkedWriter returns a new writer that translates writes into HTTP @@ -64,3 +65,13 @@ func (cw *chunkedWriter) Close() os.Error { _, err := io.WriteString(cw.Wire, "0\r\n") return err } + +// NewChunkedReader returns a new reader that translates the data read from r +// out of HTTP "chunked" format before returning it. +// The reader returns os.EOF when the final 0-length chunk is read. +// +// NewChunkedReader is not needed by normal applications. The http package +// automatically decodes chunking when reading response bodies. +func NewChunkedReader(r *bufio.Reader) io.Reader { + return &chunkedReader{r: r} +} diff --git a/src/pkg/http/client.go b/src/pkg/http/client.go index 71b037042..4f63b44f2 100644 --- a/src/pkg/http/client.go +++ b/src/pkg/http/client.go @@ -16,6 +16,11 @@ import ( // A Client is an HTTP client. Its zero value (DefaultClient) is a usable client // that uses DefaultTransport. +// +// The Client's Transport typically has internal state (cached +// TCP connections), so Clients should be reused instead of created as +// needed. Clients are safe for concurrent use by multiple goroutines. +// // Client is not yet very configurable. type Client struct { Transport RoundTripper // if nil, DefaultTransport is used @@ -36,6 +41,9 @@ var DefaultClient = &Client{} // RoundTripper is an interface representing the ability to execute a // single HTTP transaction, obtaining the Response for a given Request. +// +// A RoundTripper must be safe for concurrent use by multiple +// goroutines. type RoundTripper interface { // RoundTrip executes a single HTTP transaction, returning // the Response for the request req. RoundTrip should not @@ -173,7 +181,7 @@ func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err os.Error) // Add the Referer header. lastReq := via[len(via)-1] if lastReq.URL.Scheme != "https" { - req.Referer = lastReq.URL.String() + req.Header.Set("Referer", lastReq.URL.String()) } err = redirectChecker(req, via) @@ -190,7 +198,7 @@ func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err os.Error) if shouldRedirect(r.StatusCode) { r.Body.Close() if url = r.Header.Get("Location"); url == "" { - err = os.ErrorString(fmt.Sprintf("%d response missing Location header", r.StatusCode)) + err = os.NewError(fmt.Sprintf("%d response missing Location header", r.StatusCode)) break } base = req.URL @@ -207,7 +215,7 @@ func (c *Client) doFollowingRedirects(ireq *Request) (r *Response, err os.Error) func defaultCheckRedirect(req *Request, via []*Request) os.Error { if len(via) >= 10 { - return os.ErrorString("stopped after 10 redirects") + return os.NewError("stopped after 10 redirects") } return nil } diff --git a/src/pkg/http/client_test.go b/src/pkg/http/client_test.go index 9ef81d9d4..3b8558535 100644 --- a/src/pkg/http/client_test.go +++ b/src/pkg/http/client_test.go @@ -12,6 +12,7 @@ import ( "http/httptest" "io" "io/ioutil" + "net" "os" "strconv" "strings" @@ -149,7 +150,7 @@ func TestRedirects(t *testing.T) { n, _ := strconv.Atoi(r.FormValue("n")) // Test Referer header. (7 is arbitrary position to test at) if n == 7 { - if g, e := r.Referer, ts.URL+"/?n=6"; e != g { + if g, e := r.Referer(), ts.URL+"/?n=6"; e != g { t.Errorf("on request ?n=7, expected referer of %q; got %q", e, g) } } @@ -243,3 +244,48 @@ func TestStreamingGet(t *testing.T) { t.Fatalf("at end expected EOF, got %v", err) } } + +type writeCountingConn struct { + net.Conn + count *int +} + +func (c *writeCountingConn) Write(p []byte) (int, os.Error) { + *c.count++ + return c.Conn.Write(p) +} + +// TestClientWrites verifies that client requests are buffered and we +// don't send a TCP packet per line of the http request + body. +func TestClientWrites(t *testing.T) { + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + })) + defer ts.Close() + + writes := 0 + dialer := func(netz string, addr string) (net.Conn, os.Error) { + c, err := net.Dial(netz, addr) + if err == nil { + c = &writeCountingConn{c, &writes} + } + return c, err + } + c := &Client{Transport: &Transport{Dial: dialer}} + + _, err := c.Get(ts.URL) + if err != nil { + t.Fatal(err) + } + if writes != 1 { + t.Errorf("Get request did %d Write calls, want 1", writes) + } + + writes = 0 + _, err = c.PostForm(ts.URL, Values{"foo": {"bar"}}) + if err != nil { + t.Fatal(err) + } + if writes != 1 { + t.Errorf("Post request did %d Write calls, want 1", writes) + } +} diff --git a/src/pkg/http/cookie.go b/src/pkg/http/cookie.go index eb61a7001..fe70431bb 100644 --- a/src/pkg/http/cookie.go +++ b/src/pkg/http/cookie.go @@ -7,9 +7,6 @@ package http import ( "bytes" "fmt" - "io" - "os" - "sort" "strconv" "strings" "time" @@ -40,30 +37,25 @@ type Cookie struct { } // readSetCookies parses all "Set-Cookie" values from -// the header h, removes the successfully parsed values from the -// "Set-Cookie" key in h and returns the parsed Cookies. +// the header h and returns the successfully parsed Cookies. func readSetCookies(h Header) []*Cookie { cookies := []*Cookie{} - var unparsedLines []string for _, line := range h["Set-Cookie"] { - parts := strings.Split(strings.TrimSpace(line), ";", -1) + parts := strings.Split(strings.TrimSpace(line), ";") if len(parts) == 1 && parts[0] == "" { continue } parts[0] = strings.TrimSpace(parts[0]) j := strings.Index(parts[0], "=") if j < 0 { - unparsedLines = append(unparsedLines, line) continue } name, value := parts[0][:j], parts[0][j+1:] if !isCookieNameValid(name) { - unparsedLines = append(unparsedLines, line) continue } value, success := parseCookieValue(value) if !success { - unparsedLines = append(unparsedLines, line) continue } c := &Cookie{ @@ -134,77 +126,56 @@ func readSetCookies(h Header) []*Cookie { } cookies = append(cookies, c) } - h["Set-Cookie"] = unparsedLines, unparsedLines != nil return cookies } // SetCookie adds a Set-Cookie header to the provided ResponseWriter's headers. func SetCookie(w ResponseWriter, cookie *Cookie) { - var b bytes.Buffer - writeSetCookieToBuffer(&b, cookie) - w.Header().Add("Set-Cookie", b.String()) + w.Header().Add("Set-Cookie", cookie.String()) } -func writeSetCookieToBuffer(buf *bytes.Buffer, c *Cookie) { - fmt.Fprintf(buf, "%s=%s", sanitizeName(c.Name), sanitizeValue(c.Value)) +// String returns the serialization of the cookie for use in a Cookie +// header (if only Name and Value are set) or a Set-Cookie response +// header (if other fields are set). +func (c *Cookie) String() string { + var b bytes.Buffer + fmt.Fprintf(&b, "%s=%s", sanitizeName(c.Name), sanitizeValue(c.Value)) if len(c.Path) > 0 { - fmt.Fprintf(buf, "; Path=%s", sanitizeValue(c.Path)) + fmt.Fprintf(&b, "; Path=%s", sanitizeValue(c.Path)) } if len(c.Domain) > 0 { - fmt.Fprintf(buf, "; Domain=%s", sanitizeValue(c.Domain)) + fmt.Fprintf(&b, "; Domain=%s", sanitizeValue(c.Domain)) } if len(c.Expires.Zone) > 0 { - fmt.Fprintf(buf, "; Expires=%s", c.Expires.Format(time.RFC1123)) + fmt.Fprintf(&b, "; Expires=%s", c.Expires.Format(time.RFC1123)) } if c.MaxAge > 0 { - fmt.Fprintf(buf, "; Max-Age=%d", c.MaxAge) + fmt.Fprintf(&b, "; Max-Age=%d", c.MaxAge) } else if c.MaxAge < 0 { - fmt.Fprintf(buf, "; Max-Age=0") + fmt.Fprintf(&b, "; Max-Age=0") } if c.HttpOnly { - fmt.Fprintf(buf, "; HttpOnly") + fmt.Fprintf(&b, "; HttpOnly") } if c.Secure { - fmt.Fprintf(buf, "; Secure") + fmt.Fprintf(&b, "; Secure") } + return b.String() } -// writeSetCookies writes the wire representation of the set-cookies -// to w. Each cookie is written on a separate "Set-Cookie: " line. -// This choice is made because HTTP parsers tend to have a limit on -// line-length, so it seems safer to place cookies on separate lines. -func writeSetCookies(w io.Writer, kk []*Cookie) os.Error { - if kk == nil { - return nil - } - lines := make([]string, 0, len(kk)) - var b bytes.Buffer - for _, c := range kk { - b.Reset() - writeSetCookieToBuffer(&b, c) - lines = append(lines, "Set-Cookie: "+b.String()+"\r\n") - } - sort.SortStrings(lines) - for _, l := range lines { - if _, err := io.WriteString(w, l); err != nil { - return err - } - } - return nil -} - -// readCookies parses all "Cookie" values from -// the header h, removes the successfully parsed values from the -// "Cookie" key in h and returns the parsed Cookies. -func readCookies(h Header) []*Cookie { +// readCookies parses all "Cookie" values from the header h and +// returns the successfully parsed Cookies. +// +// if filter isn't empty, only cookies of that name are returned +func readCookies(h Header, filter string) []*Cookie { cookies := []*Cookie{} lines, ok := h["Cookie"] if !ok { return cookies } - unparsedLines := []string{} + for _, line := range lines { - parts := strings.Split(strings.TrimSpace(line), ";", -1) + parts := strings.Split(strings.TrimSpace(line), ";") if len(parts) == 1 && parts[0] == "" { continue } @@ -215,50 +186,27 @@ func readCookies(h Header) []*Cookie { if len(parts[i]) == 0 { continue } - attr, val := parts[i], "" - if j := strings.Index(attr, "="); j >= 0 { - attr, val = attr[:j], attr[j+1:] + name, val := parts[i], "" + if j := strings.Index(name, "="); j >= 0 { + name, val = name[:j], name[j+1:] } - if !isCookieNameValid(attr) { + if !isCookieNameValid(name) { + continue + } + if filter != "" && filter != name { continue } val, success := parseCookieValue(val) if !success { continue } - cookies = append(cookies, &Cookie{Name: attr, Value: val}) + cookies = append(cookies, &Cookie{Name: name, Value: val}) parsedPairs++ } - if parsedPairs == 0 { - unparsedLines = append(unparsedLines, line) - } } - h["Cookie"] = unparsedLines, len(unparsedLines) > 0 return cookies } -// writeCookies writes the wire representation of the cookies to -// w. According to RFC 6265 section 5.4, writeCookies does not -// attach more than one Cookie header field. That means all -// cookies, if any, are written into the same line, separated by -// semicolon. -func writeCookies(w io.Writer, kk []*Cookie) os.Error { - if len(kk) == 0 { - return nil - } - var buf bytes.Buffer - fmt.Fprintf(&buf, "Cookie: ") - for i, c := range kk { - if i > 0 { - fmt.Fprintf(&buf, "; ") - } - fmt.Fprintf(&buf, "%s=%s", sanitizeName(c.Name), sanitizeValue(c.Value)) - } - fmt.Fprintf(&buf, "\r\n") - _, err := w.Write(buf.Bytes()) - return err -} - func sanitizeName(n string) string { n = strings.Replace(n, "\n", "-", -1) n = strings.Replace(n, "\r", "-", -1) diff --git a/src/pkg/http/cookie_test.go b/src/pkg/http/cookie_test.go index 02e42226b..d7aeda0be 100644 --- a/src/pkg/http/cookie_test.go +++ b/src/pkg/http/cookie_test.go @@ -5,7 +5,6 @@ package http import ( - "bytes" "fmt" "json" "os" @@ -15,30 +14,31 @@ import ( ) var writeSetCookiesTests = []struct { - Cookies []*Cookie - Raw string + Cookie *Cookie + Raw string }{ { - []*Cookie{ - &Cookie{Name: "cookie-1", Value: "v$1"}, - &Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600}, - &Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"}, - &Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"}, - }, - "Set-Cookie: cookie-1=v$1\r\n" + - "Set-Cookie: cookie-2=two; Max-Age=3600\r\n" + - "Set-Cookie: cookie-3=three; Domain=.example.com\r\n" + - "Set-Cookie: cookie-4=four; Path=/restricted/\r\n", + &Cookie{Name: "cookie-1", Value: "v$1"}, + "cookie-1=v$1", + }, + { + &Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600}, + "cookie-2=two; Max-Age=3600", + }, + { + &Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"}, + "cookie-3=three; Domain=.example.com", + }, + { + &Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"}, + "cookie-4=four; Path=/restricted/", }, } func TestWriteSetCookies(t *testing.T) { for i, tt := range writeSetCookiesTests { - var w bytes.Buffer - writeSetCookies(&w, tt.Cookies) - seen := string(w.Bytes()) - if seen != tt.Raw { - t.Errorf("Test %d, expecting:\n%s\nGot:\n%s\n", i, tt.Raw, seen) + if g, e := tt.Cookie.String(), tt.Raw; g != e { + t.Errorf("Test %d, expecting:\n%s\nGot:\n%s\n", i, e, g) continue } } @@ -73,7 +73,7 @@ func TestSetCookie(t *testing.T) { } } -var writeCookiesTests = []struct { +var addCookieTests = []struct { Cookies []*Cookie Raw string }{ @@ -83,7 +83,7 @@ var writeCookiesTests = []struct { }, { []*Cookie{&Cookie{Name: "cookie-1", Value: "v$1"}}, - "Cookie: cookie-1=v$1\r\n", + "cookie-1=v$1", }, { []*Cookie{ @@ -91,17 +91,18 @@ var writeCookiesTests = []struct { &Cookie{Name: "cookie-2", Value: "v$2"}, &Cookie{Name: "cookie-3", Value: "v$3"}, }, - "Cookie: cookie-1=v$1; cookie-2=v$2; cookie-3=v$3\r\n", + "cookie-1=v$1; cookie-2=v$2; cookie-3=v$3", }, } -func TestWriteCookies(t *testing.T) { - for i, tt := range writeCookiesTests { - var w bytes.Buffer - writeCookies(&w, tt.Cookies) - seen := string(w.Bytes()) - if seen != tt.Raw { - t.Errorf("Test %d, expecting:\n%s\nGot:\n%s\n", i, tt.Raw, seen) +func TestAddCookie(t *testing.T) { + for i, tt := range addCookieTests { + req, _ := NewRequest("GET", "http://example.com/", nil) + for _, c := range tt.Cookies { + req.AddCookie(c) + } + if g := req.Header.Get("Cookie"); g != tt.Raw { + t.Errorf("Test %d:\nwant: %s\n got: %s\n", i, tt.Raw, g) continue } } @@ -140,30 +141,61 @@ func toJSON(v interface{}) string { func TestReadSetCookies(t *testing.T) { for i, tt := range readSetCookiesTests { - c := readSetCookies(tt.Header) - if !reflect.DeepEqual(c, tt.Cookies) { - t.Errorf("#%d readSetCookies: have\n%s\nwant\n%s\n", i, toJSON(c), toJSON(tt.Cookies)) - continue + for n := 0; n < 2; n++ { // to verify readSetCookies doesn't mutate its input + c := readSetCookies(tt.Header) + if !reflect.DeepEqual(c, tt.Cookies) { + t.Errorf("#%d readSetCookies: have\n%s\nwant\n%s\n", i, toJSON(c), toJSON(tt.Cookies)) + continue + } } } } var readCookiesTests = []struct { Header Header + Filter string Cookies []*Cookie }{ { - Header{"Cookie": {"Cookie-1=v$1"}}, - []*Cookie{&Cookie{Name: "Cookie-1", Value: "v$1"}}, + Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}}, + "", + []*Cookie{ + &Cookie{Name: "Cookie-1", Value: "v$1"}, + &Cookie{Name: "c2", Value: "v2"}, + }, + }, + { + Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}}, + "c2", + []*Cookie{ + &Cookie{Name: "c2", Value: "v2"}, + }, + }, + { + Header{"Cookie": {"Cookie-1=v$1; c2=v2"}}, + "", + []*Cookie{ + &Cookie{Name: "Cookie-1", Value: "v$1"}, + &Cookie{Name: "c2", Value: "v2"}, + }, + }, + { + Header{"Cookie": {"Cookie-1=v$1; c2=v2"}}, + "c2", + []*Cookie{ + &Cookie{Name: "c2", Value: "v2"}, + }, }, } func TestReadCookies(t *testing.T) { for i, tt := range readCookiesTests { - c := readCookies(tt.Header) - if !reflect.DeepEqual(c, tt.Cookies) { - t.Errorf("#%d readCookies: have\n%s\nwant\n%s\n", i, toJSON(c), toJSON(tt.Cookies)) - continue + for n := 0; n < 2; n++ { // to verify readCookies doesn't mutate its input + c := readCookies(tt.Header, tt.Filter) + if !reflect.DeepEqual(c, tt.Cookies) { + t.Errorf("#%d readCookies:\nhave: %s\nwant: %s\n", i, toJSON(c), toJSON(tt.Cookies)) + continue + } } } } diff --git a/src/pkg/http/fs.go b/src/pkg/http/fs.go index 28a0c51ef..0b830053a 100644 --- a/src/pkg/http/fs.go +++ b/src/pkg/http/fs.go @@ -11,6 +11,7 @@ import ( "io" "mime" "os" + "path" "path/filepath" "strconv" "strings" @@ -18,6 +19,38 @@ import ( "utf8" ) +// A Dir implements http.FileSystem using the native file +// system restricted to a specific directory tree. +type Dir string + +func (d Dir) Open(name string) (File, os.Error) { + if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 { + return nil, os.NewError("http: invalid character in file path") + } + f, err := os.Open(filepath.Join(string(d), filepath.FromSlash(path.Clean("/"+name)))) + if err != nil { + return nil, err + } + return f, nil +} + +// A FileSystem implements access to a collection of named files. +// The elements in a file path are separated by slash ('/', U+002F) +// characters, regardless of host operating system convention. +type FileSystem interface { + Open(name string) (File, os.Error) +} + +// A File is returned by a FileSystem's Open method and can be +// served by the FileServer implementation. +type File interface { + Close() os.Error + Stat() (*os.FileInfo, os.Error) + Readdir(count int) ([]os.FileInfo, os.Error) + Read([]byte) (int, os.Error) + Seek(offset int64, whence int) (int64, os.Error) +} + // Heuristic: b is text if it is valid UTF-8 and doesn't // contain any unprintable ASCII or Unicode characters. func isText(b []byte) bool { @@ -44,7 +77,7 @@ func isText(b []byte) bool { return true } -func dirList(w ResponseWriter, f *os.File) { +func dirList(w ResponseWriter, f File) { fmt.Fprintf(w, "<pre>\n") for { dirs, err := f.Readdir(100) @@ -63,7 +96,8 @@ func dirList(w ResponseWriter, f *os.File) { fmt.Fprintf(w, "</pre>\n") } -func serveFile(w ResponseWriter, r *Request, name string, redirect bool) { +// name is '/'-separated, not filepath.Separator. +func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirect bool) { const indexPage = "/index.html" // redirect .../index.html to .../ @@ -72,7 +106,7 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) { return } - f, err := os.Open(name) + f, err := fs.Open(name) if err != nil { // TODO expose actual error? NotFound(w, r) @@ -113,7 +147,7 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) { // use contents of index.html for directory, if present if d.IsDirectory() { index := name + filepath.FromSlash(indexPage) - ff, err := os.Open(index) + ff, err := fs.Open(index) if err == nil { defer ff.Close() dd, err := ff.Stat() @@ -157,7 +191,7 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) { // TODO(adg): handle multiple ranges ranges, err := parseRange(r.Header.Get("Range"), size) if err == nil && len(ranges) > 1 { - err = os.ErrorString("multiple ranges not supported") + err = os.NewError("multiple ranges not supported") } if err != nil { Error(w, err.String(), StatusRequestedRangeNotSatisfiable) @@ -188,28 +222,26 @@ func serveFile(w ResponseWriter, r *Request, name string, redirect bool) { // ServeFile replies to the request with the contents of the named file or directory. func ServeFile(w ResponseWriter, r *Request, name string) { - serveFile(w, r, name, false) + serveFile(w, r, Dir(name), "", false) } type fileHandler struct { - root string - prefix string + root FileSystem } // FileServer returns a handler that serves HTTP requests // with the contents of the file system rooted at root. -// It strips prefix from the incoming requests before -// looking up the file name in the file system. -func FileServer(root, prefix string) Handler { return &fileHandler{root, prefix} } +// +// To use the operating system's file system implementation, +// use http.Dir: +// +// http.Handle("/", http.FileServer(http.Dir("/tmp"))) +func FileServer(root FileSystem) Handler { + return &fileHandler{root} +} func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) { - path := r.URL.Path - if !strings.HasPrefix(path, f.prefix) { - NotFound(w, r) - return - } - path = path[len(f.prefix):] - serveFile(w, r, filepath.Join(f.root, filepath.FromSlash(path)), true) + serveFile(w, r, f.root, path.Clean(r.URL.Path), true) } // httpRange specifies the byte range to be sent to the client. @@ -227,7 +259,7 @@ func parseRange(s string, size int64) ([]httpRange, os.Error) { return nil, os.NewError("invalid range") } var ranges []httpRange - for _, ra := range strings.Split(s[len(b):], ",", -1) { + for _, ra := range strings.Split(s[len(b):], ",") { i := strings.Index(ra, "-") if i < 0 { return nil, os.NewError("invalid range") diff --git a/src/pkg/http/fs_test.go b/src/pkg/http/fs_test.go index 554053449..dbbdf05bd 100644 --- a/src/pkg/http/fs_test.go +++ b/src/pkg/http/fs_test.go @@ -85,6 +85,72 @@ func TestServeFile(t *testing.T) { } } +type testFileSystem struct { + open func(name string) (File, os.Error) +} + +func (fs *testFileSystem) Open(name string) (File, os.Error) { + return fs.open(name) +} + +func TestFileServerCleans(t *testing.T) { + ch := make(chan string, 1) + fs := FileServer(&testFileSystem{func(name string) (File, os.Error) { + ch <- name + return nil, os.ENOENT + }}) + tests := []struct { + reqPath, openArg string + }{ + {"/foo.txt", "/foo.txt"}, + {"//foo.txt", "/foo.txt"}, + {"/../foo.txt", "/foo.txt"}, + } + req, _ := NewRequest("GET", "http://example.com", nil) + for n, test := range tests { + rec := httptest.NewRecorder() + req.URL.Path = test.reqPath + fs.ServeHTTP(rec, req) + if got := <-ch; got != test.openArg { + t.Errorf("test %d: got %q, want %q", n, got, test.openArg) + } + } +} + +func TestDirJoin(t *testing.T) { + wfi, err := os.Stat("/etc/hosts") + if err != nil { + t.Logf("skipping test; no /etc/hosts file") + return + } + test := func(d Dir, name string) { + f, err := d.Open(name) + if err != nil { + t.Fatalf("open of %s: %v", name, err) + } + defer f.Close() + gfi, err := f.Stat() + if err != nil { + t.Fatalf("stat of %s: %v", err) + } + if gfi.Ino != wfi.Ino { + t.Errorf("%s got different inode") + } + } + test(Dir("/etc/"), "/hosts") + test(Dir("/etc/"), "hosts") + test(Dir("/etc/"), "../../../../hosts") + test(Dir("/etc"), "/hosts") + test(Dir("/etc"), "hosts") + test(Dir("/etc"), "../../../../hosts") + + // Not really directories, but since we use this trick in + // ServeFile, test it: + test(Dir("/etc/hosts"), "") + test(Dir("/etc/hosts"), "/") + test(Dir("/etc/hosts"), "../") +} + func TestServeFileContentType(t *testing.T) { const ctype = "icecream/chocolate" override := false diff --git a/src/pkg/http/header.go b/src/pkg/http/header.go index 95140b01f..08b077130 100644 --- a/src/pkg/http/header.go +++ b/src/pkg/http/header.go @@ -56,15 +56,12 @@ func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) os.Error { keys = append(keys, k) } } - sort.SortStrings(keys) + sort.Strings(keys) for _, k := range keys { for _, v := range h[k] { v = strings.Replace(v, "\n", " ", -1) v = strings.Replace(v, "\r", " ", -1) v = strings.TrimSpace(v) - if v == "" { - continue - } if _, err := fmt.Fprintf(w, "%s: %s\r\n", k, v); err != nil { return err } diff --git a/src/pkg/http/header_test.go b/src/pkg/http/header_test.go index 7e24cb069..ccdee8a97 100644 --- a/src/pkg/http/header_test.go +++ b/src/pkg/http/header_test.go @@ -57,6 +57,16 @@ var headerWriteTests = []struct { map[string]bool{"Content-Length": true, "Expires": true, "Content-Encoding": true}, "", }, + { + Header{ + "Nil": nil, + "Empty": {}, + "Blank": {""}, + "Double-Blank": {"", ""}, + }, + nil, + "Blank: \r\nDouble-Blank: \r\nDouble-Blank: \r\n", + }, } func TestHeaderWrite(t *testing.T) { diff --git a/src/pkg/http/persist.go b/src/pkg/http/persist.go index 62f9ff1b5..78bf9058f 100644 --- a/src/pkg/http/persist.go +++ b/src/pkg/http/persist.go @@ -24,6 +24,9 @@ var ( // to regain control over the connection. ServerConn supports pipe-lining, // i.e. requests can be read out of sync (but in the same order) while the // respective responses are sent. +// +// ServerConn is low-level and should not be needed by most applications. +// See Server. type ServerConn struct { lk sync.Mutex // read-write protects the following fields c net.Conn @@ -211,6 +214,9 @@ func (sc *ServerConn) Write(req *Request, resp *Response) os.Error { // connection, while respecting the HTTP keepalive logic. ClientConn // supports hijacking the connection calling Hijack to // regain control of the underlying net.Conn and deal with it as desired. +// +// ClientConn is low-level and should not be needed by most applications. +// See Client. type ClientConn struct { lk sync.Mutex // read-write protects the following fields c net.Conn diff --git a/src/pkg/http/readrequest_test.go b/src/pkg/http/readrequest_test.go index d93e573f5..79f8de70d 100644 --- a/src/pkg/http/readrequest_test.go +++ b/src/pkg/http/readrequest_test.go @@ -13,11 +13,15 @@ import ( ) type reqTest struct { - Raw string - Req Request - Body string + Raw string + Req *Request + Body string + Error string } +var noError = "" +var noBody = "" + var reqTests = []reqTest{ // Baseline test; All Request fields included for template use { @@ -33,7 +37,7 @@ var reqTests = []reqTest{ "Proxy-Connection: keep-alive\r\n\r\n" + "abcdef\n???", - Request{ + &Request{ Method: "GET", RawURL: "http://www.techcrunch.com/", URL: &URL{ @@ -58,16 +62,43 @@ var reqTests = []reqTest{ "Keep-Alive": {"300"}, "Proxy-Connection": {"keep-alive"}, "Content-Length": {"7"}, + "User-Agent": {"Fake"}, }, Close: false, ContentLength: 7, Host: "www.techcrunch.com", - Referer: "", - UserAgent: "Fake", Form: Values{}, }, "abcdef\n", + + noError, + }, + + // GET request with no body (the normal case) + { + "GET / HTTP/1.1\r\n" + + "Host: foo.com\r\n\r\n", + + &Request{ + Method: "GET", + RawURL: "/", + URL: &URL{ + Raw: "/", + Path: "/", + RawPath: "/", + }, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Close: false, + ContentLength: 0, + Host: "foo.com", + Form: Values{}, + }, + + noBody, + noError, }, // Tests that we don't parse a path that looks like a @@ -76,7 +107,7 @@ var reqTests = []reqTest{ "GET //user@host/is/actually/a/path/ HTTP/1.1\r\n" + "Host: test\r\n\r\n", - Request{ + &Request{ Method: "GET", RawURL: "//user@host/is/actually/a/path/", URL: &URL{ @@ -95,14 +126,31 @@ var reqTests = []reqTest{ ProtoMinor: 1, Header: Header{}, Close: false, - ContentLength: -1, + ContentLength: 0, Host: "test", - Referer: "", - UserAgent: "", Form: Values{}, }, - "", + noBody, + noError, + }, + + // Tests a bogus abs_path on the Request-Line (RFC 2616 section 5.1.2) + { + "GET ../../../../etc/passwd HTTP/1.1\r\n" + + "Host: test\r\n\r\n", + nil, + noBody, + "parse ../../../../etc/passwd: invalid URI for request", + }, + + // Tests missing URL: + { + "GET HTTP/1.1\r\n" + + "Host: test\r\n\r\n", + nil, + noBody, + "parse : empty url", }, } @@ -113,12 +161,14 @@ func TestReadRequest(t *testing.T) { braw.WriteString(tt.Raw) req, err := ReadRequest(bufio.NewReader(&braw)) if err != nil { - t.Errorf("#%d: %s", i, err) + if err.String() != tt.Error { + t.Errorf("#%d: error %q, want error %q", i, err.String(), tt.Error) + } continue } rbody := req.Body req.Body = nil - diff(t, fmt.Sprintf("#%d Request", i), req, &tt.Req) + diff(t, fmt.Sprintf("#%d Request", i), req, tt.Req) var bout bytes.Buffer if rbody != nil { io.Copy(&bout, rbody) diff --git a/src/pkg/http/request.go b/src/pkg/http/request.go index bdc3a7e4f..2917cc1e6 100644 --- a/src/pkg/http/request.go +++ b/src/pkg/http/request.go @@ -35,13 +35,15 @@ const ( // ErrMissingFile is returned by FormFile when the provided file field name // is either not present in the request or not a file field. -var ErrMissingFile = os.ErrorString("http: no such file") +var ErrMissingFile = os.NewError("http: no such file") // HTTP request parsing errors. type ProtocolError struct { - os.ErrorString + ErrorString string } +func (err *ProtocolError) String() string { return err.ErrorString } + var ( ErrLineTooLong = &ProtocolError{"header line too long"} ErrHeaderTooLong = &ProtocolError{"header too long"} @@ -60,10 +62,10 @@ type badStringError struct { func (e *badStringError) String() string { return fmt.Sprintf("%s %q", e.what, e.str) } -var reqExcludeHeader = map[string]bool{ +// Headers that Request.Write handles itself and should be skipped. +var reqWriteExcludeHeader = map[string]bool{ "Host": true, "User-Agent": true, - "Referer": true, "Content-Length": true, "Transfer-Encoding": true, "Trailer": true, @@ -102,9 +104,6 @@ type Request struct { // following a hyphen uppercase and the rest lowercase. Header Header - // Cookie records the HTTP cookies sent with the request. - Cookie []*Cookie - // The message body. Body io.ReadCloser @@ -125,21 +124,6 @@ type Request struct { // or the host name given in the URL itself. Host string - // The referring URL, if sent in the request. - // - // Referer is misspelled as in the request itself, - // a mistake from the earliest days of HTTP. - // This value can also be fetched from the Header map - // as Header["Referer"]; the benefit of making it - // available as a structure field is that the compiler - // can diagnose programs that use the alternate - // (correct English) spelling req.Referrer but cannot - // diagnose programs that use Header["Referrer"]. - Referer string - - // The User-Agent: header string, if sent in the request. - UserAgent string - // The parsed form. Only available after ParseForm is called. Form Values @@ -176,6 +160,52 @@ func (r *Request) ProtoAtLeast(major, minor int) bool { r.ProtoMajor == major && r.ProtoMinor >= minor } +// UserAgent returns the client's User-Agent, if sent in the request. +func (r *Request) UserAgent() string { + return r.Header.Get("User-Agent") +} + +// Cookies parses and returns the HTTP cookies sent with the request. +func (r *Request) Cookies() []*Cookie { + return readCookies(r.Header, "") +} + +var ErrNoCookie = os.NewError("http: named cookied not present") + +// Cookie returns the named cookie provided in the request or +// ErrNoCookie if not found. +func (r *Request) Cookie(name string) (*Cookie, os.Error) { + for _, c := range readCookies(r.Header, name) { + return c, nil + } + return nil, ErrNoCookie +} + +// AddCookie adds a cookie to the request. Per RFC 6265 section 5.4, +// AddCookie does not attach more than one Cookie header field. That +// means all cookies, if any, are written into the same line, +// separated by semicolon. +func (r *Request) AddCookie(c *Cookie) { + s := fmt.Sprintf("%s=%s", sanitizeName(c.Name), sanitizeValue(c.Value)) + if c := r.Header.Get("Cookie"); c != "" { + r.Header.Set("Cookie", c+"; "+s) + } else { + r.Header.Set("Cookie", s) + } +} + +// Referer returns the referring URL, if sent in the request. +// +// Referer is misspelled as in the request itself, a mistake from the +// earliest days of HTTP. This value can also be fetched from the +// Header map as Header["Referer"]; the benefit of making it available +// as a method is that the compiler can diagnose programs that use the +// alternate (correct English) spelling req.Referrer() but cannot +// diagnose programs that use Header["Referrer"]. +func (r *Request) Referer() string { + return r.Header.Get("Referer") +} + // multipartByReader is a sentinel value. // Its presence in Request.MultipartForm indicates that parsing of the request // body has been handed off to a MultipartReader instead of ParseMultipartFrom. @@ -188,7 +218,7 @@ var multipartByReader = &multipart.Form{ // multipart/form-data POST request, else returns nil and an error. // Use this function instead of ParseMultipartForm to // process the request body as a stream. -func (r *Request) MultipartReader() (multipart.Reader, os.Error) { +func (r *Request) MultipartReader() (*multipart.Reader, os.Error) { if r.MultipartForm == multipartByReader { return nil, os.NewError("http: MultipartReader called twice") } @@ -199,7 +229,7 @@ func (r *Request) MultipartReader() (multipart.Reader, os.Error) { return r.multipartReader() } -func (r *Request) multipartReader() (multipart.Reader, os.Error) { +func (r *Request) multipartReader() (*multipart.Reader, os.Error) { v := r.Header.Get("Content-Type") if v == "" { return nil, ErrNotMultipart @@ -230,10 +260,7 @@ const defaultUserAgent = "Go http package" // Host // RawURL, if non-empty, or else URL // Method (defaults to "GET") -// UserAgent (defaults to defaultUserAgent) -// Referer -// Header (only keys not already in this list) -// Cookie +// Header // ContentLength // TransferEncoding // Body @@ -277,13 +304,22 @@ func (req *Request) write(w io.Writer, usingProxy bool) os.Error { } } - fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), uri) + bw := bufio.NewWriter(w) + fmt.Fprintf(bw, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), uri) // Header lines - fmt.Fprintf(w, "Host: %s\r\n", host) - fmt.Fprintf(w, "User-Agent: %s\r\n", valueOrDefault(req.UserAgent, defaultUserAgent)) - if req.Referer != "" { - fmt.Fprintf(w, "Referer: %s\r\n", req.Referer) + fmt.Fprintf(bw, "Host: %s\r\n", host) + + // Use the defaultUserAgent unless the Header contains one, which + // may be blank to not send the header. + userAgent := defaultUserAgent + if req.Header != nil { + if ua := req.Header["User-Agent"]; len(ua) > 0 { + userAgent = ua[0] + } + } + if userAgent != "" { + fmt.Fprintf(bw, "User-Agent: %s\r\n", userAgent) } // Process Body,ContentLength,Close,Trailer @@ -291,35 +327,25 @@ func (req *Request) write(w io.Writer, usingProxy bool) os.Error { if err != nil { return err } - err = tw.WriteHeader(w) + err = tw.WriteHeader(bw) if err != nil { return err } // TODO: split long values? (If so, should share code with Conn.Write) - // TODO: if Header includes values for Host, User-Agent, or Referer, this - // may conflict with the User-Agent or Referer headers we add manually. - // One solution would be to remove the Host, UserAgent, and Referer fields - // from Request, and introduce Request methods along the lines of - // Response.{GetHeader,AddHeader} and string constants for "Host", - // "User-Agent" and "Referer". - err = req.Header.WriteSubset(w, reqExcludeHeader) + err = req.Header.WriteSubset(bw, reqWriteExcludeHeader) if err != nil { return err } - if err = writeCookies(w, req.Cookie); err != nil { - return err - } - - io.WriteString(w, "\r\n") + io.WriteString(bw, "\r\n") // Write body and trailer - err = tw.WriteBody(w) + err = tw.WriteBody(bw) if err != nil { return err } - + bw.Flush() return nil } @@ -402,10 +428,6 @@ type chunkedReader struct { err os.Error } -func newChunkedReader(r *bufio.Reader) *chunkedReader { - return &chunkedReader{r: r} -} - func (cr *chunkedReader) beginChunk() { // chunk-size CRLF var line string @@ -485,13 +507,6 @@ func NewRequest(method, url string, body io.Reader) (*Request, os.Error) { req.ContentLength = int64(v.Len()) case *bytes.Buffer: req.ContentLength = int64(v.Len()) - default: - req.ContentLength = -1 // chunked - } - if req.ContentLength == 0 { - // To prevent chunking and disambiguate this - // from the default ContentLength zero value. - req.TransferEncoding = []string{"identity"} } } @@ -524,7 +539,7 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { } var f []string - if f = strings.Split(s, " ", 3); len(f) < 3 { + if f = strings.SplitN(s, " ", 3); len(f) < 3 { return nil, &badStringError{"malformed HTTP request", s} } req.Method, req.RawURL, req.Proto = f[0], f[1], f[2] @@ -559,13 +574,6 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { fixPragmaCacheControl(req.Header) - // Pull out useful fields as a convenience to clients. - req.Referer = req.Header.Get("Referer") - req.Header.Del("Referer") - - req.UserAgent = req.Header.Get("User-Agent") - req.Header.Del("User-Agent") - // TODO: Parse specific header values: // Accept // Accept-Encoding @@ -597,8 +605,6 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { return nil, err } - req.Cookie = readCookies(req.Header) - return req, nil } @@ -652,11 +658,11 @@ func ParseQuery(query string) (m Values, err os.Error) { } func parseQuery(m Values, query string) (err os.Error) { - for _, kv := range strings.Split(query, "&", -1) { + for _, kv := range strings.Split(query, "&") { if len(kv) == 0 { continue } - kvPair := strings.Split(kv, "=", 2) + kvPair := strings.SplitN(kv, "=", 2) var key, value string var e os.Error @@ -690,10 +696,10 @@ func (r *Request) ParseForm() (err os.Error) { } if r.Method == "POST" { if r.Body == nil { - return os.ErrorString("missing form body") + return os.NewError("missing form body") } ct := r.Header.Get("Content-Type") - switch strings.Split(ct, ";", 2)[0] { + switch strings.SplitN(ct, ";", 2)[0] { case "text/plain", "application/x-www-form-urlencoded", "": const maxFormSize = int64(10 << 20) // 10 MB is a lot of text. b, e := ioutil.ReadAll(io.LimitReader(r.Body, maxFormSize+1)) diff --git a/src/pkg/http/requestwrite_test.go b/src/pkg/http/requestwrite_test.go index 98fbcf459..0052c0cfc 100644 --- a/src/pkg/http/requestwrite_test.go +++ b/src/pkg/http/requestwrite_test.go @@ -6,6 +6,7 @@ package http import ( "bytes" + "fmt" "io" "io/ioutil" "os" @@ -15,7 +16,7 @@ import ( type reqWriteTest struct { Req Request - Body []byte + Body interface{} // optional []byte or func() io.ReadCloser to populate Req.Body Raw string RawProxy string } @@ -47,13 +48,12 @@ var reqWriteTests = []reqWriteTest{ "Accept-Language": {"en-us,en;q=0.5"}, "Keep-Alive": {"300"}, "Proxy-Connection": {"keep-alive"}, + "User-Agent": {"Fake"}, }, - Body: nil, - Close: false, - Host: "www.techcrunch.com", - Referer: "", - UserAgent: "Fake", - Form: map[string][]string{}, + Body: nil, + Close: false, + Host: "www.techcrunch.com", + Form: map[string][]string{}, }, nil, @@ -99,13 +99,13 @@ var reqWriteTests = []reqWriteTest{ "Host: www.google.com\r\n" + "User-Agent: Go http package\r\n" + "Transfer-Encoding: chunked\r\n\r\n" + - "6\r\nabcdef\r\n0\r\n\r\n", + chunk("abcdef") + chunk(""), "GET http://www.google.com/search HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "User-Agent: Go http package\r\n" + "Transfer-Encoding: chunked\r\n\r\n" + - "6\r\nabcdef\r\n0\r\n\r\n", + chunk("abcdef") + chunk(""), }, // HTTP/1.1 POST => chunked coding; body; empty trailer { @@ -130,14 +130,14 @@ var reqWriteTests = []reqWriteTest{ "User-Agent: Go http package\r\n" + "Connection: close\r\n" + "Transfer-Encoding: chunked\r\n\r\n" + - "6\r\nabcdef\r\n0\r\n\r\n", + chunk("abcdef") + chunk(""), "POST http://www.google.com/search HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "User-Agent: Go http package\r\n" + "Connection: close\r\n" + "Transfer-Encoding: chunked\r\n\r\n" + - "6\r\nabcdef\r\n0\r\n\r\n", + chunk("abcdef") + chunk(""), }, // HTTP/1.1 POST with Content-Length, no chunking @@ -225,13 +225,75 @@ var reqWriteTests = []reqWriteTest{ "User-Agent: Go http package\r\n" + "\r\n", }, + + // Request with a 0 ContentLength and a 0 byte body. + { + Request{ + Method: "POST", + RawURL: "/", + Host: "example.com", + ProtoMajor: 1, + ProtoMinor: 1, + ContentLength: 0, // as if unset by user + }, + + func() io.ReadCloser { return ioutil.NopCloser(io.LimitReader(strings.NewReader("xx"), 0)) }, + + "POST / HTTP/1.1\r\n" + + "Host: example.com\r\n" + + "User-Agent: Go http package\r\n" + + "\r\n", + + "POST / HTTP/1.1\r\n" + + "Host: example.com\r\n" + + "User-Agent: Go http package\r\n" + + "\r\n", + }, + + // Request with a 0 ContentLength and a 1 byte body. + { + Request{ + Method: "POST", + RawURL: "/", + Host: "example.com", + ProtoMajor: 1, + ProtoMinor: 1, + ContentLength: 0, // as if unset by user + }, + + func() io.ReadCloser { return ioutil.NopCloser(io.LimitReader(strings.NewReader("xx"), 1)) }, + + "POST / HTTP/1.1\r\n" + + "Host: example.com\r\n" + + "User-Agent: Go http package\r\n" + + "Transfer-Encoding: chunked\r\n\r\n" + + chunk("x") + chunk(""), + + "POST / HTTP/1.1\r\n" + + "Host: example.com\r\n" + + "User-Agent: Go http package\r\n" + + "Transfer-Encoding: chunked\r\n\r\n" + + chunk("x") + chunk(""), + }, } func TestRequestWrite(t *testing.T) { for i := range reqWriteTests { tt := &reqWriteTests[i] + + setBody := func() { + switch b := tt.Body.(type) { + case []byte: + tt.Req.Body = ioutil.NopCloser(bytes.NewBuffer(b)) + case func() io.ReadCloser: + tt.Req.Body = b() + } + } if tt.Body != nil { - tt.Req.Body = ioutil.NopCloser(bytes.NewBuffer(tt.Body)) + setBody() + } + if tt.Req.Header == nil { + tt.Req.Header = make(Header) } var braw bytes.Buffer err := tt.Req.Write(&braw) @@ -246,7 +308,7 @@ func TestRequestWrite(t *testing.T) { } if tt.Body != nil { - tt.Req.Body = ioutil.NopCloser(bytes.NewBuffer(tt.Body)) + setBody() } var praw bytes.Buffer err = tt.Req.WriteProxy(&praw) @@ -278,41 +340,30 @@ func (rc *closeChecker) Close() os.Error { func TestRequestWriteClosesBody(t *testing.T) { rc := &closeChecker{Reader: strings.NewReader("my body")} req, _ := NewRequest("POST", "http://foo.com/", rc) - if g, e := req.ContentLength, int64(-1); g != e { - t.Errorf("got req.ContentLength %d, want %d", g, e) + if req.ContentLength != 0 { + t.Errorf("got req.ContentLength %d, want 0", req.ContentLength) } buf := new(bytes.Buffer) req.Write(buf) if !rc.closed { t.Error("body not closed after write") } - if g, e := buf.String(), "POST / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: Go http package\r\nTransfer-Encoding: chunked\r\n\r\n7\r\nmy body\r\n0\r\n\r\n"; g != e { - t.Errorf("write:\n got: %s\nwant: %s", g, e) + expected := "POST / HTTP/1.1\r\n" + + "Host: foo.com\r\n" + + "User-Agent: Go http package\r\n" + + "Transfer-Encoding: chunked\r\n\r\n" + + // TODO: currently we don't buffer before chunking, so we get a + // single "m" chunk before the other chunks, as this was the 1-byte + // read from our MultiReader where we stiched the Body back together + // after sniffing whether the Body was 0 bytes or not. + chunk("m") + + chunk("y body") + + chunk("") + if buf.String() != expected { + t.Errorf("write:\n got: %s\nwant: %s", buf.String(), expected) } } -func TestZeroLengthNewRequest(t *testing.T) { - var buf bytes.Buffer - - // Writing with default identity encoding - req, _ := NewRequest("PUT", "http://foo.com/", strings.NewReader("")) - if len(req.TransferEncoding) == 0 || req.TransferEncoding[0] != "identity" { - t.Fatalf("got req.TransferEncoding of %v, want %v", req.TransferEncoding, []string{"identity"}) - } - if g, e := req.ContentLength, int64(0); g != e { - t.Errorf("got req.ContentLength %d, want %d", g, e) - } - req.Write(&buf) - if g, e := buf.String(), "PUT / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: Go http package\r\nContent-Length: 0\r\n\r\n"; g != e { - t.Errorf("identity write:\n got: %s\nwant: %s", g, e) - } - - // Overriding identity encoding and forcing chunked. - req, _ = NewRequest("PUT", "http://foo.com/", strings.NewReader("")) - req.TransferEncoding = nil - buf.Reset() - req.Write(&buf) - if g, e := buf.String(), "PUT / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: Go http package\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\n"; g != e { - t.Errorf("chunked write:\n got: %s\nwant: %s", g, e) - } +func chunk(s string) string { + return fmt.Sprintf("%x\r\n%s\r\n", len(s), s) } diff --git a/src/pkg/http/response.go b/src/pkg/http/response.go index 42e60c1f6..915327a69 100644 --- a/src/pkg/http/response.go +++ b/src/pkg/http/response.go @@ -40,9 +40,6 @@ type Response struct { // Keys in the map are canonicalized (see CanonicalHeaderKey). Header Header - // SetCookie records the Set-Cookie requests sent with the response. - SetCookie []*Cookie - // Body represents the response body. Body io.ReadCloser @@ -71,6 +68,11 @@ type Response struct { Request *Request } +// Cookies parses and returns the cookies set in the Set-Cookie headers. +func (r *Response) Cookies() []*Cookie { + return readSetCookies(r.Header) +} + // ReadResponse reads and returns an HTTP response from r. The // req parameter specifies the Request that corresponds to // this Response. Clients must call resp.Body.Close when finished @@ -93,7 +95,7 @@ func ReadResponse(r *bufio.Reader, req *Request) (resp *Response, err os.Error) } return nil, err } - f := strings.Split(line, " ", 3) + f := strings.SplitN(line, " ", 3) if len(f) < 2 { return nil, &badStringError{"malformed HTTP response", line} } @@ -127,8 +129,6 @@ func ReadResponse(r *bufio.Reader, req *Request) (resp *Response, err os.Error) return nil, err } - resp.SetCookie = readSetCookies(resp.Header) - return resp, nil } @@ -200,10 +200,6 @@ func (resp *Response) Write(w io.Writer) os.Error { return err } - if err = writeSetCookies(w, resp.SetCookie); err != nil { - return err - } - // End-of-header io.WriteString(w, "\r\n") diff --git a/src/pkg/http/reverseproxy.go b/src/pkg/http/reverseproxy.go index 9a9e21599..e4ce1e34c 100644 --- a/src/pkg/http/reverseproxy.go +++ b/src/pkg/http/reverseproxy.go @@ -92,10 +92,6 @@ func (p *ReverseProxy) ServeHTTP(rw ResponseWriter, req *Request) { } } - for _, cookie := range res.SetCookie { - SetCookie(rw, cookie) - } - rw.WriteHeader(res.StatusCode) if res.Body != nil { diff --git a/src/pkg/http/reverseproxy_test.go b/src/pkg/http/reverseproxy_test.go index d7bcde90d..b2dd24633 100644 --- a/src/pkg/http/reverseproxy_test.go +++ b/src/pkg/http/reverseproxy_test.go @@ -17,6 +17,9 @@ func TestReverseProxy(t *testing.T) { const backendResponse = "I am the backend" const backendStatus = 404 backend := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + if len(r.TransferEncoding) > 0 { + t.Errorf("backend got unexpected TransferEncoding: %v", r.TransferEncoding) + } if r.Header.Get("X-Forwarded-For") == "" { t.Errorf("didn't get X-Forwarded-For header") } @@ -49,10 +52,10 @@ func TestReverseProxy(t *testing.T) { if g, e := res.Header.Get("X-Foo"), "bar"; g != e { t.Errorf("got X-Foo %q; expected %q", g, e) } - if g, e := len(res.SetCookie), 1; g != e { + if g, e := len(res.Header["Set-Cookie"]), 1; g != e { t.Fatalf("got %d SetCookies, want %d", g, e) } - if cookie := res.SetCookie[0]; cookie.Name != "flavor" { + if cookie := res.Cookies()[0]; cookie.Name != "flavor" { t.Errorf("unexpected cookie %q", cookie.Name) } bodyBytes, _ := ioutil.ReadAll(res.Body) diff --git a/src/pkg/http/serve_test.go b/src/pkg/http/serve_test.go index dc4594a79..55a9cbf70 100644 --- a/src/pkg/http/serve_test.go +++ b/src/pkg/http/serve_test.go @@ -373,11 +373,8 @@ func TestIdentityResponse(t *testing.T) { } } -// TestServeHTTP10Close verifies that HTTP/1.0 requests won't be kept alive. -func TestServeHTTP10Close(t *testing.T) { - s := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { - ServeFile(w, r, "testdata/file") - })) +func testTcpConnectionCloses(t *testing.T, req string, h Handler) { + s := httptest.NewServer(h) defer s.Close() conn, err := net.Dial("tcp", s.Listener.Addr().String()) @@ -386,7 +383,7 @@ func TestServeHTTP10Close(t *testing.T) { } defer conn.Close() - _, err = fmt.Fprint(conn, "GET / HTTP/1.0\r\n\r\n") + _, err = fmt.Fprint(conn, req) if err != nil { t.Fatal("print error:", err) } @@ -414,6 +411,27 @@ func TestServeHTTP10Close(t *testing.T) { success <- true } +// TestServeHTTP10Close verifies that HTTP/1.0 requests won't be kept alive. +func TestServeHTTP10Close(t *testing.T) { + testTcpConnectionCloses(t, "GET / HTTP/1.0\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { + ServeFile(w, r, "testdata/file") + })) +} + +// TestHandlersCanSetConnectionClose verifies that handlers can force a connection to close, +// even for HTTP/1.1 requests. +func TestHandlersCanSetConnectionClose11(t *testing.T) { + testTcpConnectionCloses(t, "GET / HTTP/1.1\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { + w.Header().Set("Connection", "close") + })) +} + +func TestHandlersCanSetConnectionClose10(t *testing.T) { + testTcpConnectionCloses(t, "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { + w.Header().Set("Connection", "close") + })) +} + func TestSetsRemoteAddr(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { fmt.Fprintf(w, "%s", r.RemoteAddr) @@ -522,7 +540,12 @@ func TestHeadResponses(t *testing.T) { func TestTLSServer(t *testing.T) { ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) { - fmt.Fprintf(w, "tls=%v", r.TLS != nil) + if r.TLS != nil { + w.Header().Set("X-TLS-Set", "true") + if r.TLS.HandshakeComplete { + w.Header().Set("X-TLS-HandshakeComplete", "true") + } + } })) defer ts.Close() if !strings.HasPrefix(ts.URL, "https://") { @@ -530,20 +553,17 @@ func TestTLSServer(t *testing.T) { } res, err := Get(ts.URL) if err != nil { - t.Error(err) + t.Fatal(err) } if res == nil { t.Fatalf("got nil Response") } - if res.Body == nil { - t.Fatalf("got nil Response.Body") - } - body, err := ioutil.ReadAll(res.Body) - if err != nil { - t.Error(err) + defer res.Body.Close() + if res.Header.Get("X-TLS-Set") != "true" { + t.Errorf("expected X-TLS-Set response header") } - if e, g := "tls=true", string(body); e != g { - t.Errorf("expected body %q; got %q", e, g) + if res.Header.Get("X-TLS-HandshakeComplete") != "true" { + t.Errorf("expected X-TLS-HandshakeComplete header") } } @@ -781,6 +801,45 @@ func TestHandlerPanic(t *testing.T) { } } +func TestNoDate(t *testing.T) { + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + w.Header()["Date"] = nil + })) + defer ts.Close() + res, err := Get(ts.URL) + if err != nil { + t.Fatal(err) + } + _, present := res.Header["Date"] + if present { + t.Fatalf("Expected no Date header; got %v", res.Header["Date"]) + } +} + +func TestStripPrefix(t *testing.T) { + h := HandlerFunc(func(w ResponseWriter, r *Request) { + w.Header().Set("X-Path", r.URL.Path) + }) + ts := httptest.NewServer(StripPrefix("/foo", h)) + defer ts.Close() + + res, err := Get(ts.URL + "/foo/bar") + if err != nil { + t.Fatal(err) + } + if g, e := res.Header.Get("X-Path"), "/bar"; g != e { + t.Errorf("test 1: got %s, want %s", g, e) + } + + res, err = Get(ts.URL + "/bar") + if err != nil { + t.Fatal(err) + } + if g, e := res.StatusCode, 404; g != e { + t.Errorf("test 2: got status %v, want %v", g, e) + } +} + type errorListener struct { errs []os.Error } diff --git a/src/pkg/http/server.go b/src/pkg/http/server.go index d4638f127..ab960f4f0 100644 --- a/src/pkg/http/server.go +++ b/src/pkg/http/server.go @@ -20,7 +20,7 @@ import ( "net" "os" "path" - "runtime" + "runtime/debug" "strconv" "strings" "sync" @@ -152,6 +152,7 @@ func newConn(rwc net.Conn, handler Handler) (c *conn, err os.Error) { c.buf = bufio.NewReadWriter(br, bw) if tlsConn, ok := rwc.(*tls.Conn); ok { + tlsConn.Handshake() c.tlsState = new(tls.ConnectionState) *c.tlsState = tlsConn.ConnectionState() } @@ -254,7 +255,7 @@ func (w *response) WriteHeader(code int) { } } - if w.header.Get("Date") == "" { + if _, ok := w.header["Date"]; !ok { w.Header().Set("Date", time.UTC().Format(TimeFormat)) } @@ -314,6 +315,10 @@ func (w *response) WriteHeader(code int) { w.closeAfterReply = true } + if w.header.Get("Connection") == "close" { + w.closeAfterReply = true + } + // Cannot use Content-Length with non-identity Transfer-Encoding. if w.chunking { w.header.Del("Content-Length") @@ -405,7 +410,7 @@ func errorKludge(w *response) { // Is it a broken browser? var msg string - switch agent := w.req.UserAgent; { + switch agent := w.req.UserAgent(); { case strings.Contains(agent, "MSIE"): msg = "Internet Explorer" case strings.Contains(agent, "Chrome/"): @@ -416,7 +421,7 @@ func errorKludge(w *response) { msg += " would ignore this error page if this text weren't here.\n" // Is it text? ("Content-Type" is always in the map) - baseType := strings.Split(w.header.Get("Content-Type"), ";", 2)[0] + baseType := strings.SplitN(w.header.Get("Content-Type"), ";", 2)[0] switch baseType { case "text/html": io.WriteString(w, "<!-- ") @@ -490,23 +495,9 @@ func (c *conn) serve() { } c.rwc.Close() - // TODO(rsc,bradfitz): this is boilerplate. move it to runtime.Stack() var buf bytes.Buffer fmt.Fprintf(&buf, "http: panic serving %v: %v\n", c.remoteAddr, err) - for i := 1; i < 20; i++ { - pc, file, line, ok := runtime.Caller(i) - if !ok { - break - } - var name string - f := runtime.FuncForPC(pc) - if f != nil { - name = f.Name() - } else { - name = fmt.Sprintf("%#x", pc) - } - fmt.Fprintf(&buf, " %s %s:%d\n", name, file, line) - } + buf.Write(debug.Stack()) log.Print(buf.String()) }() @@ -584,7 +575,7 @@ func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err os.Error) // Handler object that calls f. type HandlerFunc func(ResponseWriter, *Request) -// ServeHTTP calls f(w, req). +// ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) } @@ -605,6 +596,22 @@ func NotFound(w ResponseWriter, r *Request) { Error(w, "404 page not found", Sta // that replies to each request with a ``404 page not found'' reply. func NotFoundHandler() Handler { return HandlerFunc(NotFound) } +// StripPrefix returns a handler that serves HTTP requests +// by removing the given prefix from the request URL's Path +// and invoking the handler h. StripPrefix handles a +// request for a path that doesn't begin with prefix by +// replying with an HTTP 404 not found error. +func StripPrefix(prefix string, h Handler) Handler { + return HandlerFunc(func(w ResponseWriter, r *Request) { + if !strings.HasPrefix(r.URL.Path, prefix) { + NotFound(w, r) + return + } + r.URL.Path = r.URL.Path[len(prefix):] + h.ServeHTTP(w, r) + }) +} + // Redirect replies to the request with a redirect to url, // which may be a path relative to the request path. func Redirect(w ResponseWriter, r *Request, url string, code int) { @@ -922,7 +929,9 @@ func ListenAndServe(addr string, handler Handler) os.Error { // ListenAndServeTLS acts identically to ListenAndServe, except that it // expects HTTPS connections. Additionally, files containing a certificate and -// matching private key for the server must be provided. +// matching private key for the server must be provided. If the certificate +// is signed by a certificate authority, the certFile should be the concatenation +// of the server's certificate followed by the CA's certificate. // // A trivial example server is: // @@ -947,6 +956,24 @@ func ListenAndServe(addr string, handler Handler) os.Error { // // One can use generate_cert.go in crypto/tls to generate cert.pem and key.pem. func ListenAndServeTLS(addr string, certFile string, keyFile string, handler Handler) os.Error { + server := &Server{Addr: addr, Handler: handler} + return server.ListenAndServeTLS(certFile, keyFile) +} + +// ListenAndServeTLS listens on the TCP network address srv.Addr and +// then calls Serve to handle requests on incoming TLS connections. +// +// Filenames containing a certificate and matching private key for +// the server must be provided. If the certificate is signed by a +// certificate authority, the certFile should be the concatenation +// of the server's certificate followed by the CA's certificate. +// +// If srv.Addr is blank, ":https" is used. +func (s *Server) ListenAndServeTLS(certFile, keyFile string) os.Error { + addr := s.Addr + if addr == "" { + addr = ":https" + } config := &tls.Config{ Rand: rand.Reader, Time: time.Seconds, @@ -966,7 +993,7 @@ func ListenAndServeTLS(addr string, certFile string, keyFile string, handler Han } tlsListener := tls.NewListener(conn, config) - return Serve(tlsListener, handler) + return s.Serve(tlsListener) } // TimeoutHandler returns a Handler that runs h with the given time limit. diff --git a/src/pkg/http/spdy/read.go b/src/pkg/http/spdy/read.go index 159dbc578..c6b6ab3af 100644 --- a/src/pkg/http/spdy/read.go +++ b/src/pkg/http/spdy/read.go @@ -80,7 +80,7 @@ func (frame *HeadersFrame) read(h ControlFrameHeader, f *Framer) os.Error { func newControlFrame(frameType ControlFrameType) (controlFrame, os.Error) { ctor, ok := cframeCtor[frameType] if !ok { - return nil, InvalidControlFrame + return nil, &Error{Err: InvalidControlFrame} } return ctor(), nil } @@ -97,30 +97,12 @@ var cframeCtor = map[ControlFrameType]func() controlFrame{ // TODO(willchan): Add TypeWindowUpdate } -type corkedReader struct { - r io.Reader - ch chan int - n int -} - -func (cr *corkedReader) Read(p []byte) (int, os.Error) { - if cr.n == 0 { - cr.n = <-cr.ch - } - if len(p) > cr.n { - p = p[:cr.n] - } - n, err := cr.r.Read(p) - cr.n -= n - return n, err -} - -func (f *Framer) uncorkHeaderDecompressor(payloadSize int) os.Error { +func (f *Framer) uncorkHeaderDecompressor(payloadSize int64) os.Error { if f.headerDecompressor != nil { - f.headerReader.ch <- payloadSize + f.headerReader.N = payloadSize return nil } - f.headerReader = corkedReader{r: f.r, ch: make(chan int, 1), n: payloadSize} + f.headerReader = io.LimitedReader{R: f.r, N: payloadSize} decompressor, err := zlib.NewReaderDict(&f.headerReader, []byte(HeaderDictionary)) if err != nil { return err @@ -161,11 +143,12 @@ func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) ( return cframe, nil } -func parseHeaderValueBlock(r io.Reader) (http.Header, os.Error) { +func parseHeaderValueBlock(r io.Reader, streamId uint32) (http.Header, os.Error) { var numHeaders uint16 if err := binary.Read(r, binary.BigEndian, &numHeaders); err != nil { return nil, err } + var e os.Error h := make(http.Header, int(numHeaders)) for i := 0; i < int(numHeaders); i++ { var length uint16 @@ -178,10 +161,11 @@ func parseHeaderValueBlock(r io.Reader) (http.Header, os.Error) { } name := string(nameBytes) if name != strings.ToLower(name) { - return nil, UnlowercasedHeaderName + e = &Error{UnlowercasedHeaderName, streamId} + name = strings.ToLower(name) } if h[name] != nil { - return nil, DuplicateHeaders + e = &Error{DuplicateHeaders, streamId} } if err := binary.Read(r, binary.BigEndian, &length); err != nil { return nil, err @@ -190,11 +174,14 @@ func parseHeaderValueBlock(r io.Reader) (http.Header, os.Error) { if _, err := io.ReadFull(r, value); err != nil { return nil, err } - valueList := strings.Split(string(value), "\x00", -1) + valueList := strings.Split(string(value), "\x00") for _, v := range valueList { h.Add(name, v) } } + if e != nil { + return h, e + } return h, nil } @@ -214,14 +201,25 @@ func (f *Framer) readSynStreamFrame(h ControlFrameHeader, frame *SynStreamFrame) reader := f.r if !f.headerCompressionDisabled { - f.uncorkHeaderDecompressor(int(h.length - 10)) + f.uncorkHeaderDecompressor(int64(h.length - 10)) reader = f.headerDecompressor } - frame.Headers, err = parseHeaderValueBlock(reader) + frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && ((err == os.EOF && f.headerReader.N == 0) || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } if err != nil { return err } + // Remove this condition when we bump Version to 3. + if Version >= 3 { + for h, _ := range frame.Headers { + if invalidReqHeaders[h] { + return &Error{InvalidHeaderPresent, frame.StreamId} + } + } + } return nil } @@ -237,13 +235,24 @@ func (f *Framer) readSynReplyFrame(h ControlFrameHeader, frame *SynReplyFrame) o } reader := f.r if !f.headerCompressionDisabled { - f.uncorkHeaderDecompressor(int(h.length - 6)) + f.uncorkHeaderDecompressor(int64(h.length - 6)) reader = f.headerDecompressor } - frame.Headers, err = parseHeaderValueBlock(reader) + frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && ((err == os.EOF && f.headerReader.N == 0) || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } if err != nil { return err } + // Remove this condition when we bump Version to 3. + if Version >= 3 { + for h, _ := range frame.Headers { + if invalidRespHeaders[h] { + return &Error{InvalidHeaderPresent, frame.StreamId} + } + } + } return nil } @@ -259,13 +268,31 @@ func (f *Framer) readHeadersFrame(h ControlFrameHeader, frame *HeadersFrame) os. } reader := f.r if !f.headerCompressionDisabled { - f.uncorkHeaderDecompressor(int(h.length - 6)) + f.uncorkHeaderDecompressor(int64(h.length - 6)) reader = f.headerDecompressor } - frame.Headers, err = parseHeaderValueBlock(reader) + frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) + if !f.headerCompressionDisabled && ((err == os.EOF && f.headerReader.N == 0) || f.headerReader.N != 0) { + err = &Error{WrongCompressedPayloadSize, 0} + } if err != nil { return err } + + // Remove this condition when we bump Version to 3. + if Version >= 3 { + var invalidHeaders map[string]bool + if frame.StreamId%2 == 0 { + invalidHeaders = invalidReqHeaders + } else { + invalidHeaders = invalidRespHeaders + } + for h, _ := range frame.Headers { + if invalidHeaders[h] { + return &Error{InvalidHeaderPresent, frame.StreamId} + } + } + } return nil } @@ -279,7 +306,6 @@ func (f *Framer) parseDataFrame(streamId uint32) (*DataFrame, os.Error) { frame.Flags = DataFlags(length >> 24) length &= 0xffffff frame.Data = make([]byte, length) - // TODO(willchan): Support compressed data frames. if _, err := io.ReadFull(f.r, frame.Data); err != nil { return nil, err } diff --git a/src/pkg/http/spdy/spdy_test.go b/src/pkg/http/spdy/spdy_test.go index 9100e1ea8..cb91e0286 100644 --- a/src/pkg/http/spdy/spdy_test.go +++ b/src/pkg/http/spdy/spdy_test.go @@ -21,7 +21,8 @@ func TestHeaderParsing(t *testing.T) { var headerValueBlockBuf bytes.Buffer writeHeaderValueBlock(&headerValueBlockBuf, headers) - newHeaders, err := parseHeaderValueBlock(&headerValueBlockBuf) + const bogusStreamId = 1 + newHeaders, err := parseHeaderValueBlock(&headerValueBlockBuf, bogusStreamId) if err != nil { t.Fatal("parseHeaderValueBlock:", err) } diff --git a/src/pkg/http/spdy/types.go b/src/pkg/http/spdy/types.go index 5a665f04f..41cafb174 100644 --- a/src/pkg/http/spdy/types.go +++ b/src/pkg/http/spdy/types.go @@ -10,7 +10,6 @@ import ( "http" "io" "os" - "strconv" ) // Data Frame Format @@ -302,33 +301,41 @@ const HeaderDictionary = "optionsgetheadpostputdeletetrace" + "chunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplication/xhtmltext/plainpublicmax-age" + "charset=iso-8859-1utf-8gzipdeflateHTTP/1.1statusversionurl\x00" -type FramerError int +// A SPDY specific error. +type ErrorCode string const ( - Internal FramerError = iota - InvalidControlFrame - UnlowercasedHeaderName - DuplicateHeaders - UnknownFrameType - InvalidDataFrame + UnlowercasedHeaderName ErrorCode = "header was not lowercased" + DuplicateHeaders ErrorCode = "multiple headers with same name" + WrongCompressedPayloadSize ErrorCode = "compressed payload size was incorrect" + UnknownFrameType ErrorCode = "unknown frame type" + InvalidControlFrame ErrorCode = "invalid control frame" + InvalidDataFrame ErrorCode = "invalid data frame" + InvalidHeaderPresent ErrorCode = "frame contained invalid header" ) -func (e FramerError) String() string { - switch e { - case Internal: - return "Internal" - case InvalidControlFrame: - return "InvalidControlFrame" - case UnlowercasedHeaderName: - return "UnlowercasedHeaderName" - case DuplicateHeaders: - return "DuplicateHeaders" - case UnknownFrameType: - return "UnknownFrameType" - case InvalidDataFrame: - return "InvalidDataFrame" - } - return "Error(" + strconv.Itoa(int(e)) + ")" +// Error contains both the type of error and additional values. StreamId is 0 +// if Error is not associated with a stream. +type Error struct { + Err ErrorCode + StreamId uint32 +} + +func (e *Error) String() string { + return string(e.Err) +} + +var invalidReqHeaders = map[string]bool{ + "Connection": true, + "Keep-Alive": true, + "Proxy-Connection": true, + "Transfer-Encoding": true, +} + +var invalidRespHeaders = map[string]bool{ + "Connection": true, + "Keep-Alive": true, + "Transfer-Encoding": true, } // Framer handles serializing/deserializing SPDY frames, including compressing/ @@ -339,7 +346,7 @@ type Framer struct { headerBuf *bytes.Buffer headerCompressor *zlib.Writer r io.Reader - headerReader corkedReader + headerReader io.LimitedReader headerDecompressor io.ReadCloser } diff --git a/src/pkg/http/spdy/write.go b/src/pkg/http/spdy/write.go index aa1679f1b..7d40bbe9f 100644 --- a/src/pkg/http/spdy/write.go +++ b/src/pkg/http/spdy/write.go @@ -267,10 +267,9 @@ func (f *Framer) writeHeadersFrame(frame *HeadersFrame) (err os.Error) { func (f *Framer) writeDataFrame(frame *DataFrame) (err os.Error) { // Validate DataFrame if frame.StreamId&0x80000000 != 0 || len(frame.Data) >= 0x0f000000 { - return InvalidDataFrame + return &Error{InvalidDataFrame, frame.StreamId} } - // TODO(willchan): Support data compression. // Serialize frame to Writer if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { return diff --git a/src/pkg/http/transfer.go b/src/pkg/http/transfer.go index b54508e7a..b65d99a6f 100644 --- a/src/pkg/http/transfer.go +++ b/src/pkg/http/transfer.go @@ -5,6 +5,7 @@ package http import ( + "bytes" "bufio" "io" "io/ioutil" @@ -17,7 +18,8 @@ import ( // sanitizes them without changing the user object and provides methods for // writing the respective header, body and trailer in wire format. type transferWriter struct { - Body io.ReadCloser + Body io.Reader + BodyCloser io.Closer ResponseToHEAD bool ContentLength int64 Close bool @@ -33,16 +35,37 @@ func newTransferWriter(r interface{}) (t *transferWriter, err os.Error) { switch rr := r.(type) { case *Request: t.Body = rr.Body + t.BodyCloser = rr.Body t.ContentLength = rr.ContentLength t.Close = rr.Close t.TransferEncoding = rr.TransferEncoding t.Trailer = rr.Trailer atLeastHTTP11 = rr.ProtoAtLeast(1, 1) - if t.Body != nil && t.ContentLength <= 0 && len(t.TransferEncoding) == 0 && atLeastHTTP11 { - t.TransferEncoding = []string{"chunked"} + if t.Body != nil && len(t.TransferEncoding) == 0 && atLeastHTTP11 { + if t.ContentLength == 0 { + // Test to see if it's actually zero or just unset. + var buf [1]byte + n, _ := io.ReadFull(t.Body, buf[:]) + if n == 1 { + // Oh, guess there is data in this Body Reader after all. + // The ContentLength field just wasn't set. + // Stich the Body back together again, re-attaching our + // consumed byte. + t.ContentLength = -1 + t.Body = io.MultiReader(bytes.NewBuffer(buf[:]), t.Body) + } else { + // Body is actually empty. + t.Body = nil + t.BodyCloser = nil + } + } + if t.ContentLength < 0 { + t.TransferEncoding = []string{"chunked"} + } } case *Response: t.Body = rr.Body + t.BodyCloser = rr.Body t.ContentLength = rr.ContentLength t.Close = rr.Close t.TransferEncoding = rr.TransferEncoding @@ -147,7 +170,7 @@ func (t *transferWriter) WriteBody(w io.Writer) (err os.Error) { if err != nil { return err } - if err = t.Body.Close(); err != nil { + if err = t.BodyCloser.Close(); err != nil { return err } } @@ -195,6 +218,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) { t := &transferReader{} // Unify input + isResponse := false switch rr := msg.(type) { case *Response: t.Header = rr.Header @@ -203,6 +227,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) { t.ProtoMajor = rr.ProtoMajor t.ProtoMinor = rr.ProtoMinor t.Close = shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header) + isResponse = true case *Request: t.Header = rr.Header t.ProtoMajor = rr.ProtoMajor @@ -211,6 +236,8 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) { // Responses with status code 200, responding to a GET method t.StatusCode = 200 t.RequestMethod = "GET" + default: + panic("unexpected type") } // Default to HTTP/1.1 @@ -224,7 +251,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) { return err } - t.ContentLength, err = fixLength(t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding) + t.ContentLength, err = fixLength(isResponse, t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding) if err != nil { return err } @@ -252,7 +279,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) { // or close connection when finished, since multipart is not supported yet switch { case chunked(t.TransferEncoding): - t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close} + t.Body = &body{Reader: NewChunkedReader(r), hdr: msg, r: r, closing: t.Close} case t.ContentLength >= 0: // TODO: limit the Content-Length. This is an easy DoS vector. t.Body = &body{Reader: io.LimitReader(r, t.ContentLength), closing: t.Close} @@ -265,9 +292,6 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) { // Persistent connection (i.e. HTTP/1.1) t.Body = &body{Reader: io.LimitReader(r, 0), closing: t.Close} } - // TODO(petar): It may be a good idea, for extra robustness, to - // assume ContentLength=0 for GET requests (and other special - // cases?). This logic should be in fixLength(). } // Unify output @@ -310,7 +334,7 @@ func fixTransferEncoding(requestMethod string, header Header) ([]string, os.Erro return nil, nil } - encodings := strings.Split(raw[0], ",", -1) + encodings := strings.Split(raw[0], ",") te := make([]string, 0, len(encodings)) // TODO: Even though we only support "identity" and "chunked" // encodings, the loop below is designed with foresight. One @@ -345,7 +369,7 @@ func fixTransferEncoding(requestMethod string, header Header) ([]string, os.Erro // Determine the expected body length, using RFC 2616 Section 4.4. This // function is not a method, because ultimately it should be shared by // ReadResponse and ReadRequest. -func fixLength(status int, requestMethod string, header Header, te []string) (int64, os.Error) { +func fixLength(isResponse bool, status int, requestMethod string, header Header, te []string) (int64, os.Error) { // Logic based on response type or status if noBodyExpected(requestMethod) { @@ -376,6 +400,14 @@ func fixLength(status int, requestMethod string, header Header, te []string) (in header.Del("Content-Length") } + if !isResponse && requestMethod == "GET" { + // RFC 2616 doesn't explicitly permit nor forbid an + // entity-body on a GET request so we permit one if + // declared, but we default to 0 here (not -1 below) + // if there's no mention of a body. + return 0, nil + } + // Logic based on media type. The purpose of the following code is just // to detect whether the unsupported "multipart/byteranges" is being // used. A proper Content-Type parser is needed in the future. @@ -418,7 +450,7 @@ func fixTrailer(header Header, te []string) (Header, os.Error) { header.Del("Trailer") trailer := make(Header) - keys := strings.Split(raw, ",", -1) + keys := strings.Split(raw, ",") for _, key := range keys { key = CanonicalHeaderKey(strings.TrimSpace(key)) switch key { diff --git a/src/pkg/http/transport.go b/src/pkg/http/transport.go index c907d85fd..3c16c880d 100644 --- a/src/pkg/http/transport.go +++ b/src/pkg/http/transport.go @@ -76,12 +76,12 @@ func ProxyFromEnvironment(req *Request) (*URL, os.Error) { } proxyURL, err := ParseRequestURL(proxy) if err != nil { - return nil, os.ErrorString("invalid proxy address") + return nil, os.NewError("invalid proxy address") } if proxyURL.Host == "" { proxyURL, err = ParseRequestURL("http://" + proxy) if err != nil { - return nil, os.ErrorString("invalid proxy address") + return nil, os.NewError("invalid proxy address") } } return proxyURL, nil @@ -329,9 +329,9 @@ func (t *Transport) getConn(cm *connectMethod) (*persistConn, os.Error) { return nil, err } if resp.StatusCode != 200 { - f := strings.Split(resp.Status, " ", 2) + f := strings.SplitN(resp.Status, " ", 2) conn.Close() - return nil, os.ErrorString(f[1]) + return nil, os.NewError(f[1]) } } @@ -383,7 +383,7 @@ func useProxy(addr string) bool { addr = addr[:strings.LastIndex(addr, ":")] } - for _, p := range strings.Split(no_proxy, ",", -1) { + for _, p := range strings.Split(no_proxy, ",") { p = strings.ToLower(strings.TrimSpace(p)) if len(p) == 0 { continue diff --git a/src/pkg/http/url.go b/src/pkg/http/url.go index 05b1662d3..e934b27c4 100644 --- a/src/pkg/http/url.go +++ b/src/pkg/http/url.go @@ -299,7 +299,7 @@ func getscheme(rawurl string) (scheme, path string, err os.Error) { } case c == ':': if i == 0 { - return "", "", os.ErrorString("missing protocol scheme") + return "", "", os.NewError("missing protocol scheme") } return rawurl[0:i], rawurl[i+1:], nil default: @@ -348,8 +348,13 @@ func ParseRequestURL(rawurl string) (url *URL, err os.Error) { // in which case only absolute URLs or path-absolute relative URLs are allowed. // If viaRequest is false, all forms of relative URLs are allowed. func parseURL(rawurl string, viaRequest bool) (url *URL, err os.Error) { + var ( + leadingSlash bool + path string + ) + if rawurl == "" { - err = os.ErrorString("empty url") + err = os.NewError("empty url") goto Error } url = new(URL) @@ -357,12 +362,10 @@ func parseURL(rawurl string, viaRequest bool) (url *URL, err os.Error) { // Split off possible leading "http:", "mailto:", etc. // Cannot contain escaped characters. - var path string if url.Scheme, path, err = getscheme(rawurl); err != nil { goto Error } - - leadingSlash := strings.HasPrefix(path, "/") + leadingSlash = strings.HasPrefix(path, "/") if url.Scheme != "" && !leadingSlash { // RFC 2396: @@ -377,7 +380,7 @@ func parseURL(rawurl string, viaRequest bool) (url *URL, err os.Error) { url.OpaquePath = true } else { if viaRequest && !leadingSlash { - err = os.ErrorString("invalid URI for request") + err = os.NewError("invalid URI for request") goto Error } @@ -411,7 +414,7 @@ func parseURL(rawurl string, viaRequest bool) (url *URL, err os.Error) { if strings.Contains(rawHost, "%") { // Host cannot contain escaped characters. - err = os.ErrorString("hexadecimal escape in host") + err = os.NewError("hexadecimal escape in host") goto Error } url.Host = rawHost @@ -505,8 +508,8 @@ func (v Values) Encode() string { // resolvePath applies special path segments from refs and applies // them to base, per RFC 2396. func resolvePath(basepath string, refpath string) string { - base := strings.Split(basepath, "/", -1) - refs := strings.Split(refpath, "/", -1) + base := strings.Split(basepath, "/") + refs := strings.Split(refpath, "/") if len(base) == 0 { base = []string{""} } diff --git a/src/pkg/image/draw/draw.go b/src/pkg/image/draw/draw.go index 618fb4aa6..0ab7b59ab 100644 --- a/src/pkg/image/draw/draw.go +++ b/src/pkg/image/draw/draw.go @@ -34,9 +34,9 @@ type Image interface { Set(x, y int, c image.Color) } -// Draw calls DrawMask with a nil mask and an Over op. -func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point) { - DrawMask(dst, r, src, sp, nil, image.ZP, Over) +// Draw calls DrawMask with a nil mask. +func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op) { + DrawMask(dst, r, src, sp, nil, image.ZP, op) } // clip clips r against each image's bounds (after translating into the diff --git a/src/pkg/image/draw/draw_test.go b/src/pkg/image/draw/draw_test.go index 37d630353..55435cc27 100644 --- a/src/pkg/image/draw/draw_test.go +++ b/src/pkg/image/draw/draw_test.go @@ -154,22 +154,32 @@ var drawTests = []drawTest{ {"genericSrc", fillBlue(255), vgradAlpha(192), Src, image.RGBAColor{0, 0, 102, 102}}, } -func makeGolden(dst, src, mask image.Image, op Op) image.Image { +func makeGolden(dst image.Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) image.Image { // Since golden is a newly allocated image, we don't have to check if the // input source and mask images and the output golden image overlap. b := dst.Bounds() - sx0 := src.Bounds().Min.X - b.Min.X - sy0 := src.Bounds().Min.Y - b.Min.Y - var mx0, my0 int + sb := src.Bounds() + mb := image.Rect(-1e9, -1e9, 1e9, 1e9) if mask != nil { - mx0 = mask.Bounds().Min.X - b.Min.X - my0 = mask.Bounds().Min.Y - b.Min.Y + mb = mask.Bounds() } golden := image.NewRGBA(b.Max.X, b.Max.Y) - for y := b.Min.Y; y < b.Max.Y; y++ { - my, sy := my0+y, sy0+y - for x := b.Min.X; x < b.Max.X; x++ { - mx, sx := mx0+x, sx0+x + for y := r.Min.Y; y < r.Max.Y; y++ { + sy := y + sp.Y - r.Min.Y + my := y + mp.Y - r.Min.Y + for x := r.Min.X; x < r.Max.X; x++ { + if !(image.Point{x, y}.In(b)) { + continue + } + sx := x + sp.X - r.Min.X + if !(image.Point{sx, sy}.In(sb)) { + continue + } + mx := x + mp.X - r.Min.X + if !(image.Point{mx, my}.In(mb)) { + continue + } + const M = 1<<16 - 1 var dr, dg, db, da uint32 if op == Over { @@ -189,35 +199,49 @@ func makeGolden(dst, src, mask image.Image, op Op) image.Image { }) } } - golden.Rect = b - return golden + return golden.SubImage(b) } func TestDraw(t *testing.T) { -loop: - for _, test := range drawTests { - dst := hgradRed(255) - // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation. - golden := makeGolden(dst, test.src, test.mask, test.op) - b := dst.Bounds() - if !b.Eq(golden.Bounds()) { - t.Errorf("draw %s: bounds %v versus %v", test.desc, dst.Bounds(), golden.Bounds()) - continue - } - // Draw the same combination onto the actual dst using the optimized DrawMask implementation. - DrawMask(dst, b, test.src, image.ZP, test.mask, image.ZP, test.op) - // Check that the resultant pixel at (8, 8) matches what we expect - // (the expected value can be verified by hand). - if !eq(dst.At(8, 8), test.expected) { - t.Errorf("draw %s: at (8, 8) %v versus %v", test.desc, dst.At(8, 8), test.expected) - continue - } - // Check that the resultant dst image matches the golden output. - for y := b.Min.Y; y < b.Max.Y; y++ { - for x := b.Min.X; x < b.Max.X; x++ { - if !eq(dst.At(x, y), golden.At(x, y)) { - t.Errorf("draw %s: at (%d, %d), %v versus golden %v", test.desc, x, y, dst.At(x, y), golden.At(x, y)) - continue loop + rr := []image.Rectangle{ + image.Rect(0, 0, 0, 0), + image.Rect(0, 0, 16, 16), + image.Rect(3, 5, 12, 10), + image.Rect(0, 0, 9, 9), + image.Rect(8, 8, 16, 16), + image.Rect(8, 0, 9, 16), + image.Rect(0, 8, 16, 9), + image.Rect(8, 8, 9, 9), + image.Rect(8, 8, 8, 8), + } + for _, r := range rr { + loop: + for _, test := range drawTests { + dst := hgradRed(255).(*image.RGBA).SubImage(r).(Image) + // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation. + golden := makeGolden(dst, image.Rect(0, 0, 16, 16), test.src, image.ZP, test.mask, image.ZP, test.op) + b := dst.Bounds() + if !b.Eq(golden.Bounds()) { + t.Errorf("draw %v %s: bounds %v versus %v", r, test.desc, dst.Bounds(), golden.Bounds()) + continue + } + // Draw the same combination onto the actual dst using the optimized DrawMask implementation. + DrawMask(dst, image.Rect(0, 0, 16, 16), test.src, image.ZP, test.mask, image.ZP, test.op) + if image.Pt(8, 8).In(r) { + // Check that the resultant pixel at (8, 8) matches what we expect + // (the expected value can be verified by hand). + if !eq(dst.At(8, 8), test.expected) { + t.Errorf("draw %v %s: at (8, 8) %v versus %v", r, test.desc, dst.At(8, 8), test.expected) + continue + } + } + // Check that the resultant dst image matches the golden output. + for y := b.Min.Y; y < b.Max.Y; y++ { + for x := b.Min.X; x < b.Max.X; x++ { + if !eq(dst.At(x, y), golden.At(x, y)) { + t.Errorf("draw %v %s: at (%d, %d), %v versus golden %v", r, test.desc, x, y, dst.At(x, y), golden.At(x, y)) + continue loop + } } } } @@ -230,19 +254,11 @@ func TestDrawOverlap(t *testing.T) { loop: for xoff := -2; xoff <= 2; xoff++ { m := gradYellow(127).(*image.RGBA) - dst := &image.RGBA{ - Pix: m.Pix, - Stride: m.Stride, - Rect: image.Rect(5, 5, 10, 10), - } - src := &image.RGBA{ - Pix: m.Pix, - Stride: m.Stride, - Rect: image.Rect(5+xoff, 5+yoff, 10+xoff, 10+yoff), - } - // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation. - golden := makeGolden(dst, src, nil, op) + dst := m.SubImage(image.Rect(5, 5, 10, 10)).(*image.RGBA) + src := m.SubImage(image.Rect(5+xoff, 5+yoff, 10+xoff, 10+yoff)).(*image.RGBA) b := dst.Bounds() + // Draw the (src, mask, op) onto a copy of dst using a slow but obviously correct implementation. + golden := makeGolden(dst, b, src, src.Bounds().Min, nil, image.ZP, op) if !b.Eq(golden.Bounds()) { t.Errorf("drawOverlap xoff=%d,yoff=%d: bounds %v versus %v", xoff, yoff, dst.Bounds(), golden.Bounds()) continue @@ -271,8 +287,68 @@ func TestNonZeroSrcPt(t *testing.T) { b.Set(1, 0, image.RGBAColor{0, 0, 5, 5}) b.Set(0, 1, image.RGBAColor{0, 5, 0, 5}) b.Set(1, 1, image.RGBAColor{5, 0, 0, 5}) - Draw(a, image.Rect(0, 0, 1, 1), b, image.Pt(1, 1)) + Draw(a, image.Rect(0, 0, 1, 1), b, image.Pt(1, 1), Over) if !eq(image.RGBAColor{5, 0, 0, 5}, a.At(0, 0)) { t.Errorf("non-zero src pt: want %v got %v", image.RGBAColor{5, 0, 0, 5}, a.At(0, 0)) } } + +func TestFill(t *testing.T) { + rr := []image.Rectangle{ + image.Rect(0, 0, 0, 0), + image.Rect(0, 0, 40, 30), + image.Rect(10, 0, 40, 30), + image.Rect(0, 20, 40, 30), + image.Rect(10, 20, 40, 30), + image.Rect(10, 20, 15, 25), + image.Rect(10, 0, 35, 30), + image.Rect(0, 15, 40, 16), + image.Rect(24, 24, 25, 25), + image.Rect(23, 23, 26, 26), + image.Rect(22, 22, 27, 27), + image.Rect(21, 21, 28, 28), + image.Rect(20, 20, 29, 29), + } + for _, r := range rr { + m := image.NewRGBA(40, 30).SubImage(r).(*image.RGBA) + b := m.Bounds() + c := image.RGBAColor{11, 0, 0, 255} + src := &image.ColorImage{c} + check := func(desc string) { + for y := b.Min.Y; y < b.Max.Y; y++ { + for x := b.Min.X; x < b.Max.X; x++ { + if !eq(c, m.At(x, y)) { + t.Errorf("%s fill: at (%d, %d), sub-image bounds=%v: want %v got %v", desc, x, y, r, c, m.At(x, y)) + return + } + } + } + } + // Draw 1 pixel at a time. + for y := b.Min.Y; y < b.Max.Y; y++ { + for x := b.Min.X; x < b.Max.X; x++ { + DrawMask(m, image.Rect(x, y, x+1, y+1), src, image.ZP, nil, image.ZP, Src) + } + } + check("pixel") + // Draw 1 row at a time. + c = image.RGBAColor{0, 22, 0, 255} + src = &image.ColorImage{c} + for y := b.Min.Y; y < b.Max.Y; y++ { + DrawMask(m, image.Rect(b.Min.X, y, b.Max.X, y+1), src, image.ZP, nil, image.ZP, Src) + } + check("row") + // Draw 1 column at a time. + c = image.RGBAColor{0, 0, 33, 255} + src = &image.ColorImage{c} + for x := b.Min.X; x < b.Max.X; x++ { + DrawMask(m, image.Rect(x, b.Min.Y, x+1, b.Max.Y), src, image.ZP, nil, image.ZP, Src) + } + check("column") + // Draw the whole image at once. + c = image.RGBAColor{44, 55, 66, 77} + src = &image.ColorImage{c} + DrawMask(m, b, src, image.ZP, nil, image.ZP, Src) + check("whole") + } +} diff --git a/src/pkg/image/gif/reader.go b/src/pkg/image/gif/reader.go index 26c013b9a..98ac01cca 100644 --- a/src/pkg/image/gif/reader.go +++ b/src/pkg/image/gif/reader.go @@ -186,7 +186,7 @@ Loop: return err } if c != 0 { - return os.ErrorString("gif: extra data after image") + return os.NewError("gif: extra data after image") } // Undo the interlacing if necessary. diff --git a/src/pkg/image/image.go b/src/pkg/image/image.go index f4c38d28a..5ea302d0d 100644 --- a/src/pkg/image/image.go +++ b/src/pkg/image/image.go @@ -548,7 +548,7 @@ func NewGray16(w, h int) *Gray16 { return &Gray16{pix, w, Rectangle{ZP, Point{w, h}}} } -// A PalettedColorModel represents a fixed palette of colors. +// A PalettedColorModel represents a fixed palette of at most 256 colors. type PalettedColorModel []Color func diff(a, b uint32) uint32 { @@ -648,7 +648,20 @@ func (p *Paletted) SubImage(r Rectangle) Image { // Opaque scans the entire image and returns whether or not it is fully opaque. func (p *Paletted) Opaque() bool { - for _, c := range p.Palette { + var present [256]bool + base := p.Rect.Min.Y * p.Stride + i0, i1 := base+p.Rect.Min.X, base+p.Rect.Max.X + for y := p.Rect.Min.Y; y < p.Rect.Max.Y; y++ { + for _, c := range p.Pix[i0:i1] { + present[c] = true + } + i0 += p.Stride + i1 += p.Stride + } + for i, c := range p.Palette { + if !present[i] { + continue + } _, _, _, a := c.RGBA() if a != 0xffff { return false diff --git a/src/pkg/image/image_test.go b/src/pkg/image/image_test.go index 17e314795..5469d6423 100644 --- a/src/pkg/image/image_test.go +++ b/src/pkg/image/image_test.go @@ -8,6 +8,13 @@ import ( "testing" ) +type image interface { + Image + Opaque() bool + Set(int, int, Color) + SubImage(Rectangle) Image +} + func cmp(t *testing.T, cm ColorModel, c0, c1 Color) bool { r0, g0, b0, a0 := cm.Convert(c0).RGBA() r1, g1, b1, a1 := cm.Convert(c1).RGBA() @@ -15,12 +22,7 @@ func cmp(t *testing.T, cm ColorModel, c0, c1 Color) bool { } func TestImage(t *testing.T) { - type buffered interface { - Image - Set(int, int, Color) - SubImage(Rectangle) Image - } - testImage := []Image{ + testImage := []image{ NewRGBA(10, 10), NewRGBA64(10, 10), NewNRGBA(10, 10), @@ -35,36 +37,70 @@ func TestImage(t *testing.T) { }), } for _, m := range testImage { - b := m.(buffered) - if !Rect(0, 0, 10, 10).Eq(b.Bounds()) { - t.Errorf("%T: want bounds %v, got %v", b, Rect(0, 0, 10, 10), b.Bounds()) + if !Rect(0, 0, 10, 10).Eq(m.Bounds()) { + t.Errorf("%T: want bounds %v, got %v", m, Rect(0, 0, 10, 10), m.Bounds()) + continue + } + if !cmp(t, m.ColorModel(), Transparent, m.At(6, 3)) { + t.Errorf("%T: at (6, 3), want a zero color, got %v", m, m.At(6, 3)) continue } - if !cmp(t, b.ColorModel(), Transparent, b.At(6, 3)) { - t.Errorf("%T: at (6, 3), want a zero color, got %v", b, b.At(6, 3)) + m.Set(6, 3, Opaque) + if !cmp(t, m.ColorModel(), Opaque, m.At(6, 3)) { + t.Errorf("%T: at (6, 3), want a non-zero color, got %v", m, m.At(6, 3)) continue } - b.Set(6, 3, Opaque) - if !cmp(t, b.ColorModel(), Opaque, b.At(6, 3)) { - t.Errorf("%T: at (6, 3), want a non-zero color, got %v", b, b.At(6, 3)) + if !m.SubImage(Rect(6, 3, 7, 4)).(image).Opaque() { + t.Errorf("%T: at (6, 3) was not opaque", m) continue } - b = b.SubImage(Rect(3, 2, 9, 8)).(buffered) - if !Rect(3, 2, 9, 8).Eq(b.Bounds()) { - t.Errorf("%T: sub-image want bounds %v, got %v", b, Rect(3, 2, 9, 8), b.Bounds()) + m = m.SubImage(Rect(3, 2, 9, 8)).(image) + if !Rect(3, 2, 9, 8).Eq(m.Bounds()) { + t.Errorf("%T: sub-image want bounds %v, got %v", m, Rect(3, 2, 9, 8), m.Bounds()) continue } - if !cmp(t, b.ColorModel(), Opaque, b.At(6, 3)) { - t.Errorf("%T: sub-image at (6, 3), want a non-zero color, got %v", b, b.At(6, 3)) + if !cmp(t, m.ColorModel(), Opaque, m.At(6, 3)) { + t.Errorf("%T: sub-image at (6, 3), want a non-zero color, got %v", m, m.At(6, 3)) continue } - if !cmp(t, b.ColorModel(), Transparent, b.At(3, 3)) { - t.Errorf("%T: sub-image at (3, 3), want a zero color, got %v", b, b.At(3, 3)) + if !cmp(t, m.ColorModel(), Transparent, m.At(3, 3)) { + t.Errorf("%T: sub-image at (3, 3), want a zero color, got %v", m, m.At(3, 3)) + continue + } + m.Set(3, 3, Opaque) + if !cmp(t, m.ColorModel(), Opaque, m.At(3, 3)) { + t.Errorf("%T: sub-image at (3, 3), want a non-zero color, got %v", m, m.At(3, 3)) + continue + } + } +} + +func Test16BitsPerColorChannel(t *testing.T) { + testColorModel := []ColorModel{ + RGBA64ColorModel, + NRGBA64ColorModel, + Alpha16ColorModel, + Gray16ColorModel, + } + for _, cm := range testColorModel { + c := cm.Convert(RGBA64Color{0x1234, 0x1234, 0x1234, 0x1234}) // Premultiplied alpha. + r, _, _, _ := c.RGBA() + if r != 0x1234 { + t.Errorf("%T: want red value 0x%04x got 0x%04x", c, 0x1234, r) continue } - b.Set(3, 3, Opaque) - if !cmp(t, b.ColorModel(), Opaque, b.At(3, 3)) { - t.Errorf("%T: sub-image at (3, 3), want a non-zero color, got %v", b, b.At(3, 3)) + } + testImage := []image{ + NewRGBA64(10, 10), + NewNRGBA64(10, 10), + NewAlpha16(10, 10), + NewGray16(10, 10), + } + for _, m := range testImage { + m.Set(1, 2, NRGBA64Color{0xffff, 0xffff, 0xffff, 0x1357}) // Non-premultiplied alpha. + r, _, _, _ := m.At(1, 2).RGBA() + if r != 0x1357 { + t.Errorf("%T: want red value 0x%04x got 0x%04x", m, 0x1357, r) continue } } diff --git a/src/pkg/index/suffixarray/suffixarray.go b/src/pkg/index/suffixarray/suffixarray.go index 079b7d8ed..9d4e93217 100644 --- a/src/pkg/index/suffixarray/suffixarray.go +++ b/src/pkg/index/suffixarray/suffixarray.go @@ -115,7 +115,7 @@ func (x *Index) FindAllIndex(r *regexp.Regexp, n int) (result [][]int) { if len(indices) == 0 { return } - sort.SortInts(indices) + sort.Ints(indices) pairs := make([]int, 2*len(indices)) result = make([][]int, len(indices)) count := 0 @@ -159,7 +159,7 @@ func (x *Index) FindAllIndex(r *regexp.Regexp, n int) (result [][]int) { if len(indices) == 0 { return } - sort.SortInts(indices) + sort.Ints(indices) result = result[0:0] prev := 0 for _, i := range indices { diff --git a/src/pkg/index/suffixarray/suffixarray_test.go b/src/pkg/index/suffixarray/suffixarray_test.go index b1499027a..385ff0e56 100644 --- a/src/pkg/index/suffixarray/suffixarray_test.go +++ b/src/pkg/index/suffixarray/suffixarray_test.go @@ -141,7 +141,7 @@ func testLookup(t *testing.T, tc *testCase, x *Index, s string, n int) { // we cannot simply check that the res and exp lists are equal // check that each result is in fact a correct match and there are no duplicates - sort.SortInts(res) + sort.Ints(res) for i, r := range res { if r < 0 || len(tc.source) <= r { t.Errorf("test %q, lookup %q, result %d (n = %d): index %d out of range [0, %d[", tc.name, s, i, n, r, len(tc.source)) diff --git a/src/pkg/io/io.go b/src/pkg/io/io.go index 846dcacb5..b879fe5b7 100644 --- a/src/pkg/io/io.go +++ b/src/pkg/io/io.go @@ -12,9 +12,11 @@ import "os" // Error represents an unexpected I/O behavior. type Error struct { - os.ErrorString + ErrorString string } +func (err *Error) String() string { return err.ErrorString } + // ErrShortWrite means that a write accepted fewer bytes than requested // but failed to return an explicit error. var ErrShortWrite os.Error = &Error{"short write"} @@ -29,15 +31,24 @@ var ErrUnexpectedEOF os.Error = &Error{"unexpected EOF"} // Reader is the interface that wraps the basic Read method. // // Read reads up to len(p) bytes into p. It returns the number of bytes -// read (0 <= n <= len(p)) and any error encountered. -// Even if Read returns n < len(p), -// it may use all of p as scratch space during the call. +// read (0 <= n <= len(p)) and any error encountered. Even if Read +// returns n < len(p), it may use all of p as scratch space during the call. // If some data is available but not len(p) bytes, Read conventionally -// returns what is available rather than block waiting for more. +// returns what is available instead of waiting for more. // -// At the end of the input stream, Read returns 0, os.EOF. -// Read may return a non-zero number of bytes with a non-nil err. -// In particular, a Read that exhausts the input may return n > 0, os.EOF. +// When Read encounters an error or end-of-file condition after +// successfully reading n > 0 bytes, it returns the number of +// bytes read. It may return the (non-nil) error from the same call +// or return the error (and n == 0) from a subsequent call. +// An instance of this general case is that a Reader returning +// a non-zero number of bytes at the end of the input stream may +// return either err == os.EOF or err == nil. The next Read should +// return 0, os.EOF regardless. +// +// Callers should always process the n > 0 bytes returned before +// considering the error err. Doing so correctly handles I/O errors +// that happen after reading some bytes and also both of the +// allowed EOF behaviors. type Reader interface { Read(p []byte) (n int, err os.Error) } @@ -125,19 +136,22 @@ type WriterTo interface { // ReaderAt is the interface that wraps the basic ReadAt method. // // ReadAt reads len(p) bytes into p starting at offset off in the -// underlying data stream. It returns the number of bytes +// underlying input source. It returns the number of bytes // read (0 <= n <= len(p)) and any error encountered. // -// Even if ReadAt returns n < len(p), -// it may use all of p as scratch space during the call. -// If some data is available but not len(p) bytes, ReadAt blocks -// until either all the data is available or an error occurs. +// When ReadAt returns n < len(p), it returns a non-nil error +// explaining why more bytes were not returned. In this respect, +// ReadAt is stricter than Read. +// +// Even if ReadAt returns n < len(p), it may use all of p as scratch +// space during the call. If some data is available but not len(p) bytes, +// ReadAt blocks until either all the data is available or an error occurs. +// In this respect ReadAt is different from Read. // -// At the end of the input stream, ReadAt returns 0, os.EOF. -// ReadAt may return a non-zero number of bytes with a non-nil err. -// In particular, a ReadAt that exhausts the input may return n > 0, os.EOF. +// If the n = len(p) bytes returned by ReadAt are at the end of the +// input source, ReadAt may return either err == os.EOF or err == nil. // -// If ReadAt is reading from an data stream with a seek offset, +// If ReadAt is reading from an input source with a seek offset, // ReadAt should not affect nor be affected by the underlying // seek offset. type ReaderAt interface { @@ -195,8 +209,16 @@ type RuneScanner interface { UnreadRune() os.Error } +// stringWriter is the interface that wraps the WriteString method. +type stringWriter interface { + WriteString(s string) (n int, err os.Error) +} + // WriteString writes the contents of the string s to w, which accepts an array of bytes. func WriteString(w Writer, s string) (n int, err os.Error) { + if sw, ok := w.(stringWriter); ok { + return sw.WriteString(s) + } return w.Write([]byte(s)) } @@ -235,7 +257,10 @@ func ReadFull(r Reader, buf []byte) (n int, err os.Error) { } // Copyn copies n bytes (or until an error) from src to dst. -// It returns the number of bytes copied and the error, if any. +// It returns the number of bytes copied and the earliest +// error encountered while copying. Because Read can +// return the full amount requested as well as an error +// (including os.EOF), so can Copyn. // // If dst implements the ReaderFrom interface, // the copy is implemented by calling dst.ReadFrom(src). @@ -281,7 +306,11 @@ func Copyn(dst Writer, src Reader, n int64) (written int64, err os.Error) { // Copy copies from src to dst until either EOF is reached // on src or an error occurs. It returns the number of bytes -// copied and the error, if any. +// copied and the first error encountered while copying, if any. +// +// A successful Copy returns err == nil, not err == os.EOF. +// Because Copy is defined to read from src until EOF, it does +// not treat an EOF from Read as an error to be reported. // // If dst implements the ReaderFrom interface, // the copy is implemented by calling dst.ReadFrom(src). diff --git a/src/pkg/io/ioutil/ioutil.go b/src/pkg/io/ioutil/ioutil.go index 5f1eecaab..f79bf87f5 100644 --- a/src/pkg/io/ioutil/ioutil.go +++ b/src/pkg/io/ioutil/ioutil.go @@ -63,7 +63,7 @@ func WriteFile(filename string, data []byte, perm uint32) os.Error { return err } -// A dirList implements sort.Interface. +// A fileInfoList implements sort.Interface. type fileInfoList []*os.FileInfo func (f fileInfoList) Len() int { return len(f) } diff --git a/src/pkg/json/decode.go b/src/pkg/json/decode.go index e78b60ccb..35a06b0f9 100644 --- a/src/pkg/json/decode.go +++ b/src/pkg/json/decode.go @@ -482,7 +482,7 @@ func (d *decodeState) object(v reflect.Value) { if isValidTag(key) { for i := 0; i < sv.NumField(); i++ { f = st.Field(i) - if f.Tag == key { + if f.Tag.Get("json") == key { ok = true break } diff --git a/src/pkg/json/decode_test.go b/src/pkg/json/decode_test.go index bf8bf10bf..9b84bc76c 100644 --- a/src/pkg/json/decode_test.go +++ b/src/pkg/json/decode_test.go @@ -42,8 +42,9 @@ var ( type badTag struct { X string - Y string "y" - Z string "@#*%(#@" + Y string `json:"y"` + Z string `x:"@#*%(#@"` + W string `json:"@#$@#$"` } type unmarshalTest struct { @@ -68,7 +69,7 @@ var unmarshalTests = []unmarshalTest{ {`{"x": 1}`, new(tx), tx{}, &UnmarshalFieldError{"x", txType, txType.Field(0)}}, // skip invalid tags - {`{"X":"a", "y":"b", "Z":"c"}`, new(badTag), badTag{"a", "b", "c"}, nil}, + {`{"X":"a", "y":"b", "Z":"c", "W":"d"}`, new(badTag), badTag{"a", "b", "c", "d"}, nil}, // syntax errors {`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}}, @@ -250,7 +251,7 @@ type All struct { Float32 float32 Float64 float64 - Foo string "bar" + Foo string `json:"bar"` PBool *bool PInt *int diff --git a/src/pkg/json/encode.go b/src/pkg/json/encode.go index ec0a14a6a..adc0f0f37 100644 --- a/src/pkg/json/encode.go +++ b/src/pkg/json/encode.go @@ -36,11 +36,13 @@ import ( // Array and slice values encode as JSON arrays, except that // []byte encodes as a base64-encoded string. // -// Struct values encode as JSON objects. Each struct field becomes -// a member of the object. By default the object's key name is the -// struct field name. If the struct field has a non-empty tag consisting -// of only Unicode letters, digits, and underscores, that tag will be used -// as the name instead. Only exported fields will be encoded. +// Struct values encode as JSON objects. Each exported struct field +// becomes a member of the object. By default the object's key string +// is the struct field name. If the struct field's tag has a "json" key with a +// value that is a non-empty string consisting of only Unicode letters, +// digits, and underscores, that value will be used as the object key. +// For example, the field tag `json:"myName"` says to use "myName" +// as the object key. // // Map values encode as JSON objects. // The map's key type must be string; the object keys are used directly @@ -236,8 +238,8 @@ func (e *encodeState) reflectValue(v reflect.Value) { } else { e.WriteByte(',') } - if isValidTag(f.Tag) { - e.string(f.Tag) + if tag := f.Tag.Get("json"); tag != "" && isValidTag(tag) { + e.string(tag) } else { e.string(f.Name) } diff --git a/src/pkg/json/scanner_test.go b/src/pkg/json/scanner_test.go index 0d4de3246..023e7c81e 100644 --- a/src/pkg/json/scanner_test.go +++ b/src/pkg/json/scanner_test.go @@ -252,7 +252,10 @@ func genArray(n int) []interface{} { if f > n { f = n } - x := make([]interface{}, int(f)) + if n > 0 && f == 0 { + f = 1 + } + x := make([]interface{}, f) for i := range x { x[i] = genValue(((i+1)*n)/f - (i*n)/f) } diff --git a/src/pkg/mail/message.go b/src/pkg/mail/message.go index 754b779be..e227d17d6 100644 --- a/src/pkg/mail/message.go +++ b/src/pkg/mail/message.go @@ -18,8 +18,10 @@ package mail import ( "bufio" "bytes" + "encoding/base64" "fmt" "io" + "io/ioutil" "log" "net/textproto" "os" @@ -94,7 +96,7 @@ func parseDate(date string) (*time.Time, os.Error) { return t, nil } } - return nil, os.ErrorString("mail: header could not be parsed") + return nil, os.NewError("mail: header could not be parsed") } // A Header represents the key-value pairs in a mail message header. @@ -106,7 +108,7 @@ func (h Header) Get(key string) string { return textproto.MIMEHeader(h).Get(key) } -var ErrHeaderNotPresent = os.ErrorString("mail: header not in message") +var ErrHeaderNotPresent = os.NewError("mail: header not in message") // Date parses the Date header field. func (h Header) Date() (*time.Time, os.Error) { @@ -202,7 +204,7 @@ func (p *addrParser) parseAddressList() ([]*Address, os.Error) { break } if !p.consume(',') { - return nil, os.ErrorString("mail: expected comma") + return nil, os.NewError("mail: expected comma") } } return list, nil @@ -213,7 +215,7 @@ func (p *addrParser) parseAddress() (addr *Address, err os.Error) { debug.Printf("parseAddress: %q", *p) p.skipSpace() if p.empty() { - return nil, os.ErrorString("mail: no address") + return nil, os.NewError("mail: no address") } // address = name-addr / addr-spec @@ -244,14 +246,14 @@ func (p *addrParser) parseAddress() (addr *Address, err os.Error) { // angle-addr = "<" addr-spec ">" p.skipSpace() if !p.consume('<') { - return nil, os.ErrorString("mail: no angle-addr") + return nil, os.NewError("mail: no angle-addr") } spec, err = p.consumeAddrSpec() if err != nil { return nil, err } if !p.consume('>') { - return nil, os.ErrorString("mail: unclosed angle-addr") + return nil, os.NewError("mail: unclosed angle-addr") } debug.Printf("parseAddress: spec=%q", spec) @@ -276,7 +278,7 @@ func (p *addrParser) consumeAddrSpec() (spec string, err os.Error) { var localPart string p.skipSpace() if p.empty() { - return "", os.ErrorString("mail: no addr-spec") + return "", os.NewError("mail: no addr-spec") } if p.peek() == '"' { // quoted-string @@ -293,14 +295,14 @@ func (p *addrParser) consumeAddrSpec() (spec string, err os.Error) { } if !p.consume('@') { - return "", os.ErrorString("mail: missing @ in addr-spec") + return "", os.NewError("mail: missing @ in addr-spec") } // domain = dot-atom / domain-literal var domain string p.skipSpace() if p.empty() { - return "", os.ErrorString("mail: no domain in addr-spec") + return "", os.NewError("mail: no domain in addr-spec") } // TODO(dsymonds): Handle domain-literal domain, err = p.consumeAtom(true) @@ -321,7 +323,7 @@ func (p *addrParser) consumePhrase() (phrase string, err os.Error) { var word string p.skipSpace() if p.empty() { - return "", os.ErrorString("mail: missing phrase") + return "", os.NewError("mail: missing phrase") } if p.peek() == '"' { // quoted-string @@ -345,7 +347,7 @@ func (p *addrParser) consumePhrase() (phrase string, err os.Error) { // Ignore any error if we got at least one word. if err != nil && len(words) == 0 { debug.Printf("consumePhrase: hit err: %v", err) - return "", os.ErrorString("mail: missing word in phrase") + return "", os.NewError("mail: missing word in phrase") } phrase = strings.Join(words, " ") return phrase, nil @@ -359,14 +361,14 @@ func (p *addrParser) consumeQuotedString() (qs string, err os.Error) { Loop: for { if i >= p.len() { - return "", os.ErrorString("mail: unclosed quoted-string") + return "", os.NewError("mail: unclosed quoted-string") } switch c := (*p)[i]; { case c == '"': break Loop case c == '\\': if i+1 == p.len() { - return "", os.ErrorString("mail: unclosed quoted-string") + return "", os.NewError("mail: unclosed quoted-string") } qsb = append(qsb, (*p)[i+1]) i += 2 @@ -387,7 +389,7 @@ Loop: // If dot is true, consumeAtom parses an RFC 5322 dot-atom instead. func (p *addrParser) consumeAtom(dot bool) (atom string, err os.Error) { if !isAtext(p.peek(), false) { - return "", os.ErrorString("mail: invalid string") + return "", os.NewError("mail: invalid string") } i := 1 for ; i < p.len() && isAtext((*p)[i], dot); i++ { @@ -423,42 +425,73 @@ func (p *addrParser) len() int { } func decodeRFC2047Word(s string) (string, os.Error) { - fields := strings.Split(s, "?", -1) + fields := strings.Split(s, "?") if len(fields) != 5 || fields[0] != "=" || fields[4] != "=" { - return "", os.ErrorString("mail: address not RFC 2047 encoded") + return "", os.NewError("mail: address not RFC 2047 encoded") } charset, enc := strings.ToLower(fields[1]), strings.ToLower(fields[2]) - // TODO(dsymonds): Support "b" encoding too. - if enc != "q" { - return "", fmt.Errorf("mail: RFC 2047 encoding not supported: %q", enc) - } if charset != "iso-8859-1" && charset != "utf-8" { return "", fmt.Errorf("mail: charset not supported: %q", charset) } - in := fields[3] - b := new(bytes.Buffer) - for i := 0; i < len(in); i++ { - switch c := in[i]; { - case c == '=' && i+2 < len(in): - x, err := strconv.Btoi64(in[i+1:i+3], 16) - if err != nil { - return "", fmt.Errorf("mail: invalid RFC 2047 encoding: %q", in[i:i+3]) - } - i += 2 - switch charset { - case "iso-8859-1": - b.WriteRune(int(x)) - case "utf-8": - b.WriteByte(byte(x)) - } - case c == '_': - b.WriteByte(' ') - default: - b.WriteByte(c) + in := bytes.NewBufferString(fields[3]) + var r io.Reader + switch enc { + case "b": + r = base64.NewDecoder(base64.StdEncoding, in) + case "q": + r = qDecoder{r: in} + default: + return "", fmt.Errorf("mail: RFC 2047 encoding not supported: %q", enc) + } + + dec, err := ioutil.ReadAll(r) + if err != nil { + return "", err + } + + switch charset { + case "iso-8859-1": + b := new(bytes.Buffer) + for _, c := range dec { + b.WriteRune(int(c)) + } + return b.String(), nil + case "utf-8": + return string(dec), nil + } + panic("unreachable") +} + +type qDecoder struct { + r io.Reader + scratch [2]byte +} + +func (qd qDecoder) Read(p []byte) (n int, err os.Error) { + // This method writes at most one byte into p. + if len(p) == 0 { + return 0, nil + } + if _, err := qd.r.Read(qd.scratch[:1]); err != nil { + return 0, err + } + switch c := qd.scratch[0]; { + case c == '=': + if _, err := io.ReadFull(qd.r, qd.scratch[:2]); err != nil { + return 0, err + } + x, err := strconv.Btoi64(string(qd.scratch[:2]), 16) + if err != nil { + return 0, fmt.Errorf("mail: invalid RFC 2047 encoding: %q", qd.scratch[:2]) } + p[0] = byte(x) + case c == '_': + p[0] = ' ' + default: + p[0] = c } - return b.String(), nil + return 1, nil } var atextChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ" + diff --git a/src/pkg/mail/message_test.go b/src/pkg/mail/message_test.go index 1ff45d2c1..e1bcc89ee 100644 --- a/src/pkg/mail/message_test.go +++ b/src/pkg/mail/message_test.go @@ -217,6 +217,26 @@ func TestAddressParsing(t *testing.T) { }, }, }, + // Custom example of RFC 2047 "B"-encoded ISO-8859-1 address. + { + `=?ISO-8859-1?B?SvZyZw==?= <joerg@example.com>`, + []*Address{ + &Address{ + Name: `Jörg`, + Address: "joerg@example.com", + }, + }, + }, + // Custom example of RFC 2047 "B"-encoded UTF-8 address. + { + `=?UTF-8?B?SsO2cmc=?= <joerg@example.com>`, + []*Address{ + &Address{ + Name: `Jörg`, + Address: "joerg@example.com", + }, + }, + }, } for _, test := range tests { addrs, err := newAddrParser(test.addrsStr).parseAddressList() diff --git a/src/pkg/mime/grammar.go b/src/pkg/mime/grammar.go index e60cbb8df..6e319ff8b 100644 --- a/src/pkg/mime/grammar.go +++ b/src/pkg/mime/grammar.go @@ -9,13 +9,13 @@ import ( ) // isTSpecial returns true if rune is in 'tspecials' as defined by RFC -// 1531 and RFC 2045. +// 1521 and RFC 2045. func isTSpecial(rune int) bool { return strings.IndexRune(`()<>@,;:\"/[]?=`, rune) != -1 } // IsTokenChar returns true if rune is in 'token' as defined by RFC -// 1531 and RFC 2045. +// 1521 and RFC 2045. func IsTokenChar(rune int) bool { // token := 1*<any (US-ASCII) CHAR except SPACE, CTLs, // or tspecials> diff --git a/src/pkg/mime/mediatype.go b/src/pkg/mime/mediatype.go index f28ff3e96..40c735c5b 100644 --- a/src/pkg/mime/mediatype.go +++ b/src/pkg/mime/mediatype.go @@ -31,11 +31,13 @@ func validMediaTypeOrDisposition(s string) bool { } // ParseMediaType parses a media type value and any optional -// parameters, per RFC 1531. Media types are the values in -// Content-Type and Content-Disposition headers (RFC 2183). On -// success, ParseMediaType returns the media type converted to -// lowercase and trimmed of white space and a non-nil params. On -// error, it returns an empty string and a nil params. +// parameters, per RFC 1521. Media types are the values in +// Content-Type and Content-Disposition headers (RFC 2183). +// On success, ParseMediaType returns the media type converted +// to lowercase and trimmed of white space. The returned params +// is always a non-nil map. Params maps from the lowercase +// attribute to the attribute value with its case preserved. +// On error, it returns an empty string and a nil params. func ParseMediaType(v string) (mediatype string, params map[string]string) { i := strings.Index(v, ";") if i == -1 { @@ -132,7 +134,7 @@ func ParseMediaType(v string) (mediatype string, params map[string]string) { } func decode2231Enc(v string) string { - sv := strings.Split(v, "'", 3) + sv := strings.SplitN(v, "'", 3) if len(sv) != 3 { return "" } @@ -211,6 +213,7 @@ func consumeMediaParam(v string) (param, value, rest string) { rest = rest[1:] // consume semicolon rest = strings.TrimLeftFunc(rest, unicode.IsSpace) param, rest = consumeToken(rest) + param = strings.ToLower(param) if param == "" { return "", "", v } diff --git a/src/pkg/mime/mediatype_test.go b/src/pkg/mime/mediatype_test.go index 454ddd037..93264bd09 100644 --- a/src/pkg/mime/mediatype_test.go +++ b/src/pkg/mime/mediatype_test.go @@ -60,6 +60,7 @@ func TestConsumeMediaParam(t *testing.T) { {" ; foo=bar", "foo", "bar", ""}, {"; foo=bar", "foo", "bar", ""}, {";foo=bar", "foo", "bar", ""}, + {";FOO=bar", "foo", "bar", ""}, {`;foo="bar"`, "foo", "bar", ""}, {`;foo="bar"; `, "foo", "bar", "; "}, {`;foo="bar"; foo=baz`, "foo", "bar", "; foo=baz"}, @@ -127,7 +128,7 @@ func TestParseMediaType(t *testing.T) { `URL*1="cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar"`, "message/external-body", m("access-type", "URL", - "URL", "ftp://cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar")}, + "url", "ftp://cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar")}, {`application/x-stuff; ` + `title*0*=us-ascii'en'This%20is%20even%20more%20; ` + diff --git a/src/pkg/mime/multipart/formdata.go b/src/pkg/mime/multipart/formdata.go index 5f3286565..91404d6f4 100644 --- a/src/pkg/mime/multipart/formdata.go +++ b/src/pkg/mime/multipart/formdata.go @@ -19,7 +19,7 @@ import ( // a Content-Disposition of "form-data". // It stores up to maxMemory bytes of the file parts in memory // and the remainder on disk in temporary files. -func (r *multiReader) ReadForm(maxMemory int64) (f *Form, err os.Error) { +func (r *Reader) ReadForm(maxMemory int64) (f *Form, err os.Error) { form := &Form{make(map[string][]string), make(map[string][]*FileHeader)} defer func() { if err != nil { diff --git a/src/pkg/mime/multipart/formdata_test.go b/src/pkg/mime/multipart/formdata_test.go index 9424c3778..4bc464931 100644 --- a/src/pkg/mime/multipart/formdata_test.go +++ b/src/pkg/mime/multipart/formdata_test.go @@ -31,10 +31,12 @@ func TestReadForm(t *testing.T) { if _, ok := fd.(*os.File); ok { t.Error("file is *os.File, should not be") } + fd.Close() fd = testFile(t, f.File["fileb"][0], "fileb.txt", filebContents) if _, ok := fd.(*os.File); !ok { t.Errorf("file has unexpected underlying type %T", fd) } + fd.Close() } func testFile(t *testing.T, fh *FileHeader, efn, econtent string) File { diff --git a/src/pkg/mime/multipart/multipart.go b/src/pkg/mime/multipart/multipart.go index 9affa1126..4711fd78b 100644 --- a/src/pkg/mime/multipart/multipart.go +++ b/src/pkg/mime/multipart/multipart.go @@ -24,25 +24,15 @@ import ( "regexp" ) +// TODO(bradfitz): inline these once the compiler can inline them in +// read-only situation (such as bytes.HasSuffix) +var lf = []byte("\n") +var crlf = []byte("\r\n") + var headerRegexp *regexp.Regexp = regexp.MustCompile("^([a-zA-Z0-9\\-]+): *([^\r\n]+)") var emptyParams = make(map[string]string) -// Reader is an iterator over parts in a MIME multipart body. -// Reader's underlying parser consumes its input as needed. Seeking -// isn't supported. -type Reader interface { - // NextPart returns the next part in the multipart or an error. - // When there are no more parts, the error os.EOF is returned. - NextPart() (*Part, os.Error) - - // ReadForm parses an entire multipart message whose parts have - // a Content-Disposition of "form-data". - // It stores up to maxMemory bytes of the file parts in memory - // and the remainder on disk in temporary files. - ReadForm(maxMemory int64) (*Form, os.Error) -} - // A Part represents a single part in a multipart body. type Part struct { // The headers of the body, if any, with the keys canonicalized @@ -51,7 +41,7 @@ type Part struct { Header textproto.MIMEHeader buffer *bytes.Buffer - mr *multiReader + mr *Reader disposition string dispositionParams map[string]string @@ -91,20 +81,19 @@ func (p *Part) parseContentDisposition() { // NewReader creates a new multipart Reader reading from r using the // given MIME boundary. -func NewReader(reader io.Reader, boundary string) Reader { +func NewReader(reader io.Reader, boundary string) *Reader { b := []byte("\r\n--" + boundary + "--") - return &multiReader{ + return &Reader{ bufReader: bufio.NewReader(reader), + nl: b[:2], nlDashBoundary: b[:len(b)-2], dashBoundaryDash: b[2:], dashBoundary: b[2 : len(b)-2], } } -// Implementation .... - -func newPart(mr *multiReader) (*Part, os.Error) { +func newPart(mr *Reader) (*Part, os.Error) { bp := &Part{ Header: make(map[string][]string), mr: mr, @@ -188,16 +177,21 @@ func (bp *Part) Close() os.Error { return nil } -type multiReader struct { +// Reader is an iterator over parts in a MIME multipart body. +// Reader's underlying parser consumes its input as needed. Seeking +// isn't supported. +type Reader struct { bufReader *bufio.Reader currentPart *Part partsRead int - nlDashBoundary, dashBoundaryDash, dashBoundary []byte + nl, nlDashBoundary, dashBoundaryDash, dashBoundary []byte } -func (mr *multiReader) NextPart() (*Part, os.Error) { +// NextPart returns the next part in the multipart or an error. +// When there are no more parts, the error os.EOF is returned. +func (mr *Reader) NextPart() (*Part, os.Error) { if mr.currentPart != nil { mr.currentPart.Close() } @@ -233,11 +227,11 @@ func (mr *multiReader) NextPart() (*Part, os.Error) { continue } - if bytes.Equal(line, []byte("\r\n")) { - // Consume the "\r\n" separator between the - // body of the previous part and the boundary - // line we now expect will follow. (either a - // new part or the end boundary) + // Consume the "\n" or "\r\n" separator between the + // body of the previous part and the boundary line we + // now expect will follow. (either a new part or the + // end boundary) + if bytes.Equal(line, mr.nl) { expectNewPart = true continue } @@ -247,7 +241,7 @@ func (mr *multiReader) NextPart() (*Part, os.Error) { panic("unreachable") } -func (mr *multiReader) isBoundaryDelimiterLine(line []byte) bool { +func (mr *Reader) isBoundaryDelimiterLine(line []byte) bool { // http://tools.ietf.org/html/rfc2046#section-5.1 // The boundary delimiter line is then defined as a line // consisting entirely of two hyphen characters ("-", @@ -257,13 +251,17 @@ func (mr *multiReader) isBoundaryDelimiterLine(line []byte) bool { if !bytes.HasPrefix(line, mr.dashBoundary) { return false } - if bytes.HasSuffix(line, []byte("\r\n")) { - return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-2]) + if bytes.HasSuffix(line, mr.nl) { + return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-len(mr.nl)]) } // Violate the spec and also support newlines without the // carriage return... - if bytes.HasSuffix(line, []byte("\n")) { - return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-1]) + if mr.partsRead == 0 && bytes.HasSuffix(line, lf) { + if onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-1]) { + mr.nl = mr.nl[1:] + mr.nlDashBoundary = mr.nlDashBoundary[1:] + return true + } } return false } @@ -280,5 +278,5 @@ func onlyHorizontalWhitespace(s []byte) bool { func hasPrefixThenNewline(s, prefix []byte) bool { return bytes.HasPrefix(s, prefix) && (len(s) == len(prefix)+1 && s[len(s)-1] == '\n' || - len(s) == len(prefix)+2 && bytes.HasSuffix(s, []byte("\r\n"))) + len(s) == len(prefix)+2 && bytes.HasSuffix(s, crlf)) } diff --git a/src/pkg/mime/multipart/multipart_test.go b/src/pkg/mime/multipart/multipart_test.go index 4ec3d30bd..1357466ac 100644 --- a/src/pkg/mime/multipart/multipart_test.go +++ b/src/pkg/mime/multipart/multipart_test.go @@ -25,7 +25,7 @@ func TestHorizontalWhitespace(t *testing.T) { } func TestBoundaryLine(t *testing.T) { - mr := NewReader(strings.NewReader(""), "myBoundary").(*multiReader) + mr := NewReader(strings.NewReader(""), "myBoundary") if !mr.isBoundaryDelimiterLine([]byte("--myBoundary\r\n")) { t.Error("expected") } @@ -81,7 +81,7 @@ func TestNameAccessors(t *testing.T) { var longLine = strings.Repeat("\n\n\r\r\r\n\r\000", (1<<20)/8) -func testMultipartBody() string { +func testMultipartBody(sep string) string { testBody := ` This is a multi-part message. This line is ignored. --MyBoundary @@ -112,21 +112,26 @@ never read data useless trailer ` - testBody = strings.Replace(testBody, "\n", "\r\n", -1) + testBody = strings.Replace(testBody, "\n", sep, -1) return strings.Replace(testBody, "[longline]", longLine, 1) } func TestMultipart(t *testing.T) { - bodyReader := strings.NewReader(testMultipartBody()) - testMultipart(t, bodyReader) + bodyReader := strings.NewReader(testMultipartBody("\r\n")) + testMultipart(t, bodyReader, false) +} + +func TestMultipartOnlyNewlines(t *testing.T) { + bodyReader := strings.NewReader(testMultipartBody("\n")) + testMultipart(t, bodyReader, true) } func TestMultipartSlowInput(t *testing.T) { - bodyReader := strings.NewReader(testMultipartBody()) - testMultipart(t, &slowReader{bodyReader}) + bodyReader := strings.NewReader(testMultipartBody("\r\n")) + testMultipart(t, &slowReader{bodyReader}, false) } -func testMultipart(t *testing.T, r io.Reader) { +func testMultipart(t *testing.T, r io.Reader, onlyNewlines bool) { reader := NewReader(r, "MyBoundary") buf := new(bytes.Buffer) @@ -149,8 +154,15 @@ func testMultipart(t *testing.T, r io.Reader) { if _, err := io.Copy(buf, part); err != nil { t.Errorf("part 1 copy: %v", err) } - expectEq(t, "My value\r\nThe end.", - buf.String(), "Value of first part") + + adjustNewlines := func(s string) string { + if onlyNewlines { + return strings.Replace(s, "\r\n", "\n", -1) + } + return s + } + + expectEq(t, adjustNewlines("My value\r\nThe end."), buf.String(), "Value of first part") // Part2 part, err = reader.NextPart() @@ -187,7 +199,7 @@ func testMultipart(t *testing.T, r io.Reader) { if _, err := io.Copy(buf, part); err != nil { t.Errorf("part 3 copy: %v", err) } - expectEq(t, "Line 1\r\nLine 2\r\nLine 3 ends in a newline, but just one.\r\n", + expectEq(t, adjustNewlines("Line 1\r\nLine 2\r\nLine 3 ends in a newline, but just one.\r\n"), buf.String(), "body of part 3") // Part4 diff --git a/src/pkg/mime/multipart/writer.go b/src/pkg/mime/multipart/writer.go index b436dd012..97a8897b2 100644 --- a/src/pkg/mime/multipart/writer.go +++ b/src/pkg/mime/multipart/writer.go @@ -61,7 +61,11 @@ func (w *Writer) CreatePart(header textproto.MIMEHeader) (io.Writer, os.Error) { } } var b bytes.Buffer - fmt.Fprintf(&b, "\r\n--%s\r\n", w.boundary) + if w.lastpart != nil { + fmt.Fprintf(&b, "\r\n--%s\r\n", w.boundary) + } else { + fmt.Fprintf(&b, "--%s\r\n", w.boundary) + } // TODO(bradfitz): move this to textproto.MimeHeader.Write(w), have it sort // and clean, like http.Header.Write(w) does. for k, vv := range header { diff --git a/src/pkg/mime/multipart/writer_test.go b/src/pkg/mime/multipart/writer_test.go index e6a04c388..494e936c4 100644 --- a/src/pkg/mime/multipart/writer_test.go +++ b/src/pkg/mime/multipart/writer_test.go @@ -30,6 +30,13 @@ func TestWriter(t *testing.T) { if err != nil { t.Fatalf("Close: %v", err) } + s := b.String() + if len(s) == 0 { + t.Fatal("String: unexpected empty result") + } + if s[0] == '\r' || s[0] == '\n' { + t.Fatal("String: unexpected newline") + } } r := NewReader(&b, w.Boundary()) diff --git a/src/pkg/net/Makefile b/src/pkg/net/Makefile index 5472df392..536fe369d 100644 --- a/src/pkg/net/Makefile +++ b/src/pkg/net/Makefile @@ -7,6 +7,7 @@ include ../../Make.inc TARG=net GOFILES=\ dial.go\ + dnsclient.go\ dnsmsg.go\ fd_$(GOOS).go\ hosts.go\ @@ -14,7 +15,6 @@ GOFILES=\ ip.go\ ipsock.go\ iprawsock.go\ - lookup.go\ net.go\ parse.go\ pipe.go\ @@ -24,11 +24,12 @@ GOFILES=\ unixsock.go\ GOFILES_freebsd=\ - dnsclient.go\ + dnsclient_unix.go\ dnsconfig.go\ fd.go\ file.go\ interface_bsd.go\ + lookup_unix.go\ newpollserver.go\ port.go\ sendfile_stub.go\ @@ -39,11 +40,12 @@ CGOFILES_freebsd=\ cgo_unix.go\ GOFILES_darwin=\ - dnsclient.go\ + dnsclient_unix.go\ dnsconfig.go\ fd.go\ file.go\ interface_bsd.go\ + lookup_unix.go\ newpollserver.go\ port.go\ sendfile_stub.go\ @@ -54,11 +56,12 @@ CGOFILES_darwin=\ cgo_unix.go\ GOFILES_linux=\ - dnsclient.go\ + dnsclient_unix.go\ dnsconfig.go\ fd.go\ file.go\ interface_linux.go\ + lookup_unix.go\ newpollserver.go\ port.go\ sendfile_linux.go\ @@ -66,6 +69,7 @@ GOFILES_linux=\ GOFILES_plan9=\ interface_stub.go\ + lookup_unix.go\ sendfile_stub.go\ ifeq ($(GOARCH),arm) @@ -78,11 +82,10 @@ CGOFILES_linux=\ endif GOFILES_windows=\ - cgo_stub.go\ file_windows.go\ - interface_stub.go\ - resolv_windows.go\ - sendfile_stub.go\ + interface_windows.go\ + lookup_windows.go\ + sendfile_windows.go\ sock_windows.go\ GOFILES+=$(GOFILES_$(GOOS)) diff --git a/src/pkg/net/dial.go b/src/pkg/net/dial.go index ead775fe6..10c67dcc4 100644 --- a/src/pkg/net/dial.go +++ b/src/pkg/net/dial.go @@ -6,6 +6,28 @@ package net import "os" +func resolveNetAddr(op, net, addr string) (a Addr, err os.Error) { + if addr == "" { + return nil, &OpError{op, net, nil, errMissingAddress} + } + switch net { + case "tcp", "tcp4", "tcp6": + a, err = ResolveTCPAddr(net, addr) + case "udp", "udp4", "udp6": + a, err = ResolveUDPAddr(net, addr) + case "unix", "unixgram", "unixpacket": + a, err = ResolveUnixAddr(net, addr) + case "ip", "ip4", "ip6": + a, err = ResolveIPAddr(net, addr) + default: + err = UnknownNetworkError(net) + } + if err != nil { + return nil, &OpError{op, net + " " + addr, nil, err} + } + return +} + // Dial connects to the address addr on the network net. // // Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only), @@ -23,56 +45,26 @@ import "os" // Dial("tcp", "[de:ad:be:ef::ca:fe]:80") // func Dial(net, addr string) (c Conn, err os.Error) { - raddr := addr - if raddr == "" { - return nil, &OpError{"dial", net, nil, errMissingAddress} + addri, err := resolveNetAddr("dial", net, addr) + if err != nil { + return nil, err } - switch net { - case "tcp", "tcp4", "tcp6": - var ra *TCPAddr - if ra, err = ResolveTCPAddr(net, raddr); err != nil { - goto Error - } - c, err := DialTCP(net, nil, ra) - if err != nil { - return nil, err - } - return c, nil - case "udp", "udp4", "udp6": - var ra *UDPAddr - if ra, err = ResolveUDPAddr(net, raddr); err != nil { - goto Error - } - c, err := DialUDP(net, nil, ra) - if err != nil { - return nil, err - } - return c, nil - case "unix", "unixgram", "unixpacket": - var ra *UnixAddr - if ra, err = ResolveUnixAddr(net, raddr); err != nil { - goto Error - } + switch ra := addri.(type) { + case *TCPAddr: + c, err = DialTCP(net, nil, ra) + case *UDPAddr: + c, err = DialUDP(net, nil, ra) + case *UnixAddr: c, err = DialUnix(net, nil, ra) - if err != nil { - return nil, err - } - return c, nil - case "ip", "ip4", "ip6": - var ra *IPAddr - if ra, err = ResolveIPAddr(net, raddr); err != nil { - goto Error - } - c, err := DialIP(net, nil, ra) - if err != nil { - return nil, err - } - return c, nil - + case *IPAddr: + c, err = DialIP(net, nil, ra) + default: + err = UnknownNetworkError(net) + } + if err != nil { + return nil, &OpError{"dial", net + " " + addr, nil, err} } - err = UnknownNetworkError(net) -Error: - return nil, &OpError{"dial", net + " " + raddr, nil, err} + return } // Listen announces on the local network address laddr. diff --git a/src/pkg/net/dnsclient.go b/src/pkg/net/dnsclient.go index ae9ca8430..280b19453 100644 --- a/src/pkg/net/dnsclient.go +++ b/src/pkg/net/dnsclient.go @@ -2,16 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// DNS client: see RFC 1035. -// Has to be linked into package net for Dial. - -// TODO(rsc): -// Check periodically whether /etc/resolv.conf has changed. -// Could potentially handle many outstanding lookups faster. -// Could have a small cache. -// Random UDP source port (net.Dial should do that for us). -// Random request IDs. - package net import ( @@ -19,9 +9,6 @@ import ( "fmt" "os" "rand" - "sync" - "time" - "sort" ) // DNSError represents a DNS lookup error. @@ -49,54 +36,31 @@ func (e *DNSError) Temporary() bool { return e.IsTimeout } const noSuchHost = "no such host" -// Send a request on the connection and hope for a reply. -// Up to cfg.attempts attempts. -func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, os.Error) { - if len(name) >= 256 { - return nil, &DNSError{Error: "name too long", Name: name} - } - out := new(dnsMsg) - out.id = uint16(rand.Int()) ^ uint16(time.Nanoseconds()) - out.question = []dnsQuestion{ - {name, qtype, dnsClassINET}, - } - out.recursion_desired = true - msg, ok := out.Pack() - if !ok { - return nil, &DNSError{Error: "internal error - cannot pack message", Name: name} +// reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP +// address addr suitable for rDNS (PTR) record lookup or an error if it fails +// to parse the IP address. +func reverseaddr(addr string) (arpa string, err os.Error) { + ip := ParseIP(addr) + if ip == nil { + return "", &DNSError{Error: "unrecognized address", Name: addr} } - - for attempt := 0; attempt < cfg.attempts; attempt++ { - n, err := c.Write(msg) - if err != nil { - return nil, err - } - - c.SetReadTimeout(int64(cfg.timeout) * 1e9) // nanoseconds - - buf := make([]byte, 2000) // More than enough. - n, err = c.Read(buf) - if err != nil { - if e, ok := err.(Error); ok && e.Timeout() { - continue - } - return nil, err - } - buf = buf[0:n] - in := new(dnsMsg) - if !in.Unpack(buf) || in.id != out.id { - continue - } - return in, nil + if ip.To4() != nil { + return fmt.Sprintf("%d.%d.%d.%d.in-addr.arpa.", ip[15], ip[14], ip[13], ip[12]), nil } - var server string - if a := c.RemoteAddr(); a != nil { - server = a.String() + // Must be IPv6 + var buf bytes.Buffer + // Add it, in reverse, to the buffer + for i := len(ip) - 1; i >= 0; i-- { + s := fmt.Sprintf("%02x", ip[i]) + buf.WriteByte(s[1]) + buf.WriteByte('.') + buf.WriteByte(s[0]) + buf.WriteByte('.') } - return nil, &DNSError{Error: "no answer from server", Name: name, Server: server, IsTimeout: true} + // Append "ip6.arpa." and return (buf already has the final .) + return buf.String() + "ip6.arpa.", nil } - // Find answer for name in dns message. // On return, if err == nil, addrs != nil. func answer(name, server string, dns *dnsMsg, qtype uint16) (cname string, addrs []dnsRR, err os.Error) { @@ -150,63 +114,6 @@ Cname: return "", nil, &DNSError{Error: "too many redirects", Name: name, Server: server} } -// Do a lookup for a single name, which must be rooted -// (otherwise answer will not find the answers). -func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs []dnsRR, err os.Error) { - if len(cfg.servers) == 0 { - return "", nil, &DNSError{Error: "no DNS servers", Name: name} - } - for i := 0; i < len(cfg.servers); i++ { - // Calling Dial here is scary -- we have to be sure - // not to dial a name that will require a DNS lookup, - // or Dial will call back here to translate it. - // The DNS config parser has already checked that - // all the cfg.servers[i] are IP addresses, which - // Dial will use without a DNS lookup. - server := cfg.servers[i] + ":53" - c, cerr := Dial("udp", server) - if cerr != nil { - err = cerr - continue - } - msg, merr := exchange(cfg, c, name, qtype) - c.Close() - if merr != nil { - err = merr - continue - } - cname, addrs, err = answer(name, server, msg, qtype) - if err == nil || err.(*DNSError).Error == noSuchHost { - break - } - } - return -} - -func convertRR_A(records []dnsRR) []IP { - addrs := make([]IP, len(records)) - for i, rr := range records { - a := rr.(*dnsRR_A).A - addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a)) - } - return addrs -} - -func convertRR_AAAA(records []dnsRR) []IP { - addrs := make([]IP, len(records)) - for i, rr := range records { - a := make(IP, 16) - copy(a, rr.(*dnsRR_AAAA).AAAA[:]) - addrs[i] = a - } - return addrs -} - -var cfg *dnsConfig -var dnserr os.Error - -func loadConfig() { cfg, dnserr = dnsReadConfig() } - func isDomainName(s string) bool { // See RFC 1035, RFC 3696. if len(s) == 0 { @@ -255,141 +162,6 @@ func isDomainName(s string) bool { return ok } -var onceLoadConfig sync.Once - -func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Error) { - if !isDomainName(name) { - return name, nil, &DNSError{Error: "invalid domain name", Name: name} - } - onceLoadConfig.Do(loadConfig) - if dnserr != nil || cfg == nil { - err = dnserr - return - } - // If name is rooted (trailing dot) or has enough dots, - // try it by itself first. - rooted := len(name) > 0 && name[len(name)-1] == '.' - if rooted || count(name, '.') >= cfg.ndots { - rname := name - if !rooted { - rname += "." - } - // Can try as ordinary name. - cname, addrs, err = tryOneName(cfg, rname, qtype) - if err == nil { - return - } - } - if rooted { - return - } - - // Otherwise, try suffixes. - for i := 0; i < len(cfg.search); i++ { - rname := name + "." + cfg.search[i] - if rname[len(rname)-1] != '.' { - rname += "." - } - cname, addrs, err = tryOneName(cfg, rname, qtype) - if err == nil { - return - } - } - - // Last ditch effort: try unsuffixed. - rname := name - if !rooted { - rname += "." - } - cname, addrs, err = tryOneName(cfg, rname, qtype) - if err == nil { - return - } - return -} - -// goLookupHost is the native Go implementation of LookupHost. -// Used only if cgoLookupHost refuses to handle the request -// (that is, only if cgoLookupHost is the stub in cgo_stub.go). -// Normally we let cgo use the C library resolver instead of -// depending on our lookup code, so that Go and C get the same -// answers. -func goLookupHost(name string) (addrs []string, err os.Error) { - // Use entries from /etc/hosts if they match. - addrs = lookupStaticHost(name) - if len(addrs) > 0 { - return - } - onceLoadConfig.Do(loadConfig) - if dnserr != nil || cfg == nil { - err = dnserr - return - } - ips, err := goLookupIP(name) - if err != nil { - return - } - addrs = make([]string, 0, len(ips)) - for _, ip := range ips { - addrs = append(addrs, ip.String()) - } - return -} - -// goLookupIP is the native Go implementation of LookupIP. -// Used only if cgoLookupIP refuses to handle the request -// (that is, only if cgoLookupIP is the stub in cgo_stub.go). -// Normally we let cgo use the C library resolver instead of -// depending on our lookup code, so that Go and C get the same -// answers. -func goLookupIP(name string) (addrs []IP, err os.Error) { - onceLoadConfig.Do(loadConfig) - if dnserr != nil || cfg == nil { - err = dnserr - return - } - var records []dnsRR - var cname string - cname, records, err = lookup(name, dnsTypeA) - if err != nil { - return - } - addrs = convertRR_A(records) - if cname != "" { - name = cname - } - _, records, err = lookup(name, dnsTypeAAAA) - if err != nil && len(addrs) > 0 { - // Ignore error because A lookup succeeded. - err = nil - } - if err != nil { - return - } - addrs = append(addrs, convertRR_AAAA(records)...) - return -} - -// goLookupCNAME is the native Go implementation of LookupCNAME. -// Used only if cgoLookupCNAME refuses to handle the request -// (that is, only if cgoLookupCNAME is the stub in cgo_stub.go). -// Normally we let cgo use the C library resolver instead of -// depending on our lookup code, so that Go and C get the same -// answers. -func goLookupCNAME(name string) (cname string, err os.Error) { - onceLoadConfig.Do(loadConfig) - if dnserr != nil || cfg == nil { - err = dnserr - return - } - _, rr, err := lookup(name, dnsTypeCNAME) - if err != nil { - return - } - cname = rr[0].(*dnsRR_CNAME).Cname - return -} - // An SRV represents a single DNS SRV record. type SRV struct { Target string @@ -436,35 +208,6 @@ func shuffleSRVByWeight(addrs []*SRV) { } } -// LookupSRV tries to resolve an SRV query of the given service, -// protocol, and domain name, as specified in RFC 2782. In most cases -// the proto argument can be the same as the corresponding -// Addr.Network(). The returned records are sorted by priority -// and randomized by weight within a priority. -func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.Error) { - target := "_" + service + "._" + proto + "." + name - var records []dnsRR - cname, records, err = lookup(target, dnsTypeSRV) - if err != nil { - return - } - addrs = make([]*SRV, len(records)) - for i, rr := range records { - r := rr.(*dnsRR_SRV) - addrs[i] = &SRV{r.Target, r.Port, r.Priority, r.Weight} - } - sort.Sort(byPriorityWeight(addrs)) - i := 0 - for j := 1; j < len(addrs); j++ { - if addrs[i].Priority != addrs[j].Priority { - shuffleSRVByWeight(addrs[i:j]) - i = j - } - } - shuffleSRVByWeight(addrs[i:len(addrs)]) - return -} - // An MX represents a single DNS MX record. type MX struct { Host string @@ -479,73 +222,3 @@ func (s byPref) Len() int { return len(s) } func (s byPref) Less(i, j int) bool { return s[i].Pref < s[j].Pref } func (s byPref) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - -// LookupMX returns the DNS MX records for the given domain name sorted by preference. -func LookupMX(name string) (mx []*MX, err os.Error) { - _, rr, err := lookup(name, dnsTypeMX) - if err != nil { - return - } - mx = make([]*MX, len(rr)) - for i := range rr { - r := rr[i].(*dnsRR_MX) - mx[i] = &MX{r.Mx, r.Pref} - } - // Shuffle the records to match RFC 5321 when sorted - for i := range mx { - j := rand.Intn(i + 1) - mx[i], mx[j] = mx[j], mx[i] - } - sort.Sort(byPref(mx)) - return -} - -// reverseaddr returns the in-addr.arpa. or ip6.arpa. hostname of the IP -// address addr suitable for rDNS (PTR) record lookup or an error if it fails -// to parse the IP address. -func reverseaddr(addr string) (arpa string, err os.Error) { - ip := ParseIP(addr) - if ip == nil { - return "", &DNSError{Error: "unrecognized address", Name: addr} - } - if ip.To4() != nil { - return fmt.Sprintf("%d.%d.%d.%d.in-addr.arpa.", ip[15], ip[14], ip[13], ip[12]), nil - } - // Must be IPv6 - var buf bytes.Buffer - // Add it, in reverse, to the buffer - for i := len(ip) - 1; i >= 0; i-- { - s := fmt.Sprintf("%02x", ip[i]) - buf.WriteByte(s[1]) - buf.WriteByte('.') - buf.WriteByte(s[0]) - buf.WriteByte('.') - } - // Append "ip6.arpa." and return (buf already has the final .) - return buf.String() + "ip6.arpa.", nil -} - -// LookupAddr performs a reverse lookup for the given address, returning a list -// of names mapping to that address. -func LookupAddr(addr string) (name []string, err os.Error) { - name = lookupStaticAddr(addr) - if len(name) > 0 { - return - } - var arpa string - arpa, err = reverseaddr(addr) - if err != nil { - return - } - var records []dnsRR - _, records, err = lookup(arpa, dnsTypePTR) - if err != nil { - return - } - name = make([]string, len(records)) - for i := range records { - r := records[i].(*dnsRR_PTR) - name[i] = r.Ptr - } - return -} diff --git a/src/pkg/net/dnsclient_unix.go b/src/pkg/net/dnsclient_unix.go new file mode 100644 index 000000000..7f3ef2878 --- /dev/null +++ b/src/pkg/net/dnsclient_unix.go @@ -0,0 +1,262 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// DNS client: see RFC 1035. +// Has to be linked into package net for Dial. + +// TODO(rsc): +// Check periodically whether /etc/resolv.conf has changed. +// Could potentially handle many outstanding lookups faster. +// Could have a small cache. +// Random UDP source port (net.Dial should do that for us). +// Random request IDs. + +package net + +import ( + "os" + "rand" + "sync" + "time" +) + +// Send a request on the connection and hope for a reply. +// Up to cfg.attempts attempts. +func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, os.Error) { + if len(name) >= 256 { + return nil, &DNSError{Error: "name too long", Name: name} + } + out := new(dnsMsg) + out.id = uint16(rand.Int()) ^ uint16(time.Nanoseconds()) + out.question = []dnsQuestion{ + {name, qtype, dnsClassINET}, + } + out.recursion_desired = true + msg, ok := out.Pack() + if !ok { + return nil, &DNSError{Error: "internal error - cannot pack message", Name: name} + } + + for attempt := 0; attempt < cfg.attempts; attempt++ { + n, err := c.Write(msg) + if err != nil { + return nil, err + } + + c.SetReadTimeout(int64(cfg.timeout) * 1e9) // nanoseconds + + buf := make([]byte, 2000) // More than enough. + n, err = c.Read(buf) + if err != nil { + if e, ok := err.(Error); ok && e.Timeout() { + continue + } + return nil, err + } + buf = buf[0:n] + in := new(dnsMsg) + if !in.Unpack(buf) || in.id != out.id { + continue + } + return in, nil + } + var server string + if a := c.RemoteAddr(); a != nil { + server = a.String() + } + return nil, &DNSError{Error: "no answer from server", Name: name, Server: server, IsTimeout: true} +} + + +// Do a lookup for a single name, which must be rooted +// (otherwise answer will not find the answers). +func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs []dnsRR, err os.Error) { + if len(cfg.servers) == 0 { + return "", nil, &DNSError{Error: "no DNS servers", Name: name} + } + for i := 0; i < len(cfg.servers); i++ { + // Calling Dial here is scary -- we have to be sure + // not to dial a name that will require a DNS lookup, + // or Dial will call back here to translate it. + // The DNS config parser has already checked that + // all the cfg.servers[i] are IP addresses, which + // Dial will use without a DNS lookup. + server := cfg.servers[i] + ":53" + c, cerr := Dial("udp", server) + if cerr != nil { + err = cerr + continue + } + msg, merr := exchange(cfg, c, name, qtype) + c.Close() + if merr != nil { + err = merr + continue + } + cname, addrs, err = answer(name, server, msg, qtype) + if err == nil || err.(*DNSError).Error == noSuchHost { + break + } + } + return +} + +func convertRR_A(records []dnsRR) []IP { + addrs := make([]IP, len(records)) + for i, rr := range records { + a := rr.(*dnsRR_A).A + addrs[i] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a)) + } + return addrs +} + +func convertRR_AAAA(records []dnsRR) []IP { + addrs := make([]IP, len(records)) + for i, rr := range records { + a := make(IP, 16) + copy(a, rr.(*dnsRR_AAAA).AAAA[:]) + addrs[i] = a + } + return addrs +} + +var cfg *dnsConfig +var dnserr os.Error + +func loadConfig() { cfg, dnserr = dnsReadConfig() } + +var onceLoadConfig sync.Once + +func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err os.Error) { + if !isDomainName(name) { + return name, nil, &DNSError{Error: "invalid domain name", Name: name} + } + onceLoadConfig.Do(loadConfig) + if dnserr != nil || cfg == nil { + err = dnserr + return + } + // If name is rooted (trailing dot) or has enough dots, + // try it by itself first. + rooted := len(name) > 0 && name[len(name)-1] == '.' + if rooted || count(name, '.') >= cfg.ndots { + rname := name + if !rooted { + rname += "." + } + // Can try as ordinary name. + cname, addrs, err = tryOneName(cfg, rname, qtype) + if err == nil { + return + } + } + if rooted { + return + } + + // Otherwise, try suffixes. + for i := 0; i < len(cfg.search); i++ { + rname := name + "." + cfg.search[i] + if rname[len(rname)-1] != '.' { + rname += "." + } + cname, addrs, err = tryOneName(cfg, rname, qtype) + if err == nil { + return + } + } + + // Last ditch effort: try unsuffixed. + rname := name + if !rooted { + rname += "." + } + cname, addrs, err = tryOneName(cfg, rname, qtype) + if err == nil { + return + } + return +} + +// goLookupHost is the native Go implementation of LookupHost. +// Used only if cgoLookupHost refuses to handle the request +// (that is, only if cgoLookupHost is the stub in cgo_stub.go). +// Normally we let cgo use the C library resolver instead of +// depending on our lookup code, so that Go and C get the same +// answers. +func goLookupHost(name string) (addrs []string, err os.Error) { + // Use entries from /etc/hosts if they match. + addrs = lookupStaticHost(name) + if len(addrs) > 0 { + return + } + onceLoadConfig.Do(loadConfig) + if dnserr != nil || cfg == nil { + err = dnserr + return + } + ips, err := goLookupIP(name) + if err != nil { + return + } + addrs = make([]string, 0, len(ips)) + for _, ip := range ips { + addrs = append(addrs, ip.String()) + } + return +} + +// goLookupIP is the native Go implementation of LookupIP. +// Used only if cgoLookupIP refuses to handle the request +// (that is, only if cgoLookupIP is the stub in cgo_stub.go). +// Normally we let cgo use the C library resolver instead of +// depending on our lookup code, so that Go and C get the same +// answers. +func goLookupIP(name string) (addrs []IP, err os.Error) { + onceLoadConfig.Do(loadConfig) + if dnserr != nil || cfg == nil { + err = dnserr + return + } + var records []dnsRR + var cname string + cname, records, err = lookup(name, dnsTypeA) + if err != nil { + return + } + addrs = convertRR_A(records) + if cname != "" { + name = cname + } + _, records, err = lookup(name, dnsTypeAAAA) + if err != nil && len(addrs) > 0 { + // Ignore error because A lookup succeeded. + err = nil + } + if err != nil { + return + } + addrs = append(addrs, convertRR_AAAA(records)...) + return +} + +// goLookupCNAME is the native Go implementation of LookupCNAME. +// Used only if cgoLookupCNAME refuses to handle the request +// (that is, only if cgoLookupCNAME is the stub in cgo_stub.go). +// Normally we let cgo use the C library resolver instead of +// depending on our lookup code, so that Go and C get the same +// answers. +func goLookupCNAME(name string) (cname string, err os.Error) { + onceLoadConfig.Do(loadConfig) + if dnserr != nil || cfg == nil { + err = dnserr + return + } + _, rr, err := lookup(name, dnsTypeCNAME) + if err != nil { + return + } + cname = rr[0].(*dnsRR_CNAME).Cname + return +} diff --git a/src/pkg/net/dnsmsg.go b/src/pkg/net/dnsmsg.go index 0ba69a0ce..640973b13 100644 --- a/src/pkg/net/dnsmsg.go +++ b/src/pkg/net/dnsmsg.go @@ -93,7 +93,7 @@ const ( // DNS queries. type dnsQuestion struct { - Name string "domain-name" // "domain-name" specifies encoding; see packers below + Name string `net:"domain-name"` // `net:"domain-name"` specifies encoding; see packers below Qtype uint16 Qclass uint16 } @@ -102,7 +102,7 @@ type dnsQuestion struct { // There are many types of messages, // but they all share the same header. type dnsRR_Header struct { - Name string "domain-name" + Name string `net:"domain-name"` Rrtype uint16 Class uint16 Ttl uint32 @@ -121,7 +121,7 @@ type dnsRR interface { type dnsRR_CNAME struct { Hdr dnsRR_Header - Cname string "domain-name" + Cname string `net:"domain-name"` } func (rr *dnsRR_CNAME) Header() *dnsRR_Header { @@ -140,7 +140,7 @@ func (rr *dnsRR_HINFO) Header() *dnsRR_Header { type dnsRR_MB struct { Hdr dnsRR_Header - Mb string "domain-name" + Mb string `net:"domain-name"` } func (rr *dnsRR_MB) Header() *dnsRR_Header { @@ -149,7 +149,7 @@ func (rr *dnsRR_MB) Header() *dnsRR_Header { type dnsRR_MG struct { Hdr dnsRR_Header - Mg string "domain-name" + Mg string `net:"domain-name"` } func (rr *dnsRR_MG) Header() *dnsRR_Header { @@ -158,8 +158,8 @@ func (rr *dnsRR_MG) Header() *dnsRR_Header { type dnsRR_MINFO struct { Hdr dnsRR_Header - Rmail string "domain-name" - Email string "domain-name" + Rmail string `net:"domain-name"` + Email string `net:"domain-name"` } func (rr *dnsRR_MINFO) Header() *dnsRR_Header { @@ -168,7 +168,7 @@ func (rr *dnsRR_MINFO) Header() *dnsRR_Header { type dnsRR_MR struct { Hdr dnsRR_Header - Mr string "domain-name" + Mr string `net:"domain-name"` } func (rr *dnsRR_MR) Header() *dnsRR_Header { @@ -178,7 +178,7 @@ func (rr *dnsRR_MR) Header() *dnsRR_Header { type dnsRR_MX struct { Hdr dnsRR_Header Pref uint16 - Mx string "domain-name" + Mx string `net:"domain-name"` } func (rr *dnsRR_MX) Header() *dnsRR_Header { @@ -187,7 +187,7 @@ func (rr *dnsRR_MX) Header() *dnsRR_Header { type dnsRR_NS struct { Hdr dnsRR_Header - Ns string "domain-name" + Ns string `net:"domain-name"` } func (rr *dnsRR_NS) Header() *dnsRR_Header { @@ -196,7 +196,7 @@ func (rr *dnsRR_NS) Header() *dnsRR_Header { type dnsRR_PTR struct { Hdr dnsRR_Header - Ptr string "domain-name" + Ptr string `net:"domain-name"` } func (rr *dnsRR_PTR) Header() *dnsRR_Header { @@ -205,8 +205,8 @@ func (rr *dnsRR_PTR) Header() *dnsRR_Header { type dnsRR_SOA struct { Hdr dnsRR_Header - Ns string "domain-name" - Mbox string "domain-name" + Ns string `net:"domain-name"` + Mbox string `net:"domain-name"` Serial uint32 Refresh uint32 Retry uint32 @@ -232,7 +232,7 @@ type dnsRR_SRV struct { Priority uint16 Weight uint16 Port uint16 - Target string "domain-name" + Target string `net:"domain-name"` } func (rr *dnsRR_SRV) Header() *dnsRR_Header { @@ -241,7 +241,7 @@ func (rr *dnsRR_SRV) Header() *dnsRR_Header { type dnsRR_A struct { Hdr dnsRR_Header - A uint32 "ipv4" + A uint32 `net:"ipv4"` } func (rr *dnsRR_A) Header() *dnsRR_Header { @@ -250,7 +250,7 @@ func (rr *dnsRR_A) Header() *dnsRR_Header { type dnsRR_AAAA struct { Hdr dnsRR_Header - AAAA [16]byte "ipv6" + AAAA [16]byte `net:"ipv6"` } func (rr *dnsRR_AAAA) Header() *dnsRR_Header { @@ -394,7 +394,6 @@ func packStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool) f := val.Type().Field(i) switch fv := val.Field(i); fv.Kind() { default: - BadType: fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) return len(msg), false case reflect.Struct: @@ -419,7 +418,8 @@ func packStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool) off += 4 case reflect.Array: if fv.Type().Elem().Kind() != reflect.Uint8 { - goto BadType + fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) + return len(msg), false } n := fv.Len() if off+n > len(msg) { @@ -435,7 +435,7 @@ func packStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok bool) default: fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) return len(msg), false - case "domain-name": + case `net:"domain-name"`: off, ok = packDomainName(s, msg, off) if !ok { return len(msg), false @@ -471,7 +471,6 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok boo f := val.Type().Field(i) switch fv := val.Field(i); fv.Kind() { default: - BadType: fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) return len(msg), false case reflect.Struct: @@ -492,7 +491,8 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok boo off += 4 case reflect.Array: if fv.Type().Elem().Kind() != reflect.Uint8 { - goto BadType + fmt.Fprintf(os.Stderr, "net: dns: unknown packing type %v", f.Type) + return len(msg), false } n := fv.Len() if off+n > len(msg) { @@ -506,7 +506,7 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, ok boo default: fmt.Fprintf(os.Stderr, "net: dns: unknown string tag %v", f.Tag) return len(msg), false - case "domain-name": + case `net:"domain-name"`: s, off, ok = unpackDomainName(msg, off) if !ok { return len(msg), false @@ -536,9 +536,9 @@ func unpackStruct(any interface{}, msg []byte, off int) (off1 int, ok bool) { } // Generic struct printer. -// Doesn't care about the string tag "domain-name", -// but does look for an "ipv4" tag on uint32 variables -// and the "ipv6" tag on array variables, +// Doesn't care about the string tag `net:"domain-name"`, +// but does look for an `net:"ipv4"` tag on uint32 variables +// and the `net:"ipv6"` tag on array variables, // printing them as IP addresses. func printStructValue(val reflect.Value) string { s := "{" @@ -553,10 +553,10 @@ func printStructValue(val reflect.Value) string { fval := val.Field(i) if fv := fval; fv.Kind() == reflect.Struct { s += printStructValue(fv) - } else if fv := fval; (fv.Kind() == reflect.Uint || fv.Kind() == reflect.Uint8 || fv.Kind() == reflect.Uint16 || fv.Kind() == reflect.Uint32 || fv.Kind() == reflect.Uint64 || fv.Kind() == reflect.Uintptr) && f.Tag == "ipv4" { + } else if fv := fval; (fv.Kind() == reflect.Uint || fv.Kind() == reflect.Uint8 || fv.Kind() == reflect.Uint16 || fv.Kind() == reflect.Uint32 || fv.Kind() == reflect.Uint64 || fv.Kind() == reflect.Uintptr) && f.Tag == `net:"ipv4"` { i := fv.Uint() s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String() - } else if fv := fval; fv.Kind() == reflect.Array && f.Tag == "ipv6" { + } else if fv := fval; fv.Kind() == reflect.Array && f.Tag == `net:"ipv6"` { i := fv.Interface().([]byte) s += IP(i).String() } else { diff --git a/src/pkg/net/dnsmsg_test.go b/src/pkg/net/dnsmsg_test.go index 20c9f02b0..06152a01a 100644 --- a/src/pkg/net/dnsmsg_test.go +++ b/src/pkg/net/dnsmsg_test.go @@ -6,14 +6,10 @@ package net import ( "encoding/hex" - "runtime" "testing" ) func TestDNSParseSRVReply(t *testing.T) { - if runtime.GOOS == "windows" { - return - } data, err := hex.DecodeString(dnsSRVReply) if err != nil { t.Fatal(err) @@ -45,9 +41,6 @@ func TestDNSParseSRVReply(t *testing.T) { } func TestDNSParseCorruptSRVReply(t *testing.T) { - if runtime.GOOS == "windows" { - return - } data, err := hex.DecodeString(dnsSRVCorruptReply) if err != nil { t.Fatal(err) diff --git a/src/pkg/net/dnsname_test.go b/src/pkg/net/dnsname_test.go index 0c1a62518..70df693f7 100644 --- a/src/pkg/net/dnsname_test.go +++ b/src/pkg/net/dnsname_test.go @@ -6,7 +6,6 @@ package net import ( "testing" - "runtime" ) type testCase struct { @@ -55,9 +54,6 @@ func getTestCases(ch chan<- testCase) { } func TestDNSNames(t *testing.T) { - if runtime.GOOS == "windows" { - return - } ch := make(chan testCase) go getTestCases(ch) for tc := range ch { diff --git a/src/pkg/net/fd_darwin.go b/src/pkg/net/fd_darwin.go index 00a049bfd..7e3d549eb 100644 --- a/src/pkg/net/fd_darwin.go +++ b/src/pkg/net/fd_darwin.go @@ -56,7 +56,7 @@ func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, os.Error) { return false, os.NewSyscallError("kevent", e) } if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode { - return false, os.ErrorString("kqueue phase error") + return false, os.NewError("kqueue phase error") } if ev.Data != 0 { return false, os.Errno(int(ev.Data)) diff --git a/src/pkg/net/fd_windows.go b/src/pkg/net/fd_windows.go index 9ed7801d2..41d06120a 100644 --- a/src/pkg/net/fd_windows.go +++ b/src/pkg/net/fd_windows.go @@ -29,8 +29,8 @@ func init() { } } -func closesocket(s int) (errno int) { - return syscall.Closesocket(int32(s)) +func closesocket(s syscall.Handle) (errno int) { + return syscall.Closesocket(s) } // Interface for all io operations. @@ -88,7 +88,7 @@ func (o *bufOp) Init(fd *netFD, buf []byte) { // iocp and send them to the correspondent waiting client // goroutine via channel supplied in the request. type resultSrv struct { - iocp int32 + iocp syscall.Handle } func (s *resultSrv) Run() { @@ -132,7 +132,7 @@ func (s *ioSrv) ProcessRemoteIO() { case o := <-s.submchan: o.Op().errnoc <- o.Submit() case o := <-s.canchan: - o.Op().errnoc <- syscall.CancelIo(uint32(o.Op().fd.sysfd)) + o.Op().errnoc <- syscall.CancelIo(syscall.Handle(o.Op().fd.sysfd)) } } } @@ -189,7 +189,7 @@ var onceStartServer sync.Once func startServer() { resultsrv = new(resultSrv) var errno int - resultsrv.iocp, errno = syscall.CreateIoCompletionPort(-1, 0, 0, 1) + resultsrv.iocp, errno = syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 1) if errno != 0 { panic("CreateIoCompletionPort failed " + syscall.Errstr(errno)) } @@ -209,7 +209,7 @@ type netFD struct { closing bool // immutable until Close - sysfd int + sysfd syscall.Handle family int proto int net string @@ -225,7 +225,7 @@ type netFD struct { wio sync.Mutex } -func allocFD(fd, family, proto int, net string) (f *netFD) { +func allocFD(fd syscall.Handle, family, proto int, net string) (f *netFD) { f = &netFD{ sysfd: fd, family: family, @@ -236,13 +236,13 @@ func allocFD(fd, family, proto int, net string) (f *netFD) { return f } -func newFD(fd, family, proto int, net string) (f *netFD, err os.Error) { +func newFD(fd syscall.Handle, family, proto int, net string) (f *netFD, err os.Error) { if initErr != nil { return nil, initErr } onceStartServer.Do(startServer) // Associate our socket with resultsrv.iocp. - if _, e := syscall.CreateIoCompletionPort(int32(fd), resultsrv.iocp, 0, 0); e != 0 { + if _, e := syscall.CreateIoCompletionPort(syscall.Handle(fd), resultsrv.iocp, 0, 0); e != 0 { return nil, os.Errno(e) } return allocFD(fd, family, proto, net), nil @@ -280,7 +280,7 @@ func (fd *netFD) decref() { // use the resultsrv for Close too. Sigh. syscall.SetNonblock(fd.sysfd, false) closesocket(fd.sysfd) - fd.sysfd = -1 + fd.sysfd = syscall.InvalidHandle // no need for a finalizer anymore runtime.SetFinalizer(fd, nil) } @@ -288,7 +288,7 @@ func (fd *netFD) decref() { } func (fd *netFD) Close() os.Error { - if fd == nil || fd.sysfd == -1 { + if fd == nil || fd.sysfd == syscall.InvalidHandle { return os.EINVAL } @@ -307,7 +307,7 @@ type readOp struct { func (o *readOp) Submit() (errno int) { var d, f uint32 - return syscall.WSARecv(uint32(o.fd.sysfd), &o.buf, 1, &d, &f, &o.o, nil) + return syscall.WSARecv(syscall.Handle(o.fd.sysfd), &o.buf, 1, &d, &f, &o.o, nil) } func (o *readOp) Name() string { @@ -322,7 +322,7 @@ func (fd *netFD) Read(buf []byte) (n int, err os.Error) { defer fd.rio.Unlock() fd.incref() defer fd.decref() - if fd.sysfd == -1 { + if fd.sysfd == syscall.InvalidHandle { return 0, os.EINVAL } var o readOp @@ -344,7 +344,7 @@ type readFromOp struct { func (o *readFromOp) Submit() (errno int) { var d, f uint32 l := int32(unsafe.Sizeof(o.rsa)) - return syscall.WSARecvFrom(uint32(o.fd.sysfd), &o.buf, 1, &d, &f, &o.rsa, &l, &o.o, nil) + return syscall.WSARecvFrom(o.fd.sysfd, &o.buf, 1, &d, &f, &o.rsa, &l, &o.o, nil) } func (o *readFromOp) Name() string { @@ -362,7 +362,7 @@ func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err os.Error) defer fd.rio.Unlock() fd.incref() defer fd.decref() - if fd.sysfd == -1 { + if fd.sysfd == syscall.InvalidHandle { return 0, nil, os.EINVAL } var o readFromOp @@ -380,7 +380,7 @@ type writeOp struct { func (o *writeOp) Submit() (errno int) { var d uint32 - return syscall.WSASend(uint32(o.fd.sysfd), &o.buf, 1, &d, 0, &o.o, nil) + return syscall.WSASend(o.fd.sysfd, &o.buf, 1, &d, 0, &o.o, nil) } func (o *writeOp) Name() string { @@ -395,7 +395,7 @@ func (fd *netFD) Write(buf []byte) (n int, err os.Error) { defer fd.wio.Unlock() fd.incref() defer fd.decref() - if fd.sysfd == -1 { + if fd.sysfd == syscall.InvalidHandle { return 0, os.EINVAL } var o writeOp @@ -412,7 +412,7 @@ type writeToOp struct { func (o *writeToOp) Submit() (errno int) { var d uint32 - return syscall.WSASendto(uint32(o.fd.sysfd), &o.buf, 1, &d, 0, o.sa, &o.o, nil) + return syscall.WSASendto(o.fd.sysfd, &o.buf, 1, &d, 0, o.sa, &o.o, nil) } func (o *writeToOp) Name() string { @@ -430,7 +430,7 @@ func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (n int, err os.Error) defer fd.wio.Unlock() fd.incref() defer fd.decref() - if fd.sysfd == -1 { + if fd.sysfd == syscall.InvalidHandle { return 0, os.EINVAL } var o writeToOp @@ -443,14 +443,14 @@ func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (n int, err os.Error) type acceptOp struct { anOp - newsock int + newsock syscall.Handle attrs [2]syscall.RawSockaddrAny // space for local and remote address only } func (o *acceptOp) Submit() (errno int) { var d uint32 l := uint32(unsafe.Sizeof(o.attrs[0])) - return syscall.AcceptEx(uint32(o.fd.sysfd), uint32(o.newsock), + return syscall.AcceptEx(o.fd.sysfd, o.newsock, (*byte)(unsafe.Pointer(&o.attrs[0])), 0, l, l, &d, &o.o) } @@ -459,7 +459,7 @@ func (o *acceptOp) Name() string { } func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os.Error) { - if fd == nil || fd.sysfd == -1 { + if fd == nil || fd.sysfd == syscall.InvalidHandle { return nil, os.EINVAL } fd.incref() @@ -478,7 +478,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os. // Associate our new socket with IOCP. onceStartServer.Do(startServer) - if _, e = syscall.CreateIoCompletionPort(int32(s), resultsrv.iocp, 0, 0); e != 0 { + if _, e = syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); e != 0 { return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, os.Errno(e)} } @@ -493,7 +493,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os. } // Inherit properties of the listening socket. - e = syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, fd.sysfd) + e = syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, int(fd.sysfd)) if e != 0 { closesocket(s) return nil, err diff --git a/src/pkg/net/hosts_test.go b/src/pkg/net/hosts_test.go index e5793eef2..1bd00541c 100644 --- a/src/pkg/net/hosts_test.go +++ b/src/pkg/net/hosts_test.go @@ -59,7 +59,7 @@ func TestLookupHost(t *testing.T) { // duplicate addresses (a common bug due to the way // getaddrinfo works). addrs, _ := LookupHost("localhost") - sort.SortStrings(addrs) + sort.Strings(addrs) for i := 0; i+1 < len(addrs); i++ { if addrs[i] == addrs[i+1] { t.Fatalf("LookupHost(\"localhost\") = %v, has duplicate addresses", addrs) diff --git a/src/pkg/net/interface.go b/src/pkg/net/interface.go index f622487ab..f6de36f64 100644 --- a/src/pkg/net/interface.go +++ b/src/pkg/net/interface.go @@ -34,7 +34,41 @@ type Interface struct { MTU int // maximum transmission unit Name string // e.g., "en0", "lo0", "eth0.100" HardwareAddr HardwareAddr // IEEE MAC-48, EUI-48 and EUI-64 form - rawFlags int + Flags Flags // e.g., FlagUp, FlagLoopback, FlagMulticast +} + +type Flags uint + +const ( + FlagUp Flags = 1 << iota // interface is up + FlagBroadcast // interface supports broadcast access capability + FlagLoopback // interface is a loopback interface + FlagPointToPoint // interface belongs to a point-to-point link + FlagMulticast // interface supports multicast access capability +) + +var flagNames = []string{ + "up", + "broadcast", + "loopback", + "pointtopoint", + "multicast", +} + +func (f Flags) String() string { + s := "" + for i, name := range flagNames { + if f&(1<<uint(i)) != 0 { + if s != "" { + s += "|" + } + s += name + } + } + if s == "" { + s = "0" + } + return s } // Addrs returns interface addresses for a specific interface. diff --git a/src/pkg/net/interface_bsd.go b/src/pkg/net/interface_bsd.go index 141b95b38..a4c3e71fe 100644 --- a/src/pkg/net/interface_bsd.go +++ b/src/pkg/net/interface_bsd.go @@ -12,49 +12,6 @@ import ( "unsafe" ) -// IsUp returns true if ifi is up. -func (ifi *Interface) IsUp() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_UP != 0 -} - -// IsLoopback returns true if ifi is a loopback interface. -func (ifi *Interface) IsLoopback() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_LOOPBACK != 0 -} - -// CanBroadcast returns true if ifi supports a broadcast access -// capability. -func (ifi *Interface) CanBroadcast() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_BROADCAST != 0 -} - -// IsPointToPoint returns true if ifi belongs to a point-to-point -// link. -func (ifi *Interface) IsPointToPoint() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_POINTOPOINT != 0 -} - -// CanMulticast returns true if ifi supports a multicast access -// capability. -func (ifi *Interface) CanMulticast() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_MULTICAST != 0 -} - // If the ifindex is zero, interfaceTable returns mappings of all // network interfaces. Otheriwse it returns a mapping of a specific // interface. @@ -106,7 +63,7 @@ func newLink(m *syscall.InterfaceMessage) ([]Interface, os.Error) { // NOTE: SockaddrDatalink.Data is minimum work area, // can be larger. m.Data = m.Data[unsafe.Offsetof(v.Data):] - ifi := Interface{Index: int(m.Header.Index), rawFlags: int(m.Header.Flags)} + ifi := Interface{Index: int(m.Header.Index), Flags: linkFlags(m.Header.Flags)} var name [syscall.IFNAMSIZ]byte for i := 0; i < int(v.Nlen); i++ { name[i] = byte(m.Data[i]) @@ -125,6 +82,26 @@ func newLink(m *syscall.InterfaceMessage) ([]Interface, os.Error) { return ift, nil } +func linkFlags(rawFlags int32) Flags { + var f Flags + if rawFlags&syscall.IFF_UP != 0 { + f |= FlagUp + } + if rawFlags&syscall.IFF_BROADCAST != 0 { + f |= FlagBroadcast + } + if rawFlags&syscall.IFF_LOOPBACK != 0 { + f |= FlagLoopback + } + if rawFlags&syscall.IFF_POINTOPOINT != 0 { + f |= FlagPointToPoint + } + if rawFlags&syscall.IFF_MULTICAST != 0 { + f |= FlagMulticast + } + return f +} + // If the ifindex is zero, interfaceAddrTable returns addresses // for all network interfaces. Otherwise it returns addresses // for a specific interface. diff --git a/src/pkg/net/interface_linux.go b/src/pkg/net/interface_linux.go index 5c9657834..e869cd630 100644 --- a/src/pkg/net/interface_linux.go +++ b/src/pkg/net/interface_linux.go @@ -12,49 +12,6 @@ import ( "unsafe" ) -// IsUp returns true if ifi is up. -func (ifi *Interface) IsUp() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_UP != 0 -} - -// IsLoopback returns true if ifi is a loopback interface. -func (ifi *Interface) IsLoopback() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_LOOPBACK != 0 -} - -// CanBroadcast returns true if ifi supports a broadcast access -// capability. -func (ifi *Interface) CanBroadcast() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_BROADCAST != 0 -} - -// IsPointToPoint returns true if ifi belongs to a point-to-point -// link. -func (ifi *Interface) IsPointToPoint() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_POINTOPOINT != 0 -} - -// CanMulticast returns true if ifi supports a multicast access -// capability. -func (ifi *Interface) CanMulticast() bool { - if ifi == nil { - return false - } - return ifi.rawFlags&syscall.IFF_MULTICAST != 0 -} - // If the ifindex is zero, interfaceTable returns mappings of all // network interfaces. Otheriwse it returns a mapping of a specific // interface. @@ -98,7 +55,7 @@ done: } func newLink(attrs []syscall.NetlinkRouteAttr, ifim *syscall.IfInfomsg) Interface { - ifi := Interface{Index: int(ifim.Index), rawFlags: int(ifim.Flags)} + ifi := Interface{Index: int(ifim.Index), Flags: linkFlags(ifim.Flags)} for _, a := range attrs { switch a.Attr.Type { case syscall.IFLA_ADDRESS: @@ -112,7 +69,7 @@ func newLink(attrs []syscall.NetlinkRouteAttr, ifim *syscall.IfInfomsg) Interfac ifi.HardwareAddr = a.Value[:] } case syscall.IFLA_IFNAME: - ifi.Name = string(a.Value[:]) + ifi.Name = string(a.Value[:len(a.Value)-1]) case syscall.IFLA_MTU: ifi.MTU = int(uint32(a.Value[3])<<24 | uint32(a.Value[2])<<16 | uint32(a.Value[1])<<8 | uint32(a.Value[0])) } @@ -120,6 +77,26 @@ func newLink(attrs []syscall.NetlinkRouteAttr, ifim *syscall.IfInfomsg) Interfac return ifi } +func linkFlags(rawFlags uint32) Flags { + var f Flags + if rawFlags&syscall.IFF_UP != 0 { + f |= FlagUp + } + if rawFlags&syscall.IFF_BROADCAST != 0 { + f |= FlagBroadcast + } + if rawFlags&syscall.IFF_LOOPBACK != 0 { + f |= FlagLoopback + } + if rawFlags&syscall.IFF_POINTOPOINT != 0 { + f |= FlagPointToPoint + } + if rawFlags&syscall.IFF_MULTICAST != 0 { + f |= FlagMulticast + } + return f +} + // If the ifindex is zero, interfaceAddrTable returns addresses // for all network interfaces. Otherwise it returns addresses // for a specific interface. diff --git a/src/pkg/net/interface_stub.go b/src/pkg/net/interface_stub.go index feb871bb5..24a7431c5 100644 --- a/src/pkg/net/interface_stub.go +++ b/src/pkg/net/interface_stub.go @@ -8,34 +8,6 @@ package net import "os" -// IsUp returns true if ifi is up. -func (ifi *Interface) IsUp() bool { - return false -} - -// IsLoopback returns true if ifi is a loopback interface. -func (ifi *Interface) IsLoopback() bool { - return false -} - -// CanBroadcast returns true if ifi supports a broadcast access -// capability. -func (ifi *Interface) CanBroadcast() bool { - return false -} - -// IsPointToPoint returns true if ifi belongs to a point-to-point -// link. -func (ifi *Interface) IsPointToPoint() bool { - return false -} - -// CanMulticast returns true if ifi supports a multicast access -// capability. -func (ifi *Interface) CanMulticast() bool { - return false -} - // If the ifindex is zero, interfaceTable returns mappings of all // network interfaces. Otheriwse it returns a mapping of a specific // interface. diff --git a/src/pkg/net/interface_test.go b/src/pkg/net/interface_test.go index 938434623..ac523a049 100644 --- a/src/pkg/net/interface_test.go +++ b/src/pkg/net/interface_test.go @@ -19,30 +19,6 @@ func sameInterface(i, j *Interface) bool { return false } -func interfaceFlagsString(ifi *Interface) string { - fs := "<" - if ifi.IsUp() { - fs += "UP," - } - if ifi.CanBroadcast() { - fs += "BROADCAST," - } - if ifi.IsLoopback() { - fs += "LOOPBACK," - } - if ifi.IsPointToPoint() { - fs += "POINTOPOINT," - } - if ifi.CanMulticast() { - fs += "MULTICAST," - } - if len(fs) > 1 { - fs = fs[:len(fs)-1] - } - fs += ">" - return fs -} - func TestInterfaces(t *testing.T) { ift, err := Interfaces() if err != nil { @@ -69,11 +45,11 @@ func TestInterfaces(t *testing.T) { if err != nil { t.Fatalf("Interface.Addrs() failed: %v", err) } - t.Logf("%s: flags %s, ifindex %v, mtu %v\n", ifi.Name, interfaceFlagsString(&ifi), ifi.Index, ifi.MTU) + t.Logf("%q: flags %q, ifindex %v, mtu %v\n", ifi.Name, ifi.Flags.String(), ifi.Index, ifi.MTU) for _, ifa := range ifat { - t.Logf("\tinterface address %s\n", ifa.String()) + t.Logf("\tinterface address %q\n", ifa.String()) } - t.Logf("\thardware address %v", ifi.HardwareAddr.String()) + t.Logf("\thardware address %q", ifi.HardwareAddr.String()) } } @@ -85,6 +61,6 @@ func TestInterfaceAddrs(t *testing.T) { t.Logf("table: len/cap = %v/%v\n", len(ifat), cap(ifat)) for _, ifa := range ifat { - t.Logf("interface address %s\n", ifa.String()) + t.Logf("interface address %q\n", ifa.String()) } } diff --git a/src/pkg/net/interface_windows.go b/src/pkg/net/interface_windows.go new file mode 100644 index 000000000..198e4096f --- /dev/null +++ b/src/pkg/net/interface_windows.go @@ -0,0 +1,152 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Network interface identification for Windows + +package net + +import ( + "os" + "syscall" + "unsafe" +) + +func bytePtrToString(p *uint8) string { + a := (*[10000]uint8)(unsafe.Pointer(p)) + i := 0 + for a[i] != 0 { + i++ + } + return string(a[:i]) +} + +func getAdapterList() (*syscall.IpAdapterInfo, os.Error) { + b := make([]byte, 1000) + l := uint32(len(b)) + a := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) + e := syscall.GetAdaptersInfo(a, &l) + if e == syscall.ERROR_BUFFER_OVERFLOW { + b = make([]byte, l) + a = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) + e = syscall.GetAdaptersInfo(a, &l) + } + if e != 0 { + return nil, os.NewSyscallError("GetAdaptersInfo", e) + } + return a, nil +} + +func getInterfaceList() ([]syscall.InterfaceInfo, os.Error) { + s, e := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP) + if e != 0 { + return nil, os.NewSyscallError("Socket", e) + } + defer syscall.Closesocket(s) + + ii := [20]syscall.InterfaceInfo{} + ret := uint32(0) + size := uint32(unsafe.Sizeof(ii)) + e = syscall.WSAIoctl(s, syscall.SIO_GET_INTERFACE_LIST, nil, 0, (*byte)(unsafe.Pointer(&ii[0])), size, &ret, nil, 0) + if e != 0 { + return nil, os.NewSyscallError("WSAIoctl", e) + } + c := ret / uint32(unsafe.Sizeof(ii[0])) + return ii[:c-1], nil +} + + +// If the ifindex is zero, interfaceTable returns mappings of all +// network interfaces. Otheriwse it returns a mapping of a specific +// interface. +func interfaceTable(ifindex int) ([]Interface, os.Error) { + ai, e := getAdapterList() + if e != nil { + return nil, e + } + + ii, e := getInterfaceList() + if e != nil { + return nil, e + } + + var ift []Interface + for ; ai != nil; ai = ai.Next { + index := ai.Index + if ifindex == 0 || ifindex == int(index) { + var flags Flags + + row := syscall.MibIfRow{Index: index} + e := syscall.GetIfEntry(&row) + if e != 0 { + return nil, os.NewSyscallError("GetIfEntry", e) + } + + for _, ii := range ii { + ip := (*syscall.RawSockaddrInet4)(unsafe.Pointer(&ii.Address)).Addr + ipv4 := IPv4(ip[0], ip[1], ip[2], ip[3]) + ipl := &ai.IpAddressList + for ipl != nil { + ips := bytePtrToString(&ipl.IpAddress.String[0]) + if ipv4.Equal(parseIPv4(ips)) { + break + } + ipl = ipl.Next + } + if ipl == nil { + continue + } + if ii.Flags&syscall.IFF_UP != 0 { + flags |= FlagUp + } + if ii.Flags&syscall.IFF_LOOPBACK != 0 { + flags |= FlagLoopback + } + if ii.Flags&syscall.IFF_BROADCAST != 0 { + flags |= FlagBroadcast + } + if ii.Flags&syscall.IFF_POINTTOPOINT != 0 { + flags |= FlagPointToPoint + } + if ii.Flags&syscall.IFF_MULTICAST != 0 { + flags |= FlagMulticast + } + } + + name := bytePtrToString(&ai.AdapterName[0]) + + ifi := Interface{ + Index: int(index), + MTU: int(row.Mtu), + Name: name, + HardwareAddr: HardwareAddr(row.PhysAddr[:row.PhysAddrLen]), + Flags: flags} + ift = append(ift, ifi) + } + } + return ift, nil +} + +// If the ifindex is zero, interfaceAddrTable returns addresses +// for all network interfaces. Otherwise it returns addresses +// for a specific interface. +func interfaceAddrTable(ifindex int) ([]Addr, os.Error) { + ai, e := getAdapterList() + if e != nil { + return nil, e + } + + var ifat []Addr + for ; ai != nil; ai = ai.Next { + index := ai.Index + if ifindex == 0 || ifindex == int(index) { + ipl := &ai.IpAddressList + for ; ipl != nil; ipl = ipl.Next { + ifa := IPAddr{} + ifa.IP = parseIPv4(bytePtrToString(&ipl.IpAddress.String[0])) + ifat = append(ifat, ifa.toAddr()) + } + } + } + return ifat, nil +} diff --git a/src/pkg/net/iprawsock.go b/src/pkg/net/iprawsock.go index a811027b1..43047a78e 100644 --- a/src/pkg/net/iprawsock.go +++ b/src/pkg/net/iprawsock.go @@ -294,7 +294,7 @@ func splitNetProto(netProto string) (net string, proto int, err os.Error) { onceReadProtocols.Do(readProtocols) i := last(netProto, ':') if i < 0 { // no colon - return "", 0, os.ErrorString("no IP protocol specified") + return "", 0, os.NewError("no IP protocol specified") } net = netProto[0:i] protostr := netProto[i+1:] diff --git a/src/pkg/net/ipsock.go b/src/pkg/net/ipsock.go index 0b8c388f1..e831d9afc 100644 --- a/src/pkg/net/ipsock.go +++ b/src/pkg/net/ipsock.go @@ -26,28 +26,26 @@ import ( // boolean value is true, kernel supports IPv6 IPv4-mapping. func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { var probes = []struct { - s int la TCPAddr ok bool }{ // IPv6 communication capability - {-1, TCPAddr{IP: ParseIP("::1")}, false}, + {TCPAddr{IP: ParseIP("::1")}, false}, // IPv6 IPv4-mapped address communication capability - {-1, TCPAddr{IP: IPv4(127, 0, 0, 1)}, false}, + {TCPAddr{IP: IPv4(127, 0, 0, 1)}, false}, } - var errno int for i := range probes { - probes[i].s, errno = syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + s, errno := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) if errno != 0 { continue } - defer closesocket(probes[i].s) + defer closesocket(s) sa, err := probes[i].la.toAddr().sockaddr(syscall.AF_INET6) if err != nil { continue } - errno = syscall.Bind(probes[i].s, sa) + errno = syscall.Bind(s, sa) if errno != 0 { continue } @@ -270,12 +268,16 @@ func JoinHostPort(host, port string) string { // Convert "host:port" into IP address and port. func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) { + var ( + addr IP + p, i int + ok bool + ) host, port, err := SplitHostPort(hostport) if err != nil { goto Error } - var addr IP if host != "" { // Try as an IP address. addr = ParseIP(host) @@ -302,7 +304,7 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err os.Error) { } } - p, i, ok := dtoi(port, 0) + p, i, ok = dtoi(port, 0) if !ok || i != len(port) { p, err = LookupPort(net, port) if err != nil { diff --git a/src/pkg/net/lookup.go b/src/pkg/net/lookup.go deleted file mode 100644 index eeb22a8ae..000000000 --- a/src/pkg/net/lookup.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package net - -import ( - "os" -) - -// LookupHost looks up the given host using the local resolver. -// It returns an array of that host's addresses. -func LookupHost(host string) (addrs []string, err os.Error) { - addrs, err, ok := cgoLookupHost(host) - if !ok { - addrs, err = goLookupHost(host) - } - return -} - -// LookupIP looks up host using the local resolver. -// It returns an array of that host's IPv4 and IPv6 addresses. -func LookupIP(host string) (addrs []IP, err os.Error) { - addrs, err, ok := cgoLookupIP(host) - if !ok { - addrs, err = goLookupIP(host) - } - return -} - -// LookupPort looks up the port for the given network and service. -func LookupPort(network, service string) (port int, err os.Error) { - port, err, ok := cgoLookupPort(network, service) - if !ok { - port, err = goLookupPort(network, service) - } - return -} - -// LookupCNAME returns the canonical DNS host for the given name. -// Callers that do not care about the canonical name can call -// LookupHost or LookupIP directly; both take care of resolving -// the canonical name as part of the lookup. -func LookupCNAME(name string) (cname string, err os.Error) { - cname, err, ok := cgoLookupCNAME(name) - if !ok { - cname, err = goLookupCNAME(name) - } - return -} diff --git a/src/pkg/net/lookup_unix.go b/src/pkg/net/lookup_unix.go new file mode 100644 index 000000000..168d3fa6d --- /dev/null +++ b/src/pkg/net/lookup_unix.go @@ -0,0 +1,126 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +import ( + "os" + "rand" + "sort" +) + +// LookupHost looks up the given host using the local resolver. +// It returns an array of that host's addresses. +func LookupHost(host string) (addrs []string, err os.Error) { + addrs, err, ok := cgoLookupHost(host) + if !ok { + addrs, err = goLookupHost(host) + } + return +} + +// LookupIP looks up host using the local resolver. +// It returns an array of that host's IPv4 and IPv6 addresses. +func LookupIP(host string) (addrs []IP, err os.Error) { + addrs, err, ok := cgoLookupIP(host) + if !ok { + addrs, err = goLookupIP(host) + } + return +} + +// LookupPort looks up the port for the given network and service. +func LookupPort(network, service string) (port int, err os.Error) { + port, err, ok := cgoLookupPort(network, service) + if !ok { + port, err = goLookupPort(network, service) + } + return +} + +// LookupCNAME returns the canonical DNS host for the given name. +// Callers that do not care about the canonical name can call +// LookupHost or LookupIP directly; both take care of resolving +// the canonical name as part of the lookup. +func LookupCNAME(name string) (cname string, err os.Error) { + cname, err, ok := cgoLookupCNAME(name) + if !ok { + cname, err = goLookupCNAME(name) + } + return +} + +// LookupSRV tries to resolve an SRV query of the given service, +// protocol, and domain name, as specified in RFC 2782. In most cases +// the proto argument can be the same as the corresponding +// Addr.Network(). The returned records are sorted by priority +// and randomized by weight within a priority. +func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.Error) { + target := "_" + service + "._" + proto + "." + name + var records []dnsRR + cname, records, err = lookup(target, dnsTypeSRV) + if err != nil { + return + } + addrs = make([]*SRV, len(records)) + for i, rr := range records { + r := rr.(*dnsRR_SRV) + addrs[i] = &SRV{r.Target, r.Port, r.Priority, r.Weight} + } + sort.Sort(byPriorityWeight(addrs)) + i := 0 + for j := 1; j < len(addrs); j++ { + if addrs[i].Priority != addrs[j].Priority { + shuffleSRVByWeight(addrs[i:j]) + i = j + } + } + shuffleSRVByWeight(addrs[i:len(addrs)]) + return +} + +// LookupMX returns the DNS MX records for the given domain name sorted by preference. +func LookupMX(name string) (mx []*MX, err os.Error) { + _, rr, err := lookup(name, dnsTypeMX) + if err != nil { + return + } + mx = make([]*MX, len(rr)) + for i := range rr { + r := rr[i].(*dnsRR_MX) + mx[i] = &MX{r.Mx, r.Pref} + } + // Shuffle the records to match RFC 5321 when sorted + for i := range mx { + j := rand.Intn(i + 1) + mx[i], mx[j] = mx[j], mx[i] + } + sort.Sort(byPref(mx)) + return +} + +// LookupAddr performs a reverse lookup for the given address, returning a list +// of names mapping to that address. +func LookupAddr(addr string) (name []string, err os.Error) { + name = lookupStaticAddr(addr) + if len(name) > 0 { + return + } + var arpa string + arpa, err = reverseaddr(addr) + if err != nil { + return + } + var records []dnsRR + _, records, err = lookup(arpa, dnsTypePTR) + if err != nil { + return + } + name = make([]string, len(records)) + for i := range records { + r := records[i].(*dnsRR_PTR) + name[i] = r.Ptr + } + return +} diff --git a/src/pkg/net/resolv_windows.go b/src/pkg/net/lookup_windows.go index f7c3f51be..16b37f56c 100644 --- a/src/pkg/net/resolv_windows.go +++ b/src/pkg/net/lookup_windows.go @@ -14,8 +14,8 @@ import ( var hostentLock sync.Mutex var serventLock sync.Mutex -func goLookupHost(name string) (addrs []string, err os.Error) { - ips, err := goLookupIP(name) +func LookupHost(name string) (addrs []string, err os.Error) { + ips, err := LookupIP(name) if err != nil { return } @@ -26,7 +26,7 @@ func goLookupHost(name string) (addrs []string, err os.Error) { return } -func goLookupIP(name string) (addrs []IP, err os.Error) { +func LookupIP(name string) (addrs []IP, err os.Error) { hostentLock.Lock() defer hostentLock.Unlock() h, e := syscall.GetHostByName(name) @@ -47,7 +47,23 @@ func goLookupIP(name string) (addrs []IP, err os.Error) { return addrs, nil } -func goLookupCNAME(name string) (cname string, err os.Error) { +func LookupPort(network, service string) (port int, err os.Error) { + switch network { + case "tcp4", "tcp6": + network = "tcp" + case "udp4", "udp6": + network = "udp" + } + serventLock.Lock() + defer serventLock.Unlock() + s, e := syscall.GetServByName(service, network) + if e != 0 { + return 0, os.NewSyscallError("GetServByName", e) + } + return int(syscall.Ntohs(s.Port)), nil +} + +func LookupCNAME(name string) (cname string, err os.Error) { var r *syscall.DNSRecord e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &r, nil) if int(e) != 0 { @@ -61,13 +77,6 @@ func goLookupCNAME(name string) (cname string, err os.Error) { return } -type SRV struct { - Target string - Port uint16 - Priority uint16 - Weight uint16 -} - func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os.Error) { var r *syscall.DNSRecord target := "_" + service + "._" + proto + "." + name @@ -87,55 +96,12 @@ func LookupSRV(service, proto, name string) (cname string, addrs []*SRV, err os. return name, addrs, nil } -func goLookupPort(network, service string) (port int, err os.Error) { - switch network { - case "tcp4", "tcp6": - network = "tcp" - case "udp4", "udp6": - network = "udp" - } - serventLock.Lock() - defer serventLock.Unlock() - s, e := syscall.GetServByName(service, network) - if e != 0 { - return 0, os.NewSyscallError("GetServByName", e) - } - return int(syscall.Ntohs(s.Port)), nil -} +// TODO(brainman): implement LookupMX and LookupAddr. -// TODO(brainman): Following code is only to get tests running. - -func isDomainName(s string) bool { - panic("unimplemented") -} - -func reverseaddr(addr string) (arpa string, err os.Error) { - panic("unimplemented") -} - -func answer(name, server string, dns *dnsMsg, qtype uint16) (cname string, addrs []dnsRR, err os.Error) { - panic("unimplemented") +func LookupMX(name string) (mx []*MX, err os.Error) { + return nil, os.NewSyscallError("LookupMX", syscall.EWINDOWS) } -// DNSError represents a DNS lookup error. -type DNSError struct { - Error string // description of the error - Name string // name looked for - Server string // server used - IsTimeout bool +func LookupAddr(addr string) (name []string, err os.Error) { + return nil, os.NewSyscallError("LookupAddr", syscall.EWINDOWS) } - -func (e *DNSError) String() string { - if e == nil { - return "<nil>" - } - s := "lookup " + e.Name - if e.Server != "" { - s += " on " + e.Server - } - s += ": " + e.Error - return s -} - -func (e *DNSError) Timeout() bool { return e.IsTimeout } -func (e *DNSError) Temporary() bool { return e.IsTimeout } diff --git a/src/pkg/net/net.go b/src/pkg/net/net.go index 51db10739..5c84d3434 100644 --- a/src/pkg/net/net.go +++ b/src/pkg/net/net.go @@ -115,7 +115,7 @@ type Listener interface { Addr() Addr } -var errMissingAddress = os.ErrorString("missing address") +var errMissingAddress = os.NewError("missing address") type OpError struct { Op string diff --git a/src/pkg/net/net_test.go b/src/pkg/net/net_test.go index f7eae56fe..698a84527 100644 --- a/src/pkg/net/net_test.go +++ b/src/pkg/net/net_test.go @@ -7,7 +7,6 @@ package net import ( "flag" "regexp" - "runtime" "testing" ) @@ -103,9 +102,6 @@ var revAddrTests = []struct { } func TestReverseAddress(t *testing.T) { - if runtime.GOOS == "windows" { - return - } for i, tt := range revAddrTests { a, e := reverseaddr(tt.Addr) if len(tt.ErrPrefix) > 0 && e == nil { diff --git a/src/pkg/net/newpollserver.go b/src/pkg/net/newpollserver.go index fff54dba7..427208701 100644 --- a/src/pkg/net/newpollserver.go +++ b/src/pkg/net/newpollserver.go @@ -18,12 +18,7 @@ func newPollServer() (s *pollServer, err os.Error) { } var e int if e = syscall.SetNonblock(s.pr.Fd(), true); e != 0 { - Errno: - err = &os.PathError{"setnonblock", s.pr.Name(), os.Errno(e)} - Error: - s.pr.Close() - s.pw.Close() - return nil, err + goto Errno } if e = syscall.SetNonblock(s.pw.Fd(), true); e != 0 { goto Errno @@ -38,4 +33,11 @@ func newPollServer() (s *pollServer, err os.Error) { s.pending = make(map[int]*netFD) go s.Run() return s, nil + +Errno: + err = &os.PathError{"setnonblock", s.pr.Name(), os.Errno(e)} +Error: + s.pr.Close() + s.pw.Close() + return nil, err } diff --git a/src/pkg/net/sendfile_windows.go b/src/pkg/net/sendfile_windows.go new file mode 100644 index 000000000..3772eee24 --- /dev/null +++ b/src/pkg/net/sendfile_windows.go @@ -0,0 +1,68 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +import ( + "io" + "os" + "syscall" +) + +type sendfileOp struct { + anOp + src syscall.Handle // source + n uint32 +} + +func (o *sendfileOp) Submit() (errno int) { + return syscall.TransmitFile(o.fd.sysfd, o.src, o.n, 0, &o.o, nil, syscall.TF_WRITE_BEHIND) +} + +func (o *sendfileOp) Name() string { + return "TransmitFile" +} + +// sendFile copies the contents of r to c using the TransmitFile +// system call to minimize copies. +// +// if handled == true, sendFile returns the number of bytes copied and any +// non-EOF error. +// +// if handled == false, sendFile performed no work. +// +// Note that sendfile for windows does not suppport >2GB file. +func sendFile(c *netFD, r io.Reader) (written int64, err os.Error, handled bool) { + var n int64 = 0 // by default, copy until EOF + + lr, ok := r.(*io.LimitedReader) + if ok { + n, r = lr.N, lr.R + if n <= 0 { + return 0, nil, true + } + } + f, ok := r.(*os.File) + if !ok { + return 0, nil, false + } + + c.wio.Lock() + defer c.wio.Unlock() + c.incref() + defer c.decref() + + var o sendfileOp + o.Init(c) + o.n = uint32(n) + o.src = f.Fd() + done, err := iosrv.ExecIO(&o, 0) + if err != nil { + return 0, err, false + } + if lr != nil { + lr.N -= int64(done) + } + return int64(done), nil, true +} diff --git a/src/pkg/net/sock.go b/src/pkg/net/sock.go index eae7f3711..821716e43 100644 --- a/src/pkg/net/sock.go +++ b/src/pkg/net/sock.go @@ -50,8 +50,7 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal if ra != nil { if err = fd.connect(ra); err != nil { - fd.sysfd = -1 - closesocket(s) + fd.Close() return nil, err } } @@ -65,25 +64,25 @@ func socket(net string, f, p, t int, la, ra syscall.Sockaddr, toAddr func(syscal return fd, nil } -func setsockoptInt(fd, level, opt int, value int) os.Error { - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, opt, value)) +func setsockoptInt(fd *netFD, level, opt int, value int) os.Error { + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, level, opt, value)) } -func setsockoptNsec(fd, level, opt int, nsec int64) os.Error { +func setsockoptNsec(fd *netFD, level, opt int, nsec int64) os.Error { var tv = syscall.NsecToTimeval(nsec) - return os.NewSyscallError("setsockopt", syscall.SetsockoptTimeval(fd, level, opt, &tv)) + return os.NewSyscallError("setsockopt", syscall.SetsockoptTimeval(fd.sysfd, level, opt, &tv)) } func setReadBuffer(fd *netFD, bytes int) os.Error { fd.incref() defer fd.decref() - return setsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes) + return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes) } func setWriteBuffer(fd *netFD, bytes int) os.Error { fd.incref() defer fd.decref() - return setsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes) + return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes) } func setReadTimeout(fd *netFD, nsec int64) os.Error { @@ -106,7 +105,7 @@ func setTimeout(fd *netFD, nsec int64) os.Error { func setReuseAddr(fd *netFD, reuse bool) os.Error { fd.incref() defer fd.decref() - return setsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse)) + return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, boolint(reuse)) } func bindToDevice(fd *netFD, dev string) os.Error { @@ -117,19 +116,19 @@ func bindToDevice(fd *netFD, dev string) os.Error { func setDontRoute(fd *netFD, dontroute bool) os.Error { fd.incref() defer fd.decref() - return setsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute)) + return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_DONTROUTE, boolint(dontroute)) } func setKeepAlive(fd *netFD, keepalive bool) os.Error { fd.incref() defer fd.decref() - return setsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive)) + return setsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive)) } func setNoDelay(fd *netFD, noDelay bool) os.Error { fd.incref() defer fd.decref() - return setsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(noDelay)) + return setsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(noDelay)) } func setLinger(fd *netFD, sec int) os.Error { diff --git a/src/pkg/net/sock_windows.go b/src/pkg/net/sock_windows.go index e17c60b98..c6dbd0465 100644 --- a/src/pkg/net/sock_windows.go +++ b/src/pkg/net/sock_windows.go @@ -10,7 +10,7 @@ import ( "syscall" ) -func setKernelSpecificSockopt(s, f int) { +func setKernelSpecificSockopt(s syscall.Handle, f int) { // Allow reuse of recently-used addresses and ports. syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) diff --git a/src/pkg/net/udpsock.go b/src/pkg/net/udpsock.go index 5469acffa..94e249d62 100644 --- a/src/pkg/net/udpsock.go +++ b/src/pkg/net/udpsock.go @@ -281,7 +281,7 @@ func (c *UDPConn) BindToDevice(device string) os.Error { // Closing c does not affect f, and closing f does not affect c. func (c *UDPConn) File() (f *os.File, err os.Error) { return c.fd.dup() } -var errInvalidMulticast = os.ErrorString("invalid IPv4 multicast address") +var errInvalidMulticast = os.NewError("invalid IPv4 multicast address") // JoinGroup joins the IPv4 multicast group named by addr. // The UDPConn must use the "udp4" network. diff --git a/src/pkg/netchan/common.go b/src/pkg/netchan/common.go index a319391bf..ac1ca12f5 100644 --- a/src/pkg/netchan/common.go +++ b/src/pkg/netchan/common.go @@ -153,7 +153,7 @@ func (cs *clientSet) drain(timeout int64) os.Error { break } if timeout > 0 && time.Nanoseconds()-startTime >= timeout { - return os.ErrorString("timeout") + return os.NewError("timeout") } time.Sleep(100 * 1e6) // 100 milliseconds } @@ -186,7 +186,7 @@ func (cs *clientSet) sync(timeout int64) os.Error { break } if timeout > 0 && time.Nanoseconds()-startTime >= timeout { - return os.ErrorString("timeout") + return os.NewError("timeout") } time.Sleep(100 * 1e6) // 100 milliseconds } diff --git a/src/pkg/netchan/export.go b/src/pkg/netchan/export.go index 1e5ccdb5c..7df736515 100644 --- a/src/pkg/netchan/export.go +++ b/src/pkg/netchan/export.go @@ -343,20 +343,20 @@ func (exp *Exporter) Sync(timeout int64) os.Error { func checkChan(chT interface{}, dir Dir) (reflect.Value, os.Error) { chanType := reflect.TypeOf(chT) if chanType.Kind() != reflect.Chan { - return reflect.Value{}, os.ErrorString("not a channel") + return reflect.Value{}, os.NewError("not a channel") } if dir != Send && dir != Recv { - return reflect.Value{}, os.ErrorString("unknown channel direction") + return reflect.Value{}, os.NewError("unknown channel direction") } switch chanType.ChanDir() { case reflect.BothDir: case reflect.SendDir: if dir != Recv { - return reflect.Value{}, os.ErrorString("to import/export with Send, must provide <-chan") + return reflect.Value{}, os.NewError("to import/export with Send, must provide <-chan") } case reflect.RecvDir: if dir != Send { - return reflect.Value{}, os.ErrorString("to import/export with Recv, must provide chan<-") + return reflect.Value{}, os.NewError("to import/export with Recv, must provide chan<-") } } return reflect.ValueOf(chT), nil @@ -376,7 +376,7 @@ func (exp *Exporter) Export(name string, chT interface{}, dir Dir) os.Error { defer exp.mu.Unlock() _, present := exp.names[name] if present { - return os.ErrorString("channel name already being exported:" + name) + return os.NewError("channel name already being exported:" + name) } exp.names[name] = &chanDir{ch, dir} return nil @@ -393,7 +393,7 @@ func (exp *Exporter) Hangup(name string) os.Error { // TODO drop all instances of channel from client sets exp.mu.Unlock() if !ok { - return os.ErrorString("netchan export: hangup: no such channel: " + name) + return os.NewError("netchan export: hangup: no such channel: " + name) } chDir.ch.Close() return nil diff --git a/src/pkg/netchan/import.go b/src/pkg/netchan/import.go index 7d96228c4..ec17d9777 100644 --- a/src/pkg/netchan/import.go +++ b/src/pkg/netchan/import.go @@ -102,7 +102,7 @@ func (imp *Importer) run() { if err.Error != "" { impLog("response error:", err.Error) select { - case imp.errors <- os.ErrorString(err.Error): + case imp.errors <- os.NewError(err.Error): continue // errors are not acknowledged default: imp.shutdown() @@ -203,7 +203,7 @@ func (imp *Importer) ImportNValues(name string, chT interface{}, dir Dir, size, defer imp.chanLock.Unlock() _, present := imp.names[name] if present { - return os.ErrorString("channel name already being imported:" + name) + return os.NewError("channel name already being imported:" + name) } if size < 1 { size = 1 @@ -254,7 +254,7 @@ func (imp *Importer) Hangup(name string) os.Error { defer imp.chanLock.Unlock() nc := imp.names[name] if nc == nil { - return os.ErrorString("netchan import: hangup: no such channel: " + name) + return os.NewError("netchan import: hangup: no such channel: " + name) } imp.names[name] = nil, false imp.chans[nc.id] = nil, false @@ -279,7 +279,7 @@ func (imp *Importer) Drain(timeout int64) os.Error { startTime := time.Nanoseconds() for imp.unackedCount() > 0 { if timeout > 0 && time.Nanoseconds()-startTime >= timeout { - return os.ErrorString("timeout") + return os.NewError("timeout") } time.Sleep(100 * 1e6) } diff --git a/src/pkg/os/Makefile b/src/pkg/os/Makefile index 497e5a958..354e1e8db 100644 --- a/src/pkg/os/Makefile +++ b/src/pkg/os/Makefile @@ -73,6 +73,7 @@ GOFILES_plan9=\ path_plan9.go\ sys_plan9.go\ exec_plan9.go\ + str.go\ GOFILES+=$(GOFILES_$(GOOS)) @@ -83,5 +84,5 @@ include ../../Make.pkg signal_unix.go: ../syscall/zerrors_$(GOOS)_$(GOARCH).go ./mkunixsignals.sh $< > $@ || rm -f $@ -signal_windows.go: ../syscall/ztypes_$(GOOS)_$(GOARCH).go +signal_windows.go: ../syscall/ztypes_$(GOOS).go ./mkunixsignals.sh $< > $@ || rm -f $@ diff --git a/src/pkg/os/env_windows.go b/src/pkg/os/env_windows.go index a45d79be3..e6ddc4065 100644 --- a/src/pkg/os/env_windows.go +++ b/src/pkg/os/env_windows.go @@ -119,7 +119,7 @@ func init() { if e != 0 { return } - defer syscall.LocalFree(uint32(uintptr(unsafe.Pointer(argv)))) + defer syscall.LocalFree(syscall.Handle(uintptr(unsafe.Pointer(argv)))) Args = make([]string, argc) for i, v := range (*argv)[:argc] { Args[i] = string(syscall.UTF16ToString((*v)[:])) diff --git a/src/pkg/os/error.go b/src/pkg/os/error.go index 2c4516ca7..b4511dd2f 100644 --- a/src/pkg/os/error.go +++ b/src/pkg/os/error.go @@ -9,20 +9,17 @@ type Error interface { String() string } -// A helper type that can be embedded or wrapped to simplify satisfying -// Error. -type ErrorString string +// // errorString is a helper type used by NewError. +type errorString string -func (e ErrorString) String() string { return string(e) } -func (e ErrorString) Temporary() bool { return false } -func (e ErrorString) Timeout() bool { return false } +func (e errorString) String() string { return string(e) } // Note: If the name of the function NewError changes, // pkg/go/doc/doc.go should be adjusted since it hardwires // this name in a heuristic. -// NewError converts s to an ErrorString, which satisfies the Error interface. -func NewError(s string) Error { return ErrorString(s) } +// // NewError returns a new error with error.String() == s. +func NewError(s string) Error { return errorString(s) } // PathError records an error and the operation and file path that caused it. type PathError struct { diff --git a/src/pkg/os/error_plan9.go b/src/pkg/os/error_plan9.go index 3374775b8..cacfc150c 100644 --- a/src/pkg/os/error_plan9.go +++ b/src/pkg/os/error_plan9.go @@ -45,6 +45,7 @@ var ( EEXIST = Eexist EIO = Eio EACCES = Eperm + EPERM = Eperm EISDIR = syscall.EISDIR ENAMETOOLONG = NewError("file name too long") diff --git a/src/pkg/os/exec.go b/src/pkg/os/exec.go index f62caf9a0..e2234f14a 100644 --- a/src/pkg/os/exec.go +++ b/src/pkg/os/exec.go @@ -37,6 +37,12 @@ type ProcAttr struct { // depending on the underlying operating system. A nil entry corresponds // to that file being closed when the process starts. Files []*File + + // Operating system-specific process creation attributes. + // Note that setting this field means that your program + // may not execute properly or even compile on some + // operating systems. + Sys *syscall.SysProcAttr } // Getpid returns the process id of the caller. diff --git a/src/pkg/os/exec_plan9.go b/src/pkg/os/exec_plan9.go index 11874aba6..2590dd67d 100644 --- a/src/pkg/os/exec_plan9.go +++ b/src/pkg/os/exec_plan9.go @@ -15,6 +15,7 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err E sysattr := &syscall.ProcAttr{ Dir: attr.Dir, Env: attr.Env, + Sys: attr.Sys, } // Create array of integer (system) fds. @@ -37,6 +38,17 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err E return newProcess(pid, h), nil } +// Kill causes the Process to exit immediately. +func (p *Process) Kill() Error { + f, e := OpenFile("/proc/"+itoa(p.Pid)+"/ctl", O_WRONLY, 0) + if iserror(e) { + return NewSyscallError("kill", e) + } + defer f.Close() + _, e = f.Write([]byte("kill")) + return e +} + // Exec replaces the current process with an execution of the // named binary, with arguments argv and environment envv. // If successful, Exec never returns. If it fails, it returns an Error. @@ -51,7 +63,9 @@ func Exec(name string, argv []string, envv []string) Error { } // Waitmsg stores the information about an exited process as reported by Wait. -type Waitmsg syscall.Waitmsg +type Waitmsg struct { + syscall.Waitmsg +} // Wait waits for the Process to exit or stop, and then returns a // Waitmsg describing its status and an Error, if any. The options @@ -75,7 +89,7 @@ func (p *Process) Wait(options int) (w *Waitmsg, err Error) { } } - return (*Waitmsg)(&waitmsg), nil + return &Waitmsg{waitmsg}, nil } // Wait waits for process pid to exit or stop, and then returns a @@ -109,6 +123,9 @@ func FindProcess(pid int) (p *Process, err Error) { return newProcess(pid, 0), nil } -func (w Waitmsg) String() string { +func (w *Waitmsg) String() string { + if w == nil { + return "<nil>" + } return "exit status: " + w.Msg } diff --git a/src/pkg/os/exec_posix.go b/src/pkg/os/exec_posix.go index bf992ef42..e2097700e 100644 --- a/src/pkg/os/exec_posix.go +++ b/src/pkg/os/exec_posix.go @@ -30,20 +30,14 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err E sysattr := &syscall.ProcAttr{ Dir: attr.Dir, Env: attr.Env, + Sys: attr.Sys, } if sysattr.Env == nil { sysattr.Env = Environ() } - // Create array of integer (system) fds. - intfd := make([]int, len(attr.Files)) - for i, f := range attr.Files { - if f == nil { - intfd[i] = -1 - } else { - intfd[i] = f.Fd() - } + for _, f := range attr.Files { + sysattr.Files = append(sysattr.Files, f.Fd()) } - sysattr.Files = intfd pid, h, e := syscall.StartProcess(name, argv, sysattr) if iserror(e) { @@ -127,7 +121,10 @@ func itod(i int) string { return string(b[bp:]) } -func (w Waitmsg) String() string { +func (w *Waitmsg) String() string { + if w == nil { + return "<nil>" + } // TODO(austin) Use signal names when possible? res := "" switch { diff --git a/src/pkg/os/exec_windows.go b/src/pkg/os/exec_windows.go index bac33b908..5b432d398 100644 --- a/src/pkg/os/exec_windows.go +++ b/src/pkg/os/exec_windows.go @@ -10,17 +10,17 @@ import ( ) func (p *Process) Wait(options int) (w *Waitmsg, err Error) { - s, e := syscall.WaitForSingleObject(int32(p.handle), syscall.INFINITE) + s, e := syscall.WaitForSingleObject(syscall.Handle(p.handle), syscall.INFINITE) switch s { case syscall.WAIT_OBJECT_0: break case syscall.WAIT_FAILED: return nil, NewSyscallError("WaitForSingleObject", e) default: - return nil, ErrorString("os: unexpected result from WaitForSingleObject") + return nil, NewError("os: unexpected result from WaitForSingleObject") } var ec uint32 - e = syscall.GetExitCodeProcess(int32(p.handle), &ec) + e = syscall.GetExitCodeProcess(syscall.Handle(p.handle), &ec) if e != 0 { return nil, NewSyscallError("GetExitCodeProcess", e) } @@ -31,7 +31,7 @@ func (p *Process) Wait(options int) (w *Waitmsg, err Error) { func (p *Process) Signal(sig Signal) Error { switch sig.(UnixSignal) { case SIGKILL: - e := syscall.TerminateProcess(int32(p.handle), 1) + e := syscall.TerminateProcess(syscall.Handle(p.handle), 1) return NewSyscallError("TerminateProcess", e) } return Errno(syscall.EWINDOWS) @@ -41,7 +41,7 @@ func (p *Process) Release() Error { if p.handle == -1 { return EINVAL } - e := syscall.CloseHandle(int32(p.handle)) + e := syscall.CloseHandle(syscall.Handle(p.handle)) if e != 0 { return NewSyscallError("CloseHandle", e) } diff --git a/src/pkg/os/file.go b/src/pkg/os/file.go index dff8fa862..4335d45e5 100644 --- a/src/pkg/os/file.go +++ b/src/pkg/os/file.go @@ -4,39 +4,17 @@ // Package os provides a platform-independent interface to operating system // functionality. The design is Unix-like. +// The os interface is intended to be uniform across all operating systems. +// Features not generally available appear in the system-specific package syscall. package os import ( - "runtime" - "sync" "syscall" ) -// File represents an open file descriptor. -type File struct { - fd int - name string - dirinfo *dirInfo // nil unless directory being read - nepipe int // number of consecutive EPIPE in Write - l sync.Mutex // used to implement windows pread/pwrite -} - -// Fd returns the integer Unix file descriptor referencing the open file. -func (file *File) Fd() int { return file.fd } - // Name returns the name of the file as presented to Open. func (file *File) Name() string { return file.name } -// NewFile returns a new File with the given file descriptor and name. -func NewFile(fd int, name string) *File { - if fd < 0 { - return nil - } - f := &File{fd: fd, name: name} - runtime.SetFinalizer(f, (*File).Close) - return f -} - // Stdin, Stdout, and Stderr are open Files pointing to the standard input, // standard output, and standard error file descriptors. var ( @@ -185,9 +163,7 @@ func (file *File) WriteString(s string) (ret int, err Error) { if file == nil { return 0, EINVAL } - b := syscall.StringByteSlice(s) - b = b[0 : len(b)-1] - return file.Write(b) + return file.Write([]byte(s)) } // Mkdir creates a new directory with the specified name and permission bits. diff --git a/src/pkg/os/file_plan9.go b/src/pkg/os/file_plan9.go index 7b473f802..03792191e 100644 --- a/src/pkg/os/file_plan9.go +++ b/src/pkg/os/file_plan9.go @@ -9,6 +9,32 @@ import ( "syscall" ) +// File represents an open file descriptor. +type File struct { + fd int + name string + dirinfo *dirInfo // nil unless directory being read + nepipe int // number of consecutive EPIPE in Write +} + +// Fd returns the integer Unix file descriptor referencing the open file. +func (file *File) Fd() int { + if file == nil { + return -1 + } + return file.fd +} + +// NewFile returns a new File with the given file descriptor and name. +func NewFile(fd int, name string) *File { + if fd < 0 { + return nil + } + f := &File{fd: fd, name: name} + runtime.SetFinalizer(f, (*File).Close) + return f +} + // Auxiliary information if the File describes a directory type dirInfo struct { buf [syscall.STATMAX]byte // buffer for directory I/O @@ -30,14 +56,43 @@ const DevNull = "/dev/null" // methods on the returned File can be used for I/O. // It returns the File and an Error, if any. func OpenFile(name string, flag int, perm uint32) (file *File, err Error) { - var fd int - var e syscall.Error + var ( + fd int + e syscall.Error + create bool + excl bool + trunc bool + append bool + ) - syscall.ForkLock.RLock() if flag&O_CREATE == O_CREATE { - fd, e = syscall.Create(name, flag & ^O_CREATE, perm) + flag = flag & ^O_CREATE + create = true + } + if flag&O_EXCL == O_EXCL { + excl = true + } + if flag&O_TRUNC == O_TRUNC { + trunc = true + } + // O_APPEND is emulated on Plan 9 + if flag&O_APPEND == O_APPEND { + flag = flag &^ O_APPEND + append = true + } + + syscall.ForkLock.RLock() + if (create && trunc) || excl { + fd, e = syscall.Create(name, flag, perm) } else { fd, e = syscall.Open(name, flag) + if e != nil && create { + var e1 syscall.Error + fd, e1 = syscall.Create(name, flag, perm) + if e1 == nil { + e = nil + } + } } syscall.ForkLock.RUnlock() @@ -45,6 +100,12 @@ func OpenFile(name string, flag int, perm uint32) (file *File, err Error) { return nil, &PathError{"open", name, e} } + if append { + if _, e = syscall.Seek(fd, 0, SEEK_END); e != nil { + return nil, &PathError{"seek", name, e} + } + } + return NewFile(fd, name), nil } @@ -69,8 +130,12 @@ func (file *File) Close() Error { // Stat returns the FileInfo structure describing file. // It returns the FileInfo and an error, if any. -func (file *File) Stat() (fi *FileInfo, err Error) { - return dirstat(file) +func (f *File) Stat() (fi *FileInfo, err Error) { + d, err := dirstat(f) + if iserror(err) { + return nil, err + } + return fileInfoFromStat(new(FileInfo), d), err } // Truncate changes the size of the file. @@ -90,10 +155,15 @@ func (f *File) Truncate(size int64) Error { // Chmod changes the mode of the file to mode. func (f *File) Chmod(mode uint32) Error { var d Dir - d.Null() + var mask = ^uint32(0777) - d.Mode = mode & 0777 + d.Null() + odir, e := dirstat(f) + if iserror(e) { + return &PathError{"chmod", f.name, e} + } + d.Mode = (odir.Mode & mask) | (mode &^ mask) if e := syscall.Fwstat(f.fd, pdir(nil, &d)); iserror(e) { return &PathError{"chmod", f.name, e} } @@ -188,10 +258,15 @@ func Rename(oldname, newname string) Error { // Chmod changes the mode of the named file to mode. func Chmod(name string, mode uint32) Error { var d Dir - d.Null() + var mask = ^uint32(0777) - d.Mode = mode & 0777 + d.Null() + odir, e := dirstat(name) + if iserror(e) { + return &PathError{"chmod", name, e} + } + d.Mode = (odir.Mode & mask) | (mode &^ mask) if e := syscall.Wstat(name, pdir(nil, &d)); iserror(e) { return &PathError{"chmod", name, e} } diff --git a/src/pkg/os/file_posix.go b/src/pkg/os/file_posix.go index f1191d61f..0791a0dc0 100644 --- a/src/pkg/os/file_posix.go +++ b/src/pkg/os/file_posix.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// The os package provides a platform-independent interface to operating -// system functionality. The design is Unix-like. package os import ( @@ -23,26 +21,6 @@ func epipecheck(file *File, e int) { } } - -// Pipe returns a connected pair of Files; reads from r return bytes written to w. -// It returns the files and an Error, if any. -func Pipe() (r *File, w *File, err Error) { - var p [2]int - - // See ../syscall/exec.go for description of lock. - syscall.ForkLock.RLock() - e := syscall.Pipe(p[0:]) - if iserror(e) { - syscall.ForkLock.RUnlock() - return nil, nil, NewSyscallError("pipe", e) - } - syscall.CloseOnExec(p[0]) - syscall.CloseOnExec(p[1]) - syscall.ForkLock.RUnlock() - - return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil -} - // Stat returns a FileInfo structure describing the named file and an error, if any. // If name names a valid symbolic link, the returned FileInfo describes // the file pointed at by the link and has fi.FollowedSymlink set to true. diff --git a/src/pkg/os/file_unix.go b/src/pkg/os/file_unix.go index def9b3bf0..301c2f473 100644 --- a/src/pkg/os/file_unix.go +++ b/src/pkg/os/file_unix.go @@ -9,6 +9,32 @@ import ( "syscall" ) +// File represents an open file descriptor. +type File struct { + fd int + name string + dirinfo *dirInfo // nil unless directory being read + nepipe int // number of consecutive EPIPE in Write +} + +// Fd returns the integer Unix file descriptor referencing the open file. +func (file *File) Fd() int { + if file == nil { + return -1 + } + return file.fd +} + +// NewFile returns a new File with the given file descriptor and name. +func NewFile(fd int, name string) *File { + if fd < 0 { + return nil + } + f := &File{fd: fd, name: name} + runtime.SetFinalizer(f, (*File).Close) + return f +} + // Auxiliary information if the File describes a directory type dirInfo struct { buf []byte // buffer for directory I/O @@ -161,3 +187,22 @@ func basename(name string) string { return name } + +// Pipe returns a connected pair of Files; reads from r return bytes written to w. +// It returns the files and an Error, if any. +func Pipe() (r *File, w *File, err Error) { + var p [2]int + + // See ../syscall/exec.go for description of lock. + syscall.ForkLock.RLock() + e := syscall.Pipe(p[0:]) + if iserror(e) { + syscall.ForkLock.RUnlock() + return nil, nil, NewSyscallError("pipe", e) + } + syscall.CloseOnExec(p[0]) + syscall.CloseOnExec(p[1]) + syscall.ForkLock.RUnlock() + + return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil +} diff --git a/src/pkg/os/file_windows.go b/src/pkg/os/file_windows.go index 80886f6f5..70dd6e241 100644 --- a/src/pkg/os/file_windows.go +++ b/src/pkg/os/file_windows.go @@ -6,9 +6,37 @@ package os import ( "runtime" + "sync" "syscall" ) +// File represents an open file descriptor. +type File struct { + fd syscall.Handle + name string + dirinfo *dirInfo // nil unless directory being read + nepipe int // number of consecutive EPIPE in Write + l sync.Mutex // used to implement windows pread/pwrite +} + +// Fd returns the Windows handle referencing the open file. +func (file *File) Fd() syscall.Handle { + if file == nil { + return syscall.InvalidHandle + } + return file.fd +} + +// NewFile returns a new File with the given file descriptor and name. +func NewFile(fd syscall.Handle, name string) *File { + if fd < 0 { + return nil + } + f := &File{fd: fd, name: name} + runtime.SetFinalizer(f, (*File).Close) + return f +} + // Auxiliary information if the File describes a directory type dirInfo struct { stat syscall.Stat_t @@ -40,7 +68,7 @@ func openDir(name string) (file *File, err Error) { if e != 0 { return nil, &PathError{"open", name, Errno(e)} } - f := NewFile(int(r), name) + f := NewFile(r, name) d.usefirststat = true f.dirinfo = d return f, nil @@ -85,15 +113,15 @@ func (file *File) Close() Error { } var e int if file.isdir() { - e = syscall.FindClose(int32(file.fd)) + e = syscall.FindClose(syscall.Handle(file.fd)) } else { - e = syscall.CloseHandle(int32(file.fd)) + e = syscall.CloseHandle(syscall.Handle(file.fd)) } var err Error if e != 0 { err = &PathError{"close", file.name, Errno(e)} } - file.fd = -1 // so it can't be closed again + file.fd = syscall.InvalidHandle // so it can't be closed again // no need for a finalizer anymore runtime.SetFinalizer(file, nil) @@ -102,7 +130,7 @@ func (file *File) Close() Error { func (file *File) statFile(name string) (fi *FileInfo, err Error) { var stat syscall.ByHandleFileInformation - e := syscall.GetFileInformationByHandle(int32(file.fd), &stat) + e := syscall.GetFileInformationByHandle(syscall.Handle(file.fd), &stat) if e != 0 { return nil, &PathError{"stat", file.name, Errno(e)} } @@ -156,7 +184,7 @@ func (file *File) Readdir(n int) (fi []FileInfo, err Error) { if di.usefirststat { di.usefirststat = false } else { - e := syscall.FindNextFile(int32(file.fd), &di.stat.Windata) + e := syscall.FindNextFile(syscall.Handle(file.fd), &di.stat.Windata) if e != 0 { if e == syscall.ERROR_NO_MORE_FILES { break @@ -207,7 +235,7 @@ func (f *File) pread(b []byte, off int64) (n int, err int) { Offset: uint32(off), } var done uint32 - e = syscall.ReadFile(int32(f.fd), b, &done, &o) + e = syscall.ReadFile(syscall.Handle(f.fd), b, &done, &o) if e != 0 { return 0, e } @@ -237,7 +265,7 @@ func (f *File) pwrite(b []byte, off int64) (n int, err int) { Offset: uint32(off), } var done uint32 - e = syscall.WriteFile(int32(f.fd), b, &done, &o) + e = syscall.WriteFile(syscall.Handle(f.fd), b, &done, &o) if e != 0 { return 0, e } @@ -268,3 +296,22 @@ func Truncate(name string, size int64) Error { } return nil } + +// Pipe returns a connected pair of Files; reads from r return bytes written to w. +// It returns the files and an Error, if any. +func Pipe() (r *File, w *File, err Error) { + var p [2]syscall.Handle + + // See ../syscall/exec.go for description of lock. + syscall.ForkLock.RLock() + e := syscall.Pipe(p[0:]) + if iserror(e) { + syscall.ForkLock.RUnlock() + return nil, nil, NewSyscallError("pipe", e) + } + syscall.CloseOnExec(p[0]) + syscall.CloseOnExec(p[1]) + syscall.ForkLock.RUnlock() + + return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil +} diff --git a/src/pkg/os/mkunixsignals.sh b/src/pkg/os/mkunixsignals.sh index 6ec764cbd..4bbc43f3d 100755 --- a/src/pkg/os/mkunixsignals.sh +++ b/src/pkg/os/mkunixsignals.sh @@ -14,7 +14,7 @@ import ( "syscall" ) -var _ = syscall.Syscall // in case there are zero signals +var _ = syscall.Open // in case there are zero signals const ( EOH diff --git a/src/pkg/os/os_test.go b/src/pkg/os/os_test.go index 8eabdee6b..c22b536d5 100644 --- a/src/pkg/os/os_test.go +++ b/src/pkg/os/os_test.go @@ -359,8 +359,8 @@ func TestReaddirNValues(t *testing.T) { } func TestHardLink(t *testing.T) { - // Hardlinks are not supported under windows. - if syscall.OS == "windows" { + // Hardlinks are not supported under windows or Plan 9. + if syscall.OS == "windows" || syscall.OS == "plan9" { return } from, to := "hardlinktestfrom", "hardlinktestto" @@ -392,8 +392,8 @@ func TestHardLink(t *testing.T) { } func TestSymLink(t *testing.T) { - // Symlinks are not supported under windows. - if syscall.OS == "windows" { + // Symlinks are not supported under windows or Plan 9. + if syscall.OS == "windows" || syscall.OS == "plan9" { return } from, to := "symlinktestfrom", "symlinktestto" @@ -454,8 +454,8 @@ func TestSymLink(t *testing.T) { } func TestLongSymlink(t *testing.T) { - // Symlinks are not supported under windows. - if syscall.OS == "windows" { + // Symlinks are not supported under windows or Plan 9. + if syscall.OS == "windows" || syscall.OS == "plan9" { return } s := "0123456789abcdef" @@ -588,8 +588,9 @@ func checkUidGid(t *testing.T, path string, uid, gid int) { } func TestChown(t *testing.T) { - // Chown is not supported under windows. - if syscall.OS == "windows" { + // Chown is not supported under windows or Plan 9. + // Plan9 provides a native ChownPlan9 version instead. + if syscall.OS == "windows" || syscall.OS == "plan9" { return } // Use TempDir() to make sure we're on a local file system, @@ -708,7 +709,11 @@ func TestChtimes(t *testing.T) { t.Fatalf("second Stat %s: %s", f.Name(), err) } - if postStat.Atime_ns >= preStat.Atime_ns { + /* Plan 9: + Mtime is the time of the last change of content. Similarly, atime is set whenever the + contents are accessed; also, it is set whenever mtime is set. + */ + if postStat.Atime_ns >= preStat.Atime_ns && syscall.OS != "plan9" { t.Errorf("Atime_ns didn't go backwards; was=%d, after=%d", preStat.Atime_ns, postStat.Atime_ns) @@ -733,6 +738,10 @@ func TestChdirAndGetwd(t *testing.T) { // These are chosen carefully not to be symlinks on a Mac // (unlike, say, /var, /etc, and /tmp). dirs := []string{"/", "/usr/bin"} + // /usr/bin does not usually exist on Plan 9. + if syscall.OS == "plan9" { + dirs = []string{"/", "/usr"} + } for mode := 0; mode < 2; mode++ { for _, d := range dirs { if mode == 0 { @@ -858,7 +867,15 @@ func TestOpenError(t *testing.T) { t.Errorf("Open(%q, %d) returns error of %T type; want *os.PathError", tt.path, tt.mode, err) } if perr.Error != tt.error { - t.Errorf("Open(%q, %d) = _, %q; want %q", tt.path, tt.mode, perr.Error.String(), tt.error.String()) + if syscall.OS == "plan9" { + syscallErrStr := perr.Error.String() + expectedErrStr := strings.Replace(tt.error.String(), "file ", "", 1) + if !strings.HasSuffix(syscallErrStr, expectedErrStr) { + t.Errorf("Open(%q, %d) = _, %q; want suffix %q", tt.path, tt.mode, syscallErrStr, expectedErrStr) + } + } else { + t.Errorf("Open(%q, %d) = _, %q; want %q", tt.path, tt.mode, perr.Error.String(), tt.error.String()) + } } } } @@ -893,7 +910,8 @@ func run(t *testing.T, cmd []string) string { func TestHostname(t *testing.T) { // There is no other way to fetch hostname on windows, but via winapi. - if syscall.OS == "windows" { + // On Plan 9 it is can be taken from #c/sysname as Hostname() does. + if syscall.OS == "windows" || syscall.OS == "plan9" { return } // Check internal Hostname() against the output of /bin/hostname. @@ -1024,3 +1042,11 @@ func TestStatDirWithTrailingSlash(t *testing.T) { t.Fatal("stat failed:", err) } } + +func TestNilWaitmsgString(t *testing.T) { + var w *Waitmsg + s := w.String() + if s != "<nil>" { + t.Errorf("(*Waitmsg)(nil).String() = %q, want %q", s, "<nil>") + } +} diff --git a/src/pkg/os/path_test.go b/src/pkg/os/path_test.go index d58945aab..31acbaa43 100644 --- a/src/pkg/os/path_test.go +++ b/src/pkg/os/path_test.go @@ -166,8 +166,8 @@ func TestRemoveAll(t *testing.T) { } func TestMkdirAllWithSymlink(t *testing.T) { - if runtime.GOOS == "windows" { - t.Log("Skipping test: symlinks don't exist under Windows") + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { + t.Log("Skipping test: symlinks don't exist under Windows/Plan 9") return } @@ -191,7 +191,7 @@ func TestMkdirAllWithSymlink(t *testing.T) { } func TestMkdirAllAtSlash(t *testing.T) { - if runtime.GOOS == "windows" { + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { return } RemoveAll("/_go_os_test") diff --git a/src/pkg/os/path_windows.go b/src/pkg/os/path_windows.go index 8740a9e61..61f2ca59f 100644 --- a/src/pkg/os/path_windows.go +++ b/src/pkg/os/path_windows.go @@ -6,7 +6,7 @@ package os const ( PathSeparator = '\\' // OS-specific path separator - PathListSeparator = ':' // OS-specific path list separator + PathListSeparator = ';' // OS-specific path list separator ) // IsPathSeparator returns true if c is a directory separator character. diff --git a/src/pkg/os/stat_plan9.go b/src/pkg/os/stat_plan9.go index e96749d33..d2300d598 100644 --- a/src/pkg/os/stat_plan9.go +++ b/src/pkg/os/stat_plan9.go @@ -26,7 +26,7 @@ func fileInfoFromStat(fi *FileInfo, d *Dir) *FileInfo { } // arg is an open *File or a path string. -func dirstat(arg interface{}) (fi *FileInfo, err Error) { +func dirstat(arg interface{}) (d *Dir, err Error) { var name string nd := syscall.STATFIXLEN + 16*4 @@ -62,8 +62,7 @@ func dirstat(arg interface{}) (fi *FileInfo, err Error) { if e != nil { return nil, &PathError{"stat", name, e} } - - return fileInfoFromStat(new(FileInfo), d), nil + return d, e } } @@ -73,12 +72,20 @@ func dirstat(arg interface{}) (fi *FileInfo, err Error) { // Stat returns a FileInfo structure describing the named file and an error, if any. func Stat(name string) (fi *FileInfo, err Error) { - return dirstat(name) + d, err := dirstat(name) + if iserror(err) { + return nil, err + } + return fileInfoFromStat(new(FileInfo), d), err } // Lstat returns the FileInfo structure describing the named file and an // error, if any. If the file is a symbolic link (though Plan 9 does not have symbolic links), // the returned FileInfo describes the symbolic link. Lstat makes no attempt to follow the link. func Lstat(name string) (fi *FileInfo, err Error) { - return dirstat(name) + d, err := dirstat(name) + if iserror(err) { + return nil, err + } + return fileInfoFromStat(new(FileInfo), d), err } diff --git a/src/pkg/os/str.go b/src/pkg/os/str.go new file mode 100644 index 000000000..8dc9e4747 --- /dev/null +++ b/src/pkg/os/str.go @@ -0,0 +1,20 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package os + +func itoa(val int) string { // do it here rather than with fmt to avoid dependency + if val < 0 { + return "-" + itoa(-val) + } + var buf [32]byte // big enough for int64 + i := len(buf) - 1 + for val >= 10 { + buf[i] = byte(val%10 + '0') + i-- + val /= 10 + } + buf[i] = byte(val + '0') + return string(buf[i:]) +} diff --git a/src/pkg/os/types.go b/src/pkg/os/types.go index 79f6e9d49..df57b59a3 100644 --- a/src/pkg/os/types.go +++ b/src/pkg/os/types.go @@ -27,7 +27,7 @@ type FileInfo struct { Atime_ns int64 // access time; nanoseconds since epoch. Mtime_ns int64 // modified time; nanoseconds since epoch. Ctime_ns int64 // status change time; nanoseconds since epoch. - Name string // name of file as presented to Open. + Name string // base name of the file name provided in Open, Stat, etc. FollowedSymlink bool // followed a symlink to get this information } diff --git a/src/pkg/patch/patch.go b/src/pkg/patch/patch.go index d4977dc99..fcc8307e0 100644 --- a/src/pkg/patch/patch.go +++ b/src/pkg/patch/patch.go @@ -319,4 +319,4 @@ func hasPrefix(s []byte, t string) bool { // splitLines returns the result of splitting s into lines. // The \n on each line is preserved. -func splitLines(s []byte) [][]byte { return bytes.SplitAfter(s, newline, -1) } +func splitLines(s []byte) [][]byte { return bytes.SplitAfter(s, newline) } diff --git a/src/pkg/patch/textdiff.go b/src/pkg/patch/textdiff.go index c7e693fc6..482bd6781 100644 --- a/src/pkg/patch/textdiff.go +++ b/src/pkg/patch/textdiff.go @@ -17,6 +17,8 @@ type TextChunk struct { } func ParseTextDiff(raw []byte) (TextDiff, os.Error) { + var chunkHeader []byte + // Copy raw so it is safe to keep references to slices. _, chunks := sections(raw, "@@ -") delta := 0 @@ -26,13 +28,12 @@ func ParseTextDiff(raw []byte) (TextDiff, os.Error) { // Parse start line: @@ -oldLine,oldCount +newLine,newCount @@ junk chunk := splitLines(raw) - chunkHeader := chunk[0] + chunkHeader = chunk[0] var ok bool var oldLine, oldCount, newLine, newCount int s := chunkHeader if oldLine, s, ok = atoi(s, "@@ -", 10); !ok { - ErrChunkHdr: - return nil, SyntaxError("unexpected chunk header line: " + string(chunkHeader)) + goto ErrChunkHdr } if len(s) == 0 || s[0] != ',' { oldCount = 1 @@ -145,6 +146,9 @@ func ParseTextDiff(raw []byte) (TextDiff, os.Error) { } } return diff, nil + +ErrChunkHdr: + return nil, SyntaxError("unexpected chunk header line: " + string(chunkHeader)) } var ErrPatchFailure = os.NewError("patch did not apply cleanly") diff --git a/src/pkg/path/filepath/match.go b/src/pkg/path/filepath/match.go index 9c344309d..7fcc214c0 100644 --- a/src/pkg/path/filepath/match.go +++ b/src/pkg/path/filepath/match.go @@ -272,7 +272,7 @@ func glob(dir, pattern string, matches []string) (m []string, e os.Error) { if err != nil { return } - sort.SortStrings(names) + sort.Strings(names) for _, n := range names { matched, err := Match(pattern, n) diff --git a/src/pkg/path/filepath/path.go b/src/pkg/path/filepath/path.go index dcd8017ad..b181483ed 100644 --- a/src/pkg/path/filepath/path.go +++ b/src/pkg/path/filepath/path.go @@ -136,7 +136,7 @@ func SplitList(path string) []string { if path == "" { return []string{} } - return strings.Split(path, string(ListSeparator), -1) + return strings.Split(path, string(ListSeparator)) } // Split splits path immediately following the final Separator, diff --git a/src/pkg/path/filepath/path_test.go b/src/pkg/path/filepath/path_test.go index 6a5dd5b00..58c4c0301 100644 --- a/src/pkg/path/filepath/path_test.go +++ b/src/pkg/path/filepath/path_test.go @@ -293,10 +293,6 @@ func (v *TestVisitor) VisitFile(path string, f *os.FileInfo) { } func TestWalk(t *testing.T) { - // TODO(brainman): enable test once Windows version is implemented. - if runtime.GOOS == "windows" { - return - } makeTree(t) // 1) ignore error handling, expect none diff --git a/src/pkg/rand/rand_test.go b/src/pkg/rand/rand_test.go index a689da848..3ebc1141d 100644 --- a/src/pkg/rand/rand_test.go +++ b/src/pkg/rand/rand_test.go @@ -45,12 +45,12 @@ func (this *statsResults) checkSimilarDistribution(expected *statsResults) os.Er if !nearEqual(this.mean, expected.mean, expected.closeEnough, expected.maxError) { s := fmt.Sprintf("mean %v != %v (allowed error %v, %v)", this.mean, expected.mean, expected.closeEnough, expected.maxError) fmt.Println(s) - return os.ErrorString(s) + return os.NewError(s) } if !nearEqual(this.stddev, expected.stddev, 0, expected.maxError) { s := fmt.Sprintf("stddev %v != %v (allowed error %v, %v)", this.stddev, expected.stddev, expected.closeEnough, expected.maxError) fmt.Println(s) - return os.ErrorString(s) + return os.NewError(s) } return nil } diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go index c83a9b75f..34d74b37a 100644 --- a/src/pkg/reflect/all_test.go +++ b/src/pkg/reflect/all_test.go @@ -127,17 +127,17 @@ var typeTests = []pair{ }, {struct { x struct { - a int8 "hi there" + a int8 `reflect:"hi there"` } }{}, - `struct { a int8 "hi there" }`, + `struct { a int8 "reflect:\"hi there\"" }`, }, {struct { x struct { - a int8 "hi \x00there\t\n\"\\" + a int8 `reflect:"hi \x00there\t\n\"\\"` } }{}, - `struct { a int8 "hi \x00there\t\n\"\\" }`, + `struct { a int8 "reflect:\"hi \\x00there\\t\\n\\\"\\\\\"" }`, }, {struct { x struct { @@ -423,7 +423,7 @@ func TestAll(t *testing.T) { // make sure tag strings are not part of element type typ = TypeOf(struct { - d []uint32 "TAG" + d []uint32 `reflect:"TAG"` }{}).Field(0).Type testType(t, 14, typ, "[]uint32") } @@ -1050,6 +1050,12 @@ type Point struct { x, y int } +// This will be index 0. +func (p Point) AnotherMethod(scale int) int { + return -1 +} + +// This will be index 1. func (p Point) Dist(scale int) int { // println("Point.Dist", p.x, p.y, scale) return p.x*p.x*scale + p.y*p.y*scale @@ -1058,26 +1064,52 @@ func (p Point) Dist(scale int) int { func TestMethod(t *testing.T) { // Non-curried method of type. p := Point{3, 4} - i := TypeOf(p).Method(0).Func.Call([]Value{ValueOf(p), ValueOf(10)})[0].Int() + i := TypeOf(p).Method(1).Func.Call([]Value{ValueOf(p), ValueOf(10)})[0].Int() if i != 250 { t.Errorf("Type Method returned %d; want 250", i) } - i = TypeOf(&p).Method(0).Func.Call([]Value{ValueOf(&p), ValueOf(10)})[0].Int() + m, ok := TypeOf(p).MethodByName("Dist") + if !ok { + t.Fatalf("method by name failed") + } + m.Func.Call([]Value{ValueOf(p), ValueOf(10)})[0].Int() + if i != 250 { + t.Errorf("Type MethodByName returned %d; want 250", i) + } + + i = TypeOf(&p).Method(1).Func.Call([]Value{ValueOf(&p), ValueOf(10)})[0].Int() if i != 250 { t.Errorf("Pointer Type Method returned %d; want 250", i) } + m, ok = TypeOf(&p).MethodByName("Dist") + if !ok { + t.Fatalf("ptr method by name failed") + } + i = m.Func.Call([]Value{ValueOf(&p), ValueOf(10)})[0].Int() + if i != 250 { + t.Errorf("Pointer Type MethodByName returned %d; want 250", i) + } + // Curried method of value. - i = ValueOf(p).Method(0).Call([]Value{ValueOf(10)})[0].Int() + i = ValueOf(p).Method(1).Call([]Value{ValueOf(10)})[0].Int() if i != 250 { t.Errorf("Value Method returned %d; want 250", i) } + i = ValueOf(p).MethodByName("Dist").Call([]Value{ValueOf(10)})[0].Int() + if i != 250 { + t.Errorf("Value MethodByName returned %d; want 250", i) + } // Curried method of pointer. - i = ValueOf(&p).Method(0).Call([]Value{ValueOf(10)})[0].Int() + i = ValueOf(&p).Method(1).Call([]Value{ValueOf(10)})[0].Int() if i != 250 { - t.Errorf("Value Method returned %d; want 250", i) + t.Errorf("Pointer Value Method returned %d; want 250", i) + } + i = ValueOf(&p).MethodByName("Dist").Call([]Value{ValueOf(10)})[0].Int() + if i != 250 { + t.Errorf("Pointer Value MethodByName returned %d; want 250", i) } // Curried method of interface value. @@ -1094,6 +1126,10 @@ func TestMethod(t *testing.T) { if i != 250 { t.Errorf("Interface Method returned %d; want 250", i) } + i = pv.MethodByName("Dist").Call([]Value{ValueOf(10)})[0].Int() + if i != 250 { + t.Errorf("Interface MethodByName returned %d; want 250", i) + } } func TestInterfaceSet(t *testing.T) { @@ -1508,3 +1544,23 @@ func TestVariadic(t *testing.T) { t.Errorf("after Fprintf CallSlice: %q != %q", b.String(), "hello 42 world") } } + +var tagGetTests = []struct { + Tag StructTag + Key string + Value string +}{ + {`protobuf:"PB(1,2)"`, `protobuf`, `PB(1,2)`}, + {`protobuf:"PB(1,2)"`, `foo`, ``}, + {`protobuf:"PB(1,2)"`, `rotobuf`, ``}, + {`protobuf:"PB(1,2)" json:"name"`, `json`, `name`}, + {`protobuf:"PB(1,2)" json:"name"`, `protobuf`, `PB(1,2)`}, +} + +func TestTagGet(t *testing.T) { + for _, tt := range tagGetTests { + if v := tt.Tag.Get(tt.Key); v != tt.Value { + t.Errorf("StructTag(%#q).Get(%#q) = %#q, want %#q", tt.Tag, tt.Key, v, tt.Value) + } + } +} diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go index aef6370db..a120da732 100644 --- a/src/pkg/reflect/type.go +++ b/src/pkg/reflect/type.go @@ -47,6 +47,16 @@ type Type interface { // method signature, without a receiver, and the Func field is nil. Method(int) Method + // MethodByName returns the method with that name in the type's + // method set and a boolean indicating if the method was found. + // + // For a non-interface type T or *T, the returned Method's Type and Func + // fields describe a function whose first argument is the receiver. + // + // For an interface type, the returned Method's Type field gives the + // method signature, without a receiver, and the Func field is nil. + MethodByName(string) (Method, bool) + // NumMethod returns the number of methods in the type's method set. NumMethod() int @@ -264,7 +274,7 @@ const ( // arrayType represents a fixed array type. type arrayType struct { - commonType "array" + commonType `reflect:"array"` elem *runtime.Type slice *runtime.Type len uintptr @@ -272,14 +282,14 @@ type arrayType struct { // chanType represents a channel type. type chanType struct { - commonType "chan" + commonType `reflect:"chan"` elem *runtime.Type dir uintptr } // funcType represents a function type. type funcType struct { - commonType "func" + commonType `reflect:"func"` dotdotdot bool in []*runtime.Type out []*runtime.Type @@ -294,26 +304,26 @@ type imethod struct { // interfaceType represents an interface type. type interfaceType struct { - commonType "interface" + commonType `reflect:"interface"` methods []imethod } // mapType represents a map type. type mapType struct { - commonType "map" + commonType `reflect:"map"` key *runtime.Type elem *runtime.Type } // ptrType represents a pointer type. type ptrType struct { - commonType "ptr" + commonType `reflect:"ptr"` elem *runtime.Type } // sliceType represents a slice type. type sliceType struct { - commonType "slice" + commonType `reflect:"slice"` elem *runtime.Type } @@ -328,7 +338,7 @@ type structField struct { // structType represents a struct type. type structType struct { - commonType "struct" + commonType `reflect:"struct"` fields []structField } @@ -344,6 +354,7 @@ type Method struct { Name string Type Type Func Value + Index int } // High bit says whether type has @@ -451,6 +462,7 @@ func (t *uncommonType) Method(i int) (m Method) { m.Type = toType(p.typ) fn := p.tfn m.Func = valueFromIword(flag, m.Type, iword(fn)) + m.Index = i return } @@ -461,6 +473,20 @@ func (t *uncommonType) NumMethod() int { return len(t.methods) } +func (t *uncommonType) MethodByName(name string) (m Method, ok bool) { + if t == nil { + return + } + var p *method + for i := range t.methods { + p = &t.methods[i] + if p.name != nil && *p.name == name { + return t.Method(i), true + } + } + return +} + // TODO(rsc): 6g supplies these, but they are not // as efficient as they could be: they have commonType // as the receiver instead of *commonType. @@ -480,6 +506,14 @@ func (t *commonType) Method(i int) (m Method) { return t.uncommonType.Method(i) } +func (t *commonType) MethodByName(name string) (m Method, ok bool) { + if t.Kind() == Interface { + tt := (*interfaceType)(unsafe.Pointer(t)) + return tt.MethodByName(name) + } + return t.uncommonType.MethodByName(name) +} + func (t *commonType) PkgPath() string { return t.uncommonType.PkgPath() } @@ -636,22 +670,98 @@ func (t *interfaceType) Method(i int) (m Method) { m.PkgPath = *p.pkgPath } m.Type = toType(p.typ) + m.Index = i return } // NumMethod returns the number of interface methods in the type's method set. func (t *interfaceType) NumMethod() int { return len(t.methods) } +// MethodByName method with the given name in the type's method set. +func (t *interfaceType) MethodByName(name string) (m Method, ok bool) { + if t == nil { + return + } + var p *imethod + for i := range t.methods { + p = &t.methods[i] + if *p.name == name { + return t.Method(i), true + } + } + return +} + type StructField struct { PkgPath string // empty for uppercase Name Name string Type Type - Tag string + Tag StructTag Offset uintptr Index []int Anonymous bool } +// A StructTag is the tag string in a struct field. +// +// By convention, tag strings are a concatenation of +// optionally space-separated key:"value" pairs. +// Each key is a non-empty string consisting of non-control +// characters other than space (U+0020 ' '), quote (U+0022 '"'), +// and colon (U+003A ':'). Each value is quoted using U+0022 '"' +// characters and Go string literal syntax. +type StructTag string + +// Get returns the value associated with key in the tag string. +// If there is no such key in the tag, Get returns the empty string. +// If the tag does not have the conventional format, the value +// returned by Get is unspecified, +func (tag StructTag) Get(key string) string { + for tag != "" { + // skip leading space + i := 0 + for i < len(tag) && tag[i] == ' ' { + i++ + } + tag = tag[i:] + if tag == "" { + break + } + + // scan to colon. + // a space or a quote is a syntax error + i = 0 + for i < len(tag) && tag[i] != ' ' && tag[i] != ':' && tag[i] != '"' { + i++ + } + if i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { + break + } + name := string(tag[:i]) + tag = tag[i+1:] + + // scan quoted string to find value + i = 1 + for i < len(tag) && tag[i] != '"' { + if tag[i] == '\\' { + i++ + } + i++ + } + if i >= len(tag) { + break + } + qvalue := string(tag[:i+1]) + tag = tag[i+1:] + + if key == name { + value, _ := strconv.Unquote(qvalue) + return value + } + } + return "" +} + // Field returns the i'th struct field. func (t *structType) Field(i int) (f StructField) { if i < 0 || i >= len(t.fields) { @@ -673,7 +783,7 @@ func (t *structType) Field(i int) (f StructField) { f.PkgPath = *p.pkgPath } if p.tag != nil { - f.Tag = *p.tag + f.Tag = StructTag(*p.tag) } f.Offset = p.offset f.Index = []int{i} @@ -827,7 +937,7 @@ func (t *commonType) runtimeType() *runtime.Type { i runtime.Type ct commonType } - return (*runtime.Type)(unsafe.Pointer(uintptr(unsafe.Pointer(t)) - uintptr(unsafe.Offsetof(rt.ct)))) + return (*runtime.Type)(unsafe.Pointer(uintptr(unsafe.Pointer(t)) - unsafe.Offsetof(rt.ct))) } // PtrTo returns the pointer type with element t. @@ -888,7 +998,7 @@ func PtrTo(t Type) Type { p.uncommonType = nil p.ptrToThis = nil - p.elem = (*runtime.Type)(unsafe.Pointer(uintptr(unsafe.Pointer(ct)) - uintptr(unsafe.Offsetof(rt.ptrType)))) + p.elem = (*runtime.Type)(unsafe.Pointer(uintptr(unsafe.Pointer(ct)) - unsafe.Offsetof(rt.ptrType))) ptrMap.m[ct] = p ptrMap.Unlock() diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go index 3abe13e04..bfeb3267c 100644 --- a/src/pkg/reflect/value.go +++ b/src/pkg/reflect/value.go @@ -11,7 +11,7 @@ import ( "unsafe" ) -const ptrSize = uintptr(unsafe.Sizeof((*byte)(nil))) +const ptrSize = unsafe.Sizeof((*byte)(nil)) const cannotSet = "cannot set value obtained from unexported struct field" // TODO: This will have to go away when @@ -933,7 +933,7 @@ func (v Value) Kind() Kind { } // Len returns v's length. -// It panics if v's Kind is not Array, Chan, Map, or Slice. +// It panics if v's Kind is not Array, Chan, Map, Slice, or String. func (v Value) Len() int { iv := v.internal() switch iv.kind { @@ -945,6 +945,8 @@ func (v Value) Len() int { return int(maplen(iv.word)) case Slice: return (*SliceHeader)(iv.addr).Len + case String: + return (*StringHeader)(iv.addr).Len } panic(&ValueError{"reflect.Value.Len", iv.kind}) } @@ -1023,6 +1025,23 @@ func (v Value) Method(i int) Value { return Value{v.Internal, i + 1} } +// MethodByName returns a function value corresponding to the method +// of v with the given name. +// The arguments to a Call on the returned function should not include +// a receiver; the returned function will always use v as the receiver. +// It returns the zero Value if no method was found. +func (v Value) MethodByName(name string) Value { + iv := v.internal() + if iv.kind == Invalid { + panic(&ValueError{"reflect.Value.MethodByName", Invalid}) + } + m, ok := iv.typ.MethodByName(name) + if ok { + return Value{v.Internal, m.Index + 1} + } + return Value{} +} + // NumField returns the number of fields in the struct v. // It panics if v's Kind is not Struct. func (v Value) NumField() int { diff --git a/src/pkg/regexp/regexp.go b/src/pkg/regexp/regexp.go index e3221ac9d..e8d4c087c 100644 --- a/src/pkg/regexp/regexp.go +++ b/src/pkg/regexp/regexp.go @@ -87,16 +87,16 @@ func (e Error) String() string { // Error codes returned by failures to parse an expression. var ( - ErrInternal = Error("internal error") - ErrUnmatchedLpar = Error("unmatched '('") - ErrUnmatchedRpar = Error("unmatched ')'") - ErrUnmatchedLbkt = Error("unmatched '['") - ErrUnmatchedRbkt = Error("unmatched ']'") - ErrBadRange = Error("bad range in character class") - ErrExtraneousBackslash = Error("extraneous backslash") - ErrBadClosure = Error("repeated closure (**, ++, etc.)") - ErrBareClosure = Error("closure applies to nothing") - ErrBadBackslash = Error("illegal backslash escape") + ErrInternal = Error("regexp: internal error") + ErrUnmatchedLpar = Error("regexp: unmatched '('") + ErrUnmatchedRpar = Error("regexp: unmatched ')'") + ErrUnmatchedLbkt = Error("regexp: unmatched '['") + ErrUnmatchedRbkt = Error("regexp: unmatched ']'") + ErrBadRange = Error("regexp: bad range in character class") + ErrExtraneousBackslash = Error("regexp: extraneous backslash") + ErrBadClosure = Error("regexp: repeated closure (**, ++, etc.)") + ErrBareClosure = Error("regexp: closure applies to nothing") + ErrBadBackslash = Error("regexp: illegal backslash escape") ) const ( @@ -158,6 +158,7 @@ func (i *instr) print() { // Regexp is the representation of a compiled regular expression. // The public interface is entirely through methods. +// A Regexp is safe for concurrent use by multiple goroutines. type Regexp struct { expr string // the original expression prefix string // initial plain text string diff --git a/src/pkg/rpc/client.go b/src/pkg/rpc/client.go index a8e560cbe..b1828614f 100644 --- a/src/pkg/rpc/client.go +++ b/src/pkg/rpc/client.go @@ -23,7 +23,7 @@ func (e ServerError) String() string { return string(e) } -const ErrShutdown = os.ErrorString("connection is shut down") +var ErrShutdown = os.NewError("connection is shut down") // Call represents an active RPC. type Call struct { @@ -110,7 +110,7 @@ func (client *Client) input() { if response.Error == "" { err = client.codec.ReadResponseBody(c.Reply) if err != nil { - c.Error = os.ErrorString("reading body " + err.String()) + c.Error = os.NewError("reading body " + err.String()) } } else { // We've got an error response. Give this to the request; @@ -119,7 +119,7 @@ func (client *Client) input() { c.Error = ServerError(response.Error) err = client.codec.ReadResponseBody(nil) if err != nil { - err = os.ErrorString("reading error body: " + err.String()) + err = os.NewError("reading error body: " + err.String()) } } c.done() @@ -221,7 +221,7 @@ func DialHTTPPath(network, address, path string) (*Client, os.Error) { return NewClient(conn), nil } if err == nil { - err = os.ErrorString("unexpected HTTP response: " + resp.Status) + err = os.NewError("unexpected HTTP response: " + resp.Status) } conn.Close() return nil, &net.OpError{"dial-http", network + " " + address, nil, err} diff --git a/src/pkg/rpc/jsonrpc/all_test.go b/src/pkg/rpc/jsonrpc/all_test.go index 764ee7ff3..c1a9e8ecb 100644 --- a/src/pkg/rpc/jsonrpc/all_test.go +++ b/src/pkg/rpc/jsonrpc/all_test.go @@ -35,7 +35,7 @@ func (t *Arith) Mul(args *Args, reply *Reply) os.Error { func (t *Arith) Div(args *Args, reply *Reply) os.Error { if args.B == 0 { - return os.ErrorString("divide by zero") + return os.NewError("divide by zero") } reply.C = args.A / args.B return nil @@ -51,9 +51,9 @@ func init() { func TestServer(t *testing.T) { type addResp struct { - Id interface{} "id" - Result Reply "result" - Error interface{} "error" + Id interface{} `json:"id"` + Result Reply `json:"result"` + Error interface{} `json:"error"` } cli, srv := net.Pipe() diff --git a/src/pkg/rpc/jsonrpc/client.go b/src/pkg/rpc/jsonrpc/client.go index 57e977d32..577d0ce42 100644 --- a/src/pkg/rpc/jsonrpc/client.go +++ b/src/pkg/rpc/jsonrpc/client.go @@ -44,9 +44,9 @@ func NewClientCodec(conn io.ReadWriteCloser) rpc.ClientCodec { } type clientRequest struct { - Method string "method" - Params [1]interface{} "params" - Id uint64 "id" + Method string `json:"method"` + Params [1]interface{} `json:"params"` + Id uint64 `json:"id"` } func (c *clientCodec) WriteRequest(r *rpc.Request, param interface{}) os.Error { @@ -60,9 +60,9 @@ func (c *clientCodec) WriteRequest(r *rpc.Request, param interface{}) os.Error { } type clientResponse struct { - Id uint64 "id" - Result *json.RawMessage "result" - Error interface{} "error" + Id uint64 `json:"id"` + Result *json.RawMessage `json:"result"` + Error interface{} `json:"error"` } func (r *clientResponse) reset() { diff --git a/src/pkg/rpc/jsonrpc/server.go b/src/pkg/rpc/jsonrpc/server.go index 9c6b8b40d..9801fdf22 100644 --- a/src/pkg/rpc/jsonrpc/server.go +++ b/src/pkg/rpc/jsonrpc/server.go @@ -43,9 +43,9 @@ func NewServerCodec(conn io.ReadWriteCloser) rpc.ServerCodec { } type serverRequest struct { - Method string "method" - Params *json.RawMessage "params" - Id *json.RawMessage "id" + Method string `json:"method"` + Params *json.RawMessage `json:"params"` + Id *json.RawMessage `json:"id"` } func (r *serverRequest) reset() { @@ -59,9 +59,9 @@ func (r *serverRequest) reset() { } type serverResponse struct { - Id *json.RawMessage "id" - Result interface{} "result" - Error interface{} "error" + Id *json.RawMessage `json:"id"` + Result interface{} `json:"result"` + Error interface{} `json:"error"` } func (c *serverCodec) ReadRequestHeader(r *rpc.Request) os.Error { diff --git a/src/pkg/rpc/server.go b/src/pkg/rpc/server.go index acadeec37..07845d128 100644 --- a/src/pkg/rpc/server.go +++ b/src/pkg/rpc/server.go @@ -242,10 +242,10 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) os.E if s.typ.PkgPath() != "" && !isExported(sname) && !useName { s := "rpc Register: type " + sname + " is not exported" log.Print(s) - return os.ErrorString(s) + return os.NewError(s) } if _, present := server.serviceMap[sname]; present { - return os.ErrorString("rpc: service already defined: " + sname) + return os.NewError("rpc: service already defined: " + sname) } s.name = sname s.method = make(map[string]*methodType) @@ -294,7 +294,7 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) os.E if len(s.method) == 0 { s := "rpc Register: type " + sname + " has no exported methods of suitable type" log.Print(s) - return os.ErrorString(s) + return os.NewError(s) } server.serviceMap[s.name] = s return nil @@ -491,13 +491,13 @@ func (server *Server) readRequest(codec ServerCodec) (req *Request, service *ser if err == os.EOF || err == io.ErrUnexpectedEOF { return } - err = os.ErrorString("rpc: server cannot decode request: " + err.String()) + err = os.NewError("rpc: server cannot decode request: " + err.String()) return } - serviceMethod := strings.Split(req.ServiceMethod, ".", -1) + serviceMethod := strings.Split(req.ServiceMethod, ".") if len(serviceMethod) != 2 { - err = os.ErrorString("rpc: service/method request ill-formed: " + req.ServiceMethod) + err = os.NewError("rpc: service/method request ill-formed: " + req.ServiceMethod) return } // Look up the request. @@ -505,12 +505,12 @@ func (server *Server) readRequest(codec ServerCodec) (req *Request, service *ser service = server.serviceMap[serviceMethod[0]] server.Unlock() if service == nil { - err = os.ErrorString("rpc: can't find service " + req.ServiceMethod) + err = os.NewError("rpc: can't find service " + req.ServiceMethod) return } mtype = service.method[serviceMethod[1]] if mtype == nil { - err = os.ErrorString("rpc: can't find method " + req.ServiceMethod) + err = os.NewError("rpc: can't find method " + req.ServiceMethod) } return } diff --git a/src/pkg/rpc/server_test.go b/src/pkg/rpc/server_test.go index cfff0c9ad..1692168a8 100644 --- a/src/pkg/rpc/server_test.go +++ b/src/pkg/rpc/server_test.go @@ -52,7 +52,7 @@ func (t *Arith) Mul(args *Args, reply *Reply) os.Error { func (t *Arith) Div(args Args, reply *Reply) os.Error { if args.B == 0 { - return os.ErrorString("divide by zero") + return os.NewError("divide by zero") } reply.C = args.A / args.B return nil diff --git a/src/pkg/runtime/386/atomic.c b/src/pkg/runtime/386/atomic.c new file mode 100644 index 000000000..c031cc4f6 --- /dev/null +++ b/src/pkg/runtime/386/atomic.c @@ -0,0 +1,12 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "runtime.h" + +#pragma textflag 7 +uint32 +runtime·atomicload(uint32 volatile* addr) +{ + return *addr; +} diff --git a/src/pkg/runtime/Makefile b/src/pkg/runtime/Makefile index b122e0599..03f960cb8 100644 --- a/src/pkg/runtime/Makefile +++ b/src/pkg/runtime/Makefile @@ -12,9 +12,6 @@ SIZE_amd64=64 SIZE_arm=32 SIZE=$(SIZE_$(GOARCH)) -# TODO(kaib): fix register allocation to honor extern register so we -# can enable optimizations again. -CFLAGS_arm=-N CFLAGS_windows=-D__WINDOWS__ CFLAGS=-I$(GOOS) -I$(GOARCH) -I$(GOOS)/$(GOARCH) -wF $(CFLAGS_$(GOARCH)) $(CFLAGS_$(GOOS)) @@ -50,6 +47,7 @@ OFILES_arm=\ OFILES=\ asm.$O\ + atomic.$O\ cgocall.$O\ chan.$O\ closure.$O\ diff --git a/src/pkg/runtime/amd64/atomic.c b/src/pkg/runtime/amd64/atomic.c new file mode 100644 index 000000000..c031cc4f6 --- /dev/null +++ b/src/pkg/runtime/amd64/atomic.c @@ -0,0 +1,12 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "runtime.h" + +#pragma textflag 7 +uint32 +runtime·atomicload(uint32 volatile* addr) +{ + return *addr; +} diff --git a/src/pkg/runtime/arm/atomic.c b/src/pkg/runtime/arm/atomic.c new file mode 100644 index 000000000..9fd47bae7 --- /dev/null +++ b/src/pkg/runtime/arm/atomic.c @@ -0,0 +1,12 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "runtime.h" + +#pragma textflag 7 +uint32 +runtime·atomicload(uint32 volatile* addr) +{ + return runtime·xadd(addr, 0); +} diff --git a/src/pkg/runtime/cgo/Makefile b/src/pkg/runtime/cgo/Makefile index f26da2c51..7e752f127 100644 --- a/src/pkg/runtime/cgo/Makefile +++ b/src/pkg/runtime/cgo/Makefile @@ -10,6 +10,10 @@ ifeq ($(GOARCH),arm) ENABLED:=0 endif +ifeq ($(GOOS),plan9) +ENABLED:=0 +endif + ifeq ($(DISABLE_CGO),1) ENABLED:=0 endif diff --git a/src/pkg/runtime/cgo/darwin_386.c b/src/pkg/runtime/cgo/darwin_386.c index 13184f321..6d4e259be 100644 --- a/src/pkg/runtime/cgo/darwin_386.c +++ b/src/pkg/runtime/cgo/darwin_386.c @@ -113,11 +113,16 @@ libcgo_sys_thread_start(ThreadStart *ts) pthread_attr_t attr; pthread_t p; size_t size; + int err; pthread_attr_init(&attr); pthread_attr_getstacksize(&attr, &size); ts->g->stackguard = size; - pthread_create(&p, &attr, threadentry, ts); + err = pthread_create(&p, &attr, threadentry, ts); + if (err != 0) { + fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); + abort(); + } } static void* diff --git a/src/pkg/runtime/cgo/darwin_amd64.c b/src/pkg/runtime/cgo/darwin_amd64.c index 38cd80a6f..3471044c0 100644 --- a/src/pkg/runtime/cgo/darwin_amd64.c +++ b/src/pkg/runtime/cgo/darwin_amd64.c @@ -83,11 +83,16 @@ libcgo_sys_thread_start(ThreadStart *ts) pthread_attr_t attr; pthread_t p; size_t size; + int err; pthread_attr_init(&attr); pthread_attr_getstacksize(&attr, &size); ts->g->stackguard = size; - pthread_create(&p, &attr, threadentry, ts); + err = pthread_create(&p, &attr, threadentry, ts); + if (err != 0) { + fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); + abort(); + } } static void* diff --git a/src/pkg/runtime/cgo/freebsd_386.c b/src/pkg/runtime/cgo/freebsd_386.c index d08e1dee8..ae53201b4 100644 --- a/src/pkg/runtime/cgo/freebsd_386.c +++ b/src/pkg/runtime/cgo/freebsd_386.c @@ -20,11 +20,16 @@ libcgo_sys_thread_start(ThreadStart *ts) pthread_attr_t attr; pthread_t p; size_t size; + int err; pthread_attr_init(&attr); pthread_attr_getstacksize(&attr, &size); ts->g->stackguard = size; - pthread_create(&p, &attr, threadentry, ts); + err = pthread_create(&p, &attr, threadentry, ts); + if (err != 0) { + fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); + abort(); + } } static void* diff --git a/src/pkg/runtime/cgo/freebsd_amd64.c b/src/pkg/runtime/cgo/freebsd_amd64.c index fe6ce391f..5afc1dfea 100644 --- a/src/pkg/runtime/cgo/freebsd_amd64.c +++ b/src/pkg/runtime/cgo/freebsd_amd64.c @@ -20,11 +20,16 @@ libcgo_sys_thread_start(ThreadStart *ts) pthread_attr_t attr; pthread_t p; size_t size; + int err; pthread_attr_init(&attr); pthread_attr_getstacksize(&attr, &size); ts->g->stackguard = size; - pthread_create(&p, &attr, threadentry, ts); + err = pthread_create(&p, &attr, threadentry, ts); + if (err != 0) { + fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); + abort(); + } } static void* diff --git a/src/pkg/runtime/cgo/linux_386.c b/src/pkg/runtime/cgo/linux_386.c index 00322d4b7..e9df5ffdc 100644 --- a/src/pkg/runtime/cgo/linux_386.c +++ b/src/pkg/runtime/cgo/linux_386.c @@ -21,6 +21,7 @@ libcgo_sys_thread_start(ThreadStart *ts) pthread_attr_t attr; pthread_t p; size_t size; + int err; // Not sure why the memset is necessary here, // but without it, we get a bogus stack size @@ -30,7 +31,11 @@ libcgo_sys_thread_start(ThreadStart *ts) size = 0; pthread_attr_getstacksize(&attr, &size); ts->g->stackguard = size; - pthread_create(&p, &attr, threadentry, ts); + err = pthread_create(&p, &attr, threadentry, ts); + if (err != 0) { + fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); + abort(); + } } static void* diff --git a/src/pkg/runtime/cgo/linux_amd64.c b/src/pkg/runtime/cgo/linux_amd64.c index e77c5ddfe..d9b8b3706 100644 --- a/src/pkg/runtime/cgo/linux_amd64.c +++ b/src/pkg/runtime/cgo/linux_amd64.c @@ -20,11 +20,16 @@ libcgo_sys_thread_start(ThreadStart *ts) pthread_attr_t attr; pthread_t p; size_t size; + int err; pthread_attr_init(&attr); pthread_attr_getstacksize(&attr, &size); ts->g->stackguard = size; - pthread_create(&p, &attr, threadentry, ts); + err = pthread_create(&p, &attr, threadentry, ts); + if (err != 0) { + fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); + abort(); + } } static void* diff --git a/src/pkg/runtime/cgo/windows_amd64.c b/src/pkg/runtime/cgo/windows_amd64.c index dafe8cd9d..fd5b397ab 100755 --- a/src/pkg/runtime/cgo/windows_amd64.c +++ b/src/pkg/runtime/cgo/windows_amd64.c @@ -37,11 +37,21 @@ threadentry(void *v) ts.g->stackbase = (uintptr)&ts; /* - * libcgo_sys_thread_start set stackguard to stack size; - * change to actual guard pointer. - */ + * libcgo_sys_thread_start set stackguard to stack size; + * change to actual guard pointer. + */ ts.g->stackguard = (uintptr)&ts - ts.g->stackguard + 4096; - crosscall_386(ts.fn); + /* + * Set specific keys in thread local storage. + */ + asm volatile ( + "movq %%gs:0x58, %%rax\n" // MOVQ 0x58(GS), tmp + "movq %0, 0(%%rax)\n" // MOVQ g, 0(GS) + "movq %1, 8(%%rax)\n" // MOVQ m, 8(GS) + :: "r"(ts.g), "r"(ts.m) : "%rax" + ); + + crosscall_amd64(ts.fn); return nil; } diff --git a/src/pkg/runtime/debug/stack.go b/src/pkg/runtime/debug/stack.go index e5fae632b..a533a5c3b 100644 --- a/src/pkg/runtime/debug/stack.go +++ b/src/pkg/runtime/debug/stack.go @@ -52,7 +52,7 @@ func stack() []byte { if err != nil { continue } - lines = bytes.Split(data, []byte{'\n'}, -1) + lines = bytes.Split(data, []byte{'\n'}) lastFile = file } line-- // in stack trace, lines are 1-indexed but our array is 0-indexed diff --git a/src/pkg/runtime/debug/stack_test.go b/src/pkg/runtime/debug/stack_test.go index f4bdc4624..4aeea13ff 100644 --- a/src/pkg/runtime/debug/stack_test.go +++ b/src/pkg/runtime/debug/stack_test.go @@ -35,7 +35,7 @@ func (t T) method() []byte { */ func TestStack(t *testing.T) { b := T(0).method() - lines := strings.Split(string(b), "\n", -1) + lines := strings.Split(string(b), "\n") if len(lines) <= 6 { t.Fatal("too few lines") } diff --git a/src/pkg/runtime/error.go b/src/pkg/runtime/error.go index 289d78f49..6c37f888f 100644 --- a/src/pkg/runtime/error.go +++ b/src/pkg/runtime/error.go @@ -131,3 +131,8 @@ func printany(i interface{}) { print("(", typestring(i), ") ", i) } } + +// called from generated code +func panicwrap(pkg, typ, meth string) { + panic("value method " + pkg + "." + typ + "." + meth + " called using nil *" + typ + " pointer") +} diff --git a/src/pkg/runtime/linux/mem.c b/src/pkg/runtime/linux/mem.c index 38ca7e4a0..ad0fac6d3 100644 --- a/src/pkg/runtime/linux/mem.c +++ b/src/pkg/runtime/linux/mem.c @@ -91,6 +91,9 @@ runtime·SysMap(void *v, uintptr n) if(p != v && addrspace_free(v, n)) { // On some systems, mmap ignores v without // MAP_FIXED, so retry if the address space is free. + if(p > (void*)4096) { + runtime·munmap(p, n); + } p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); } if(p == (void*)ENOMEM) diff --git a/src/pkg/runtime/malloc.goc b/src/pkg/runtime/malloc.goc index c55be9772..49ab24df8 100644 --- a/src/pkg/runtime/malloc.goc +++ b/src/pkg/runtime/malloc.goc @@ -229,7 +229,7 @@ runtime·allocmcache(void) return c; } -int32 runtime·sizeof_C_MStats = sizeof(MStats); +uintptr runtime·sizeof_C_MStats = sizeof(MStats); #define MaxArena32 (2U<<30) diff --git a/src/pkg/runtime/mem.go b/src/pkg/runtime/mem.go index fe505a329..c3316d44c 100644 --- a/src/pkg/runtime/mem.go +++ b/src/pkg/runtime/mem.go @@ -52,7 +52,7 @@ type MemStatsType struct { } } -var sizeof_C_MStats int // filled in by malloc.goc +var sizeof_C_MStats uintptr // filled in by malloc.goc func init() { if sizeof_C_MStats != unsafe.Sizeof(MemStats) { diff --git a/src/pkg/runtime/mkasmh.sh b/src/pkg/runtime/mkasmh.sh index 91d1bbe5d..328e2d5ba 100755 --- a/src/pkg/runtime/mkasmh.sh +++ b/src/pkg/runtime/mkasmh.sh @@ -14,7 +14,6 @@ EOF case "$GOARCH" in 386) # The offsets 0 and 4 are also known to: - # nacl/thread.c:/^newosproc # ../../cmd/8l/pass.c:/D_GS # ../../libcgo/linux_386.c:/^threadentry # ../../libcgo/darwin_386.c:/^threadentry @@ -62,14 +61,23 @@ case "$GOARCH" in esac ;; amd64) - # The offsets 0 and 8 are known to: - # ../../cmd/6l/pass.c:/D_GS - # ../../libcgo/linux_amd64.c:/^threadentry - # ../../libcgo/darwin_amd64.c:/^threadentry - # - echo '#define get_tls(r)' - echo '#define g(r) 0(GS)' - echo '#define m(r) 8(GS)' + case "$GOOS" in + windows) + echo '#define get_tls(r) MOVQ 0x58(GS), r' + echo '#define g(r) 0(r)' + echo '#define m(r) 8(r)' + ;; + *) + # The offsets 0 and 8 are known to: + # ../../cmd/6l/pass.c:/D_GS + # ../../libcgo/linux_amd64.c:/^threadentry + # ../../libcgo/darwin_amd64.c:/^threadentry + # + echo '#define get_tls(r)' + echo '#define g(r) 0(GS)' + echo '#define m(r) 8(GS)' + ;; + esac ;; arm) echo '#define g R10' diff --git a/src/pkg/runtime/plan9/386/sys.s b/src/pkg/runtime/plan9/386/sys.s index f760b782f..1cb570b68 100644 --- a/src/pkg/runtime/plan9/386/sys.s +++ b/src/pkg/runtime/plan9/386/sys.s @@ -9,6 +9,11 @@ TEXT runtime·setldt(SB),7,$0 RET +TEXT runtime·open(SB),7,$0 + MOVL $14, AX + INT $64 + RET + TEXT runtime·write(SB),7,$0 MOVL $20, AX INT $64 diff --git a/src/pkg/runtime/plan9/os.h b/src/pkg/runtime/plan9/os.h index 9444acc98..b2f7357ec 100644 --- a/src/pkg/runtime/plan9/os.h +++ b/src/pkg/runtime/plan9/os.h @@ -6,6 +6,14 @@ extern int32 runtime·write(int32 fd, void* buffer, int32 nbytes); extern void runtime·exits(int8* msg); extern int32 runtime·brk_(void*); +/* open */ +enum +{ + OREAD = 0, + OWRITE = 1, + ORDWR = 2 +}; + /* rfork */ enum { @@ -22,6 +30,28 @@ enum RFREND = (1<<13), RFNOMNT = (1<<14) }; + +typedef struct Tos Tos; +typedef intptr Plink; + +struct Tos { + struct /* Per process profiling */ + { + Plink *pp; /* known to be 0(ptr) */ + Plink *next; /* known to be 4(ptr) */ + Plink *last; + Plink *first; + uint32 pid; + uint32 what; + } prof; + uint64 cyclefreq; /* cycle clock frequency if there is one, 0 otherwise */ + int64 kcycles; /* cycles spent in kernel */ + int64 pcycles; /* cycles spent in process (kernel + user) */ + uint32 pid; /* might as well put the pid here */ + uint32 clock; + /* top of stack is here */ +}; + extern int32 runtime·rfork(int32 flags, void *stk, M *m, G *g, void (*fn)(void)); extern int32 runtime·plan9_semacquire(uint32 *addr, int32 block); extern int32 runtime·plan9_semrelease(uint32 *addr, int32 count); diff --git a/src/pkg/runtime/plan9/thread.c b/src/pkg/runtime/plan9/thread.c index 7c6ca45a3..ef9a23e8e 100644 --- a/src/pkg/runtime/plan9/thread.c +++ b/src/pkg/runtime/plan9/thread.c @@ -27,24 +27,48 @@ runtime·initsig(int32 queue) { } +extern Tos *_tos; void runtime·exit(int32) { + int32 fd; + uint8 buf[128]; + uint8 tmp[16]; + uint8 *p, *q; + int32 pid; + + runtime·memclr(buf, sizeof buf); + runtime·memclr(tmp, sizeof tmp); + pid = _tos->pid; + + /* build path string /proc/pid/notepg */ + for(q=tmp; pid > 0;) { + *q++ = '0' + (pid%10); + pid = pid/10; + } + p = buf; + runtime·mcpy((void*)p, (void*)"/proc/", 6); + p += 6; + for(q--; q >= tmp;) + *p++ = *q--; + runtime·mcpy((void*)p, (void*)"/notepg", 7); + + /* post interrupt note */ + fd = runtime·open(buf, OWRITE); + runtime·write(fd, "interrupt", 9); runtime·exits(nil); } void runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)) { - USED(m, g, stk, fn); - m->tls[0] = m->id; // so 386 asm can find it if(0){ runtime·printf("newosproc stk=%p m=%p g=%p fn=%p rfork=%p id=%d/%d ostk=%p\n", stk, m, g, fn, runtime·rfork, m->id, m->tls[0], &m); } - if (runtime·rfork(RFPROC | RFMEM, stk, m, g, fn) < 0 ) + if (runtime·rfork(RFPROC|RFMEM|RFNOWAIT, stk, m, g, fn) < 0 ) runtime·throw("newosproc: rfork failed"); } diff --git a/src/pkg/runtime/pprof/pprof_test.go b/src/pkg/runtime/pprof/pprof_test.go index a060917a2..4486d5525 100644 --- a/src/pkg/runtime/pprof/pprof_test.go +++ b/src/pkg/runtime/pprof/pprof_test.go @@ -43,7 +43,7 @@ func TestCPUProfile(t *testing.T) { // Convert []byte to []uintptr. bytes := prof.Bytes() val := *(*[]uintptr)(unsafe.Pointer(&bytes)) - val = val[:len(bytes)/unsafe.Sizeof(uintptr(0))] + val = val[:len(bytes)/int(unsafe.Sizeof(uintptr(0)))] if len(val) < 10 { t.Fatalf("profile too short: %#x", val) diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c index c5af8b754..a8f3a796a 100644 --- a/src/pkg/runtime/proc.c +++ b/src/pkg/runtime/proc.c @@ -77,7 +77,7 @@ struct Sched { }; Sched runtime·sched; -int32 gomaxprocs; +int32 runtime·gomaxprocs; // An m that is waiting for notewakeup(&m->havenextg). This may be // only be accessed while the scheduler lock is held. This is used to diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h index f3ccff1bc..ad5da0a96 100644 --- a/src/pkg/runtime/runtime.h +++ b/src/pkg/runtime/runtime.h @@ -242,6 +242,11 @@ struct M uint32 fflag; // floating point compare flags #ifdef __WINDOWS__ void* sehframe; + +#ifdef _64BIT + void* gostack; +#endif + #endif }; @@ -416,7 +421,10 @@ int32 runtime·write(int32, void*, int32); int32 runtime·mincore(void*, uintptr, byte*); bool runtime·cas(uint32*, uint32, uint32); bool runtime·casp(void**, void*, void*); +// Don't confuse with XADD x86 instruction, +// this one is actually 'addx', that is, add-and-fetch. uint32 runtime·xadd(uint32 volatile*, int32); +uint32 runtime·atomicload(uint32 volatile*); void runtime·jmpdefer(byte*, void*); void runtime·exit1(int32); void runtime·ready(G*); diff --git a/src/pkg/runtime/sema.goc b/src/pkg/runtime/sema.goc index 1c77e87a5..ae84351ed 100644 --- a/src/pkg/runtime/sema.goc +++ b/src/pkg/runtime/sema.goc @@ -23,104 +23,68 @@ package runtime typedef struct Sema Sema; struct Sema { - uint32 *addr; + uint32 volatile *addr; G *g; Sema *prev; Sema *next; }; -// TODO: For now, a linked list; maybe a hash table of linked lists later. -static Sema *semfirst, *semlast; -static Lock semlock; +typedef struct SemaRoot SemaRoot; +struct SemaRoot +{ + Lock; + Sema *head; + Sema *tail; + // Number of waiters. Read w/o the lock. + uint32 volatile nwait; +}; + +// Prime to not correlate with any user patterns. +#define SEMTABLESZ 251 + +static union +{ + SemaRoot; + // Modern processors tend to have 64-byte cache lines, + // potentially with 128-byte effective cache line size for reading. + // While there are hypothetical architectures + // with 16-4096 byte cache lines, 128 looks like a good compromise. + uint8 pad[128]; +} semtable[SEMTABLESZ]; + +static SemaRoot* +semroot(uint32 *addr) +{ + return &semtable[((uintptr)addr >> 3) % SEMTABLESZ]; +} static void -semqueue(uint32 *addr, Sema *s) +semqueue(SemaRoot *root, uint32 volatile *addr, Sema *s) { + s->g = g; s->addr = addr; - s->g = nil; - - runtime·lock(&semlock); - s->prev = semlast; s->next = nil; - if(semlast) - semlast->next = s; + s->prev = root->tail; + if(root->tail) + root->tail->next = s; else - semfirst = s; - semlast = s; - runtime·unlock(&semlock); + root->head = s; + root->tail = s; } static void -semdequeue(Sema *s) +semdequeue(SemaRoot *root, Sema *s) { - runtime·lock(&semlock); if(s->next) s->next->prev = s->prev; else - semlast = s->prev; + root->tail = s->prev; if(s->prev) s->prev->next = s->next; else - semfirst = s->next; + root->head = s->next; s->prev = nil; s->next = nil; - runtime·unlock(&semlock); -} - -static void -semwakeup(uint32 *addr) -{ - Sema *s; - - runtime·lock(&semlock); - for(s=semfirst; s; s=s->next) { - if(s->addr == addr && s->g) { - runtime·ready(s->g); - s->g = nil; - break; - } - } - runtime·unlock(&semlock); -} - -// Step 1 of sleep: make ourselves available for wakeup. -// TODO(rsc): Maybe we can write a version without -// locks by using cas on s->g. Maybe not: I need to -// think more about whether it would be correct. -static void -semsleep1(Sema *s) -{ - runtime·lock(&semlock); - s->g = g; - runtime·unlock(&semlock); -} - -// Decided not to go through with it: undo step 1. -static void -semsleepundo1(Sema *s) -{ - runtime·lock(&semlock); - if(s->g != nil) { - s->g = nil; // back ourselves out - } else { - // If s->g == nil already, semwakeup - // already readied us. Since we never stopped - // running, readying us just set g->readyonstop. - // Clear it. - if(g->readyonstop == 0) - *(int32*)0x555 = 555; - g->readyonstop = 0; - } - runtime·unlock(&semlock); -} - -// Step 2: wait for the wakeup. -static void -semsleep2(Sema *s) -{ - USED(s); - g->status = Gwaiting; - runtime·gosched(); } static int32 @@ -128,52 +92,83 @@ cansemacquire(uint32 *addr) { uint32 v; - while((v = *addr) > 0) + while((v = runtime·atomicload(addr)) > 0) if(runtime·cas(addr, v, v-1)) return 1; return 0; } -// For now has no return value. -// Might return an ok (not interrupted) bool in the future? void -runtime·semacquire(uint32 *addr) +runtime·semacquire(uint32 volatile *addr) { Sema s; + SemaRoot *root; // Easy case. if(cansemacquire(addr)) return; // Harder case: - // queue - // try semacquire one more time, sleep if failed - // dequeue - // wake up one more guy to avoid races (TODO(rsc): maybe unnecessary?) - semqueue(addr, &s); + // increment waiter count + // try cansemacquire one more time, return if succeeded + // enqueue itself as a waiter + // sleep + // (waiter descriptor is dequeued by signaler) + root = semroot(addr); for(;;) { - semsleep1(&s); + runtime·lock(root); + // Add ourselves to nwait to disable "easy case" in semrelease. + runtime·xadd(&root->nwait, 1); + // Check cansemacquire to avoid missed wakeup. if(cansemacquire(addr)) { - semsleepundo1(&s); - break; + runtime·xadd(&root->nwait, -1); + runtime·unlock(root); + return; } - semsleep2(&s); + // Any semrelease after the cansemacquire knows we're waiting + // (we set nwait above), so go to sleep. + semqueue(root, addr, &s); + g->status = Gwaiting; + runtime·unlock(root); + runtime·gosched(); + if(cansemacquire(addr)) + return; } - semdequeue(&s); - semwakeup(addr); } void -runtime·semrelease(uint32 *addr) +runtime·semrelease(uint32 volatile *addr) { - uint32 v; + Sema *s; + SemaRoot *root; - for(;;) { - v = *addr; - if(runtime·cas(addr, v, v+1)) + root = semroot(addr); + runtime·xadd(addr, 1); + + // Easy case: no waiters? + // This check must happen after the xadd, to avoid a missed wakeup + // (see loop in semacquire). + if(runtime·atomicload(&root->nwait) == 0) + return; + + // Harder case: search for a waiter and wake it. + runtime·lock(root); + if(runtime·atomicload(&root->nwait) == 0) { + // The count is already consumed by another goroutine, + // so no need to wake up another goroutine. + runtime·unlock(root); + return; + } + for(s = root->head; s; s = s->next) { + if(s->addr == addr) { + runtime·xadd(&root->nwait, -1); + semdequeue(root, s); break; + } } - semwakeup(addr); + runtime·unlock(root); + if(s) + runtime·ready(s->g); } func Semacquire(addr *uint32) { diff --git a/src/pkg/runtime/sema_test.go b/src/pkg/runtime/sema_test.go new file mode 100644 index 000000000..d95bb1ec5 --- /dev/null +++ b/src/pkg/runtime/sema_test.go @@ -0,0 +1,100 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime_test + +import ( + "runtime" + "sync/atomic" + "testing" +) + +func BenchmarkSemaUncontended(b *testing.B) { + type PaddedSem struct { + sem uint32 + pad [32]uint32 + } + const CallsPerSched = 1000 + procs := runtime.GOMAXPROCS(-1) + N := int32(b.N / CallsPerSched) + c := make(chan bool, procs) + for p := 0; p < procs; p++ { + go func() { + sem := new(PaddedSem) + for atomic.AddInt32(&N, -1) >= 0 { + runtime.Gosched() + for g := 0; g < CallsPerSched; g++ { + runtime.Semrelease(&sem.sem) + runtime.Semacquire(&sem.sem) + } + } + c <- true + }() + } + for p := 0; p < procs; p++ { + <-c + } +} + +func benchmarkSema(b *testing.B, block, work bool) { + const CallsPerSched = 1000 + const LocalWork = 100 + procs := runtime.GOMAXPROCS(-1) + N := int32(b.N / CallsPerSched) + c := make(chan bool, procs) + c2 := make(chan bool, procs/2) + sem := uint32(0) + if block { + for p := 0; p < procs/2; p++ { + go func() { + runtime.Semacquire(&sem) + c2 <- true + }() + } + } + for p := 0; p < procs; p++ { + go func() { + foo := 0 + for atomic.AddInt32(&N, -1) >= 0 { + runtime.Gosched() + for g := 0; g < CallsPerSched; g++ { + runtime.Semrelease(&sem) + if work { + for i := 0; i < LocalWork; i++ { + foo *= 2 + foo /= 2 + } + } + runtime.Semacquire(&sem) + } + } + c <- foo == 42 + runtime.Semrelease(&sem) + }() + } + if block { + for p := 0; p < procs/2; p++ { + <-c2 + } + } + for p := 0; p < procs; p++ { + <-c + } +} + +func BenchmarkSemaSyntNonblock(b *testing.B) { + benchmarkSema(b, false, false) +} + +func BenchmarkSemaSyntBlock(b *testing.B) { + benchmarkSema(b, true, false) +} + +func BenchmarkSemaWorkNonblock(b *testing.B) { + benchmarkSema(b, false, true) +} + +func BenchmarkSemaWorkBlock(b *testing.B) { + benchmarkSema(b, true, true) +} diff --git a/src/pkg/runtime/windows/amd64/defs.h b/src/pkg/runtime/windows/amd64/defs.h new file mode 100644 index 000000000..830c6a855 --- /dev/null +++ b/src/pkg/runtime/windows/amd64/defs.h @@ -0,0 +1,40 @@ +// g:\opensource\go\bin\godefs.exe -f -m64 defs.c + +// MACHINE GENERATED - DO NOT EDIT. + +// Constants +enum { + PROT_NONE = 0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_EXEC = 0x4, + MAP_ANON = 0x1, + MAP_PRIVATE = 0x2, + SIGINT = 0x2, + CTRL_C_EVENT = 0, + CTRL_BREAK_EVENT = 0x1, + EXCEPTION_ACCESS_VIOLATION = 0xc0000005, + EXCEPTION_BREAKPOINT = 0x80000003, + EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d, + EXCEPTION_FLT_DIVIDE_BY_ZERO = 0xc000008e, + EXCEPTION_FLT_INEXACT_RESULT = 0xc000008f, + EXCEPTION_FLT_OVERFLOW = 0xc0000091, + EXCEPTION_FLT_UNDERFLOW = 0xc0000093, + EXCEPTION_INT_DIVIDE_BY_ZERO = 0xc0000094, + EXCEPTION_INT_OVERFLOW = 0xc0000095, +}; + +// Types +#pragma pack on + +typedef struct ExceptionRecord ExceptionRecord; +struct ExceptionRecord { + uint32 ExceptionCode; + uint32 ExceptionFlags; + ExceptionRecord *ExceptionRecord; + void *ExceptionAddress; + uint32 NumberParameters; + byte pad_godefs_0[4]; + uint64 ExceptionInformation[15]; +}; +#pragma pack off diff --git a/src/pkg/runtime/windows/amd64/rt0.s b/src/pkg/runtime/windows/amd64/rt0.s new file mode 100644 index 000000000..e54e7edeb --- /dev/null +++ b/src/pkg/runtime/windows/amd64/rt0.s @@ -0,0 +1,10 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "amd64/asm.h" + +TEXT _rt0_amd64_windows(SB),7,$-8 + MOVQ $_rt0_amd64(SB), AX + MOVQ SP, DI + JMP AX diff --git a/src/pkg/runtime/windows/amd64/signal.c b/src/pkg/runtime/windows/amd64/signal.c new file mode 100644 index 000000000..1fc3eb060 --- /dev/null +++ b/src/pkg/runtime/windows/amd64/signal.c @@ -0,0 +1,20 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "runtime.h" +#include "defs.h" +#include "os.h" + +void +runtime·initsig(int32 queue) +{ +} + +void +runtime·resetcpuprofiler(int32 hz) +{ + // TODO: Enable profiling interrupts. + + m->profilehz = hz; +} diff --git a/src/pkg/runtime/windows/amd64/sys.s b/src/pkg/runtime/windows/amd64/sys.s new file mode 100644 index 000000000..b1eacfc82 --- /dev/null +++ b/src/pkg/runtime/windows/amd64/sys.s @@ -0,0 +1,129 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "amd64/asm.h" + +// void *stdcall_raw(void *fn, uintptr nargs, void *args) +TEXT runtime·stdcall_raw(SB),7,$8 + MOVQ fn+0(FP), AX + MOVQ nargs+8(FP), CX + MOVQ args+16(FP), R11 + + // Switch to m->g0 if needed. + get_tls(DI) + MOVQ m(DI), DX + MOVQ g(DI), SI + MOVQ SI, 0(SP) // save g + MOVQ SP, m_gostack(DX) // save SP + MOVQ m_g0(DX), SI + CMPQ g(DI), SI + JEQ 3(PC) + MOVQ (g_sched+gobuf_sp)(SI), SP + MOVQ SI, g(DI) + + SUBQ $0x60, SP + + // Copy args to new stack. + MOVQ SP, DI + MOVQ R11, SI + CLD + REP; MOVSQ + MOVQ 0(R11), CX + MOVQ 8(R11), DX + MOVQ 16(R11), R8 + MOVQ 24(R11), R9 + + // Call stdcall function. + CALL AX + + // Restore original SP, g. + get_tls(DI) + MOVQ m(DI), DX + MOVQ m_gostack(DX), SP // restore SP + MOVQ 0(SP), SI // restore g + MOVQ SI, g(DI) + + RET + +// faster get/set last error +TEXT runtime·getlasterror(SB),7,$0 + MOVQ 0x30(GS), AX + MOVL 0x68(AX), AX + RET + +TEXT runtime·setlasterror(SB),7,$0 + MOVL err+0(FP), AX + MOVQ 0x30(GS), CX + MOVL AX, 0x68(CX) + RET + +// Windows runs the ctrl handler in a new thread. +TEXT runtime·ctrlhandler(SB),7,$0 + // TODO + RET + +TEXT runtime·callbackasm(SB),7,$0 + // TODO + RET + +// void tstart(M *newm); +TEXT runtime·tstart(SB),7,$0 + MOVQ newm+8(SP), CX // m + MOVQ m_g0(CX), DX // g + + MOVQ SP, DI // remember stack + + // Layout new m scheduler stack on os stack. + MOVQ SP, AX + MOVQ AX, g_stackbase(DX) + SUBQ $(64*1024), AX // stack size + MOVQ AX, g_stackguard(DX) + + // Set up tls. + LEAQ m_tls(CX), SI + MOVQ SI, 0x58(GS) + MOVQ CX, m(SI) + MOVQ DX, g(SI) + + // Someday the convention will be D is always cleared. + CLD + + PUSHQ DI // original stack + + CALL runtime·stackcheck(SB) // clobbers AX,CX + + CALL runtime·mstart(SB) + + POPQ DI // original stack + MOVQ DI, SP + + RET + +// uint32 tstart_stdcall(M *newm); +TEXT runtime·tstart_stdcall(SB),7,$0 + MOVQ CX, BX // stdcall first arg in RCX + + PUSHQ BX + CALL runtime·tstart+0(SB) + POPQ BX + + // Adjust stack for stdcall to return properly. + MOVQ (SP), AX // save return address + ADDQ $8, SP // remove single parameter + MOVQ AX, (SP) // restore return address + + XORL AX, AX // return 0 == success + + RET + +TEXT runtime·notok(SB),7,$0 + MOVQ $0xf1, BP + MOVQ BP, (BP) + RET + +// set tls base to DI +TEXT runtime·settls(SB),7,$0 + MOVQ DI, 0x58(GS) + RET + diff --git a/src/pkg/runtime/windows/mem.c b/src/pkg/runtime/windows/mem.c index 54d77da37..5d2291fa3 100644 --- a/src/pkg/runtime/windows/mem.c +++ b/src/pkg/runtime/windows/mem.c @@ -24,7 +24,7 @@ void* runtime·SysAlloc(uintptr n) { mstats.sys += n; - return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, (uintptr)(MEM_COMMIT|MEM_RESERVE), (uintptr)PAGE_EXECUTE_READWRITE); } void @@ -40,7 +40,7 @@ runtime·SysFree(void *v, uintptr n) uintptr r; mstats.sys -= n; - r = (uintptr)runtime·stdcall(runtime·VirtualFree, 3, v, 0, MEM_RELEASE); + r = (uintptr)runtime·stdcall(runtime·VirtualFree, 3, v, (uintptr)0, (uintptr)MEM_RELEASE); if(r == 0) runtime·throw("runtime: failed to release pages"); } @@ -50,12 +50,12 @@ runtime·SysReserve(void *v, uintptr n) { // v is just a hint. // First try at v. - v = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, MEM_RESERVE, PAGE_EXECUTE_READWRITE); + v = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_RESERVE, (uintptr)PAGE_EXECUTE_READWRITE); if(v != nil) return v; // Next let the kernel choose the address. - return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, MEM_RESERVE, PAGE_EXECUTE_READWRITE); + return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, (uintptr)MEM_RESERVE, (uintptr)PAGE_EXECUTE_READWRITE); } void @@ -64,7 +64,7 @@ runtime·SysMap(void *v, uintptr n) void *p; mstats.sys += n; - p = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + p = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_COMMIT, (uintptr)PAGE_EXECUTE_READWRITE); if(p != v) runtime·throw("runtime: cannot map pages in arena address space"); } diff --git a/src/pkg/runtime/windows/os.h b/src/pkg/runtime/windows/os.h index 77881e86e..bc9678733 100644 --- a/src/pkg/runtime/windows/os.h +++ b/src/pkg/runtime/windows/os.h @@ -7,6 +7,9 @@ extern void *runtime·GetProcAddress; // Call a Windows function with stdcall conventions, // and switch to os stack during the call. +#pragma varargck countpos runtime·stdcall 2 +#pragma varargck type runtime·stdcall void* +#pragma varargck type runtime·stdcall uintptr void *runtime·stdcall_raw(void *fn, uintptr nargs, void *args); void *runtime·stdcall(void *fn, int32 count, ...); uintptr runtime·syscall(void *fn, uintptr nargs, void *args, uintptr *err); diff --git a/src/pkg/runtime/windows/thread.c b/src/pkg/runtime/windows/thread.c index 81ad68033..5644fd5dd 100644 --- a/src/pkg/runtime/windows/thread.c +++ b/src/pkg/runtime/windows/thread.c @@ -45,7 +45,7 @@ void runtime·osinit(void) { runtime·stdcall(runtime·QueryPerformanceFrequency, 1, &timerfreq); - runtime·stdcall(runtime·SetConsoleCtrlHandler, 2, runtime·ctrlhandler, 1); + runtime·stdcall(runtime·SetConsoleCtrlHandler, 2, runtime·ctrlhandler, (uintptr)1); } void @@ -81,7 +81,7 @@ runtime·goenvs(void) void runtime·exit(int32 code) { - runtime·stdcall(runtime·ExitProcess, 1, code); + runtime·stdcall(runtime·ExitProcess, 1, (uintptr)code); } int32 @@ -93,15 +93,15 @@ runtime·write(int32 fd, void *buf, int32 n) written = 0; switch(fd) { case 1: - handle = runtime·stdcall(runtime·GetStdHandle, 1, -11); + handle = runtime·stdcall(runtime·GetStdHandle, 1, (uintptr)-11); break; case 2: - handle = runtime·stdcall(runtime·GetStdHandle, 1, -12); + handle = runtime·stdcall(runtime·GetStdHandle, 1, (uintptr)-12); break; default: return -1; } - runtime·stdcall(runtime·WriteFile, 5, handle, buf, n, &written, 0); + runtime·stdcall(runtime·WriteFile, 5, handle, buf, (uintptr)n, &written, (uintptr)0); return written; } @@ -111,7 +111,7 @@ initevent(void **pevent) { void *event; - event = runtime·stdcall(runtime·CreateEvent, 4, 0, 0, 0, 0); + event = runtime·stdcall(runtime·CreateEvent, 4, (uintptr)0, (uintptr)0, (uintptr)0, (uintptr)0); if(!runtime·casp(pevent, 0, event)) { // Someone else filled it in. Use theirs. runtime·stdcall(runtime·CloseHandle, 1, event); @@ -126,7 +126,7 @@ eventlock(Lock *l) initevent(&l->event); if(runtime·xadd(&l->key, 1) > 1) // someone else has it; wait - runtime·stdcall(runtime·WaitForSingleObject, 2, l->event, -1); + runtime·stdcall(runtime·WaitForSingleObject, 2, l->event, (uintptr)-1); } static void @@ -190,7 +190,7 @@ runtime·newosproc(M *m, G *g, void *stk, void (*fn)(void)) USED(g); // assuming g = m->g0 USED(fn); // assuming fn = mstart - thandle = runtime·stdcall(runtime·CreateThread, 6, 0, 0, runtime·tstart_stdcall, m, 0, 0); + thandle = runtime·stdcall(runtime·CreateThread, 6, (uintptr)0, (uintptr)0, runtime·tstart_stdcall, m, (uintptr)0, (uintptr)0); if(thandle == 0) { runtime·printf("runtime: failed to create new OS thread (have %d already; errno=%d)\n", runtime·mcount(), runtime·getlasterror()); runtime·throw("runtime.newosproc"); @@ -219,7 +219,7 @@ runtime·gettime(int64 *sec, int32 *usec) void * runtime·stdcall(void *fn, int32 count, ...) { - return runtime·stdcall_raw(fn, count, (uintptr*)(&count + 1)); + return runtime·stdcall_raw(fn, count, (uintptr*)&count + 1); } uintptr diff --git a/src/pkg/smtp/smtp.go b/src/pkg/smtp/smtp.go index d716df56b..2d5e86247 100644 --- a/src/pkg/smtp/smtp.go +++ b/src/pkg/smtp/smtp.go @@ -93,11 +93,11 @@ func (c *Client) ehlo() os.Error { return err } ext := make(map[string]string) - extList := strings.Split(msg, "\n", -1) + extList := strings.Split(msg, "\n") if len(extList) > 1 { extList = extList[1:] for _, line := range extList { - args := strings.Split(line, " ", 2) + args := strings.SplitN(line, " ", 2) if len(args) > 1 { ext[args[0]] = args[1] } else { @@ -106,7 +106,7 @@ func (c *Client) ehlo() os.Error { } } if mechs, ok := ext["AUTH"]; ok { - c.auth = strings.Split(mechs, " ", -1) + c.auth = strings.Split(mechs, " ") } c.ext = ext return err diff --git a/src/pkg/smtp/smtp_test.go b/src/pkg/smtp/smtp_test.go index 49363adf0..c053557d7 100644 --- a/src/pkg/smtp/smtp_test.go +++ b/src/pkg/smtp/smtp_test.go @@ -64,8 +64,8 @@ func (f faker) Close() os.Error { } func TestBasic(t *testing.T) { - basicServer = strings.Join(strings.Split(basicServer, "\n", -1), "\r\n") - basicClient = strings.Join(strings.Split(basicClient, "\n", -1), "\r\n") + basicServer = strings.Join(strings.Split(basicServer, "\n"), "\r\n") + basicClient = strings.Join(strings.Split(basicClient, "\n"), "\r\n") var cmdbuf bytes.Buffer bcmdbuf := bufio.NewWriter(&cmdbuf) diff --git a/src/pkg/sort/search.go b/src/pkg/sort/search.go index 6828e19b6..7d468da8a 100644 --- a/src/pkg/sort/search.go +++ b/src/pkg/sort/search.go @@ -15,7 +15,7 @@ package sort // Search calls f(i) only for i in the range [0, n). // // A common use of Search is to find the index i for a value x in -// a sorted, indexable data structure like an array or slice. +// a sorted, indexable data structure such as an array or slice. // In this case, the argument f, typically a closure, captures the value // to be searched for, and how the data structure is indexed and // ordered. @@ -75,7 +75,7 @@ func Search(n int, f func(int) bool) int { // Convenience wrappers for common cases. // SearchInts searches for x in a sorted slice of ints and returns the index -// as specified by Search. The array must be sorted in ascending order. +// as specified by Search. The slice must be sorted in ascending order. // func SearchInts(a []int, x int) int { return Search(len(a), func(i int) bool { return a[i] >= x }) @@ -83,15 +83,15 @@ func SearchInts(a []int, x int) int { // SearchFloat64s searches for x in a sorted slice of float64s and returns the index -// as specified by Search. The array must be sorted in ascending order. +// as specified by Search. The slice must be sorted in ascending order. // func SearchFloat64s(a []float64, x float64) int { return Search(len(a), func(i int) bool { return a[i] >= x }) } -// SearchStrings searches for x in a sorted slice of strings and returns the index -// as specified by Search. The array must be sorted in ascending order. +// SearchStrings searches for x slice a sorted slice of strings and returns the index +// as specified by Search. The slice must be sorted in ascending order. // func SearchStrings(a []string, x string) int { return Search(len(a), func(i int) bool { return a[i] >= x }) @@ -99,12 +99,12 @@ func SearchStrings(a []string, x string) int { // Search returns the result of applying SearchInts to the receiver and x. -func (p IntArray) Search(x int) int { return SearchInts(p, x) } +func (p IntSlice) Search(x int) int { return SearchInts(p, x) } // Search returns the result of applying SearchFloat64s to the receiver and x. -func (p Float64Array) Search(x float64) int { return SearchFloat64s(p, x) } +func (p Float64Slice) Search(x float64) int { return SearchFloat64s(p, x) } // Search returns the result of applying SearchStrings to the receiver and x. -func (p StringArray) Search(x string) int { return SearchStrings(p, x) } +func (p StringSlice) Search(x string) int { return SearchStrings(p, x) } diff --git a/src/pkg/sort/search_test.go b/src/pkg/sort/search_test.go index 939f66af3..2a9a85854 100644 --- a/src/pkg/sort/search_test.go +++ b/src/pkg/sort/search_test.go @@ -107,9 +107,9 @@ var wrappertests = []struct { {"SearchInts", SearchInts(data, 11), 8}, {"SearchFloat64s", SearchFloat64s(fdata, 2.1), 4}, {"SearchStrings", SearchStrings(sdata, ""), 0}, - {"IntArray.Search", IntArray(data).Search(0), 2}, - {"Float64Array.Search", Float64Array(fdata).Search(2.0), 3}, - {"StringArray.Search", StringArray(sdata).Search("x"), 3}, + {"IntSlice.Search", IntSlice(data).Search(0), 2}, + {"Float64Slice.Search", Float64Slice(fdata).Search(2.0), 3}, + {"StringSlice.Search", StringSlice(sdata).Search("x"), 3}, } diff --git a/src/pkg/sort/sort.go b/src/pkg/sort/sort.go index 30b1819af..daed61ea8 100644 --- a/src/pkg/sort/sort.go +++ b/src/pkg/sort/sort.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package sort provides primitives for sorting arrays and user-defined +// Package sort provides primitives for sorting slices and user-defined // collections. package sort @@ -82,7 +82,7 @@ func doPivot(data Interface, lo, hi int) (midlo, midhi int) { // data[d <= i < hi] = pivot // // Once b meets c, can swap the "= pivot" sections - // into the middle of the array. + // into the middle of the slice. pivot := lo a, b, c, d := lo+1, lo+1, hi, hi for b < c { @@ -155,52 +155,52 @@ func IsSorted(data Interface) bool { // Convenience types for common cases -// IntArray attaches the methods of Interface to []int, sorting in increasing order. -type IntArray []int +// IntSlice attaches the methods of Interface to []int, sorting in increasing order. +type IntSlice []int -func (p IntArray) Len() int { return len(p) } -func (p IntArray) Less(i, j int) bool { return p[i] < p[j] } -func (p IntArray) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (p IntSlice) Len() int { return len(p) } +func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] } +func (p IntSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } // Sort is a convenience method. -func (p IntArray) Sort() { Sort(p) } +func (p IntSlice) Sort() { Sort(p) } -// Float64Array attaches the methods of Interface to []float64, sorting in increasing order. -type Float64Array []float64 +// Float64Slice attaches the methods of Interface to []float64, sorting in increasing order. +type Float64Slice []float64 -func (p Float64Array) Len() int { return len(p) } -func (p Float64Array) Less(i, j int) bool { return p[i] < p[j] } -func (p Float64Array) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (p Float64Slice) Len() int { return len(p) } +func (p Float64Slice) Less(i, j int) bool { return p[i] < p[j] } +func (p Float64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } // Sort is a convenience method. -func (p Float64Array) Sort() { Sort(p) } +func (p Float64Slice) Sort() { Sort(p) } -// StringArray attaches the methods of Interface to []string, sorting in increasing order. -type StringArray []string +// StringSlice attaches the methods of Interface to []string, sorting in increasing order. +type StringSlice []string -func (p StringArray) Len() int { return len(p) } -func (p StringArray) Less(i, j int) bool { return p[i] < p[j] } -func (p StringArray) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (p StringSlice) Len() int { return len(p) } +func (p StringSlice) Less(i, j int) bool { return p[i] < p[j] } +func (p StringSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } // Sort is a convenience method. -func (p StringArray) Sort() { Sort(p) } +func (p StringSlice) Sort() { Sort(p) } // Convenience wrappers for common cases -// SortInts sorts an array of ints in increasing order. -func SortInts(a []int) { Sort(IntArray(a)) } -// SortFloat64s sorts an array of float64s in increasing order. -func SortFloat64s(a []float64) { Sort(Float64Array(a)) } -// SortStrings sorts an array of strings in increasing order. -func SortStrings(a []string) { Sort(StringArray(a)) } +// Ints sorts a slice of ints in increasing order. +func Ints(a []int) { Sort(IntSlice(a)) } +// Float64s sorts a slice of float64s in increasing order. +func Float64s(a []float64) { Sort(Float64Slice(a)) } +// Strings sorts a slice of strings in increasing order. +func Strings(a []string) { Sort(StringSlice(a)) } -// IntsAreSorted tests whether an array of ints is sorted in increasing order. -func IntsAreSorted(a []int) bool { return IsSorted(IntArray(a)) } -// Float64sAreSorted tests whether an array of float64s is sorted in increasing order. -func Float64sAreSorted(a []float64) bool { return IsSorted(Float64Array(a)) } -// StringsAreSorted tests whether an array of strings is sorted in increasing order. -func StringsAreSorted(a []string) bool { return IsSorted(StringArray(a)) } +// IntsAreSorted tests whether a slice of ints is sorted in increasing order. +func IntsAreSorted(a []int) bool { return IsSorted(IntSlice(a)) } +// Float64sAreSorted tests whether a slice of float64s is sorted in increasing order. +func Float64sAreSorted(a []float64) bool { return IsSorted(Float64Slice(a)) } +// StringsAreSorted tests whether a slice of strings is sorted in increasing order. +func StringsAreSorted(a []string) bool { return IsSorted(StringSlice(a)) } diff --git a/src/pkg/sort/sort_test.go b/src/pkg/sort/sort_test.go index 3d7337fd0..4da262637 100644 --- a/src/pkg/sort/sort_test.go +++ b/src/pkg/sort/sort_test.go @@ -16,9 +16,9 @@ var ints = [...]int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, var float64s = [...]float64{74.3, 59.0, 238.2, -784.0, 2.3, 9845.768, -959.7485, 905, 7.8, 7.8} var strings = [...]string{"", "Hello", "foo", "bar", "foo", "f00", "%*&^*&^&", "***"} -func TestSortIntArray(t *testing.T) { +func TestSortIntSlice(t *testing.T) { data := ints - a := IntArray(data[0:]) + a := IntSlice(data[0:]) Sort(a) if !IsSorted(a) { t.Errorf("sorted %v", ints) @@ -26,9 +26,9 @@ func TestSortIntArray(t *testing.T) { } } -func TestSortFloat64Array(t *testing.T) { +func TestSortFloat64Slice(t *testing.T) { data := float64s - a := Float64Array(data[0:]) + a := Float64Slice(data[0:]) Sort(a) if !IsSorted(a) { t.Errorf("sorted %v", float64s) @@ -36,9 +36,9 @@ func TestSortFloat64Array(t *testing.T) { } } -func TestSortStringArray(t *testing.T) { +func TestSortStringSlice(t *testing.T) { data := strings - a := StringArray(data[0:]) + a := StringSlice(data[0:]) Sort(a) if !IsSorted(a) { t.Errorf("sorted %v", strings) @@ -46,27 +46,27 @@ func TestSortStringArray(t *testing.T) { } } -func TestSortInts(t *testing.T) { +func TestInts(t *testing.T) { data := ints - SortInts(data[0:]) + Ints(data[0:]) if !IntsAreSorted(data[0:]) { t.Errorf("sorted %v", ints) t.Errorf(" got %v", data) } } -func TestSortFloat64s(t *testing.T) { +func TestFloat64s(t *testing.T) { data := float64s - SortFloat64s(data[0:]) + Float64s(data[0:]) if !Float64sAreSorted(data[0:]) { t.Errorf("sorted %v", float64s) t.Errorf(" got %v", data) } } -func TestSortStrings(t *testing.T) { +func TestStrings(t *testing.T) { data := strings - SortStrings(data[0:]) + Strings(data[0:]) if !StringsAreSorted(data[0:]) { t.Errorf("sorted %v", strings) t.Errorf(" got %v", data) @@ -85,7 +85,7 @@ func TestSortLarge_Random(t *testing.T) { if IntsAreSorted(data) { t.Fatalf("terrible rand.rand") } - SortInts(data) + Ints(data) if !IntsAreSorted(data) { t.Errorf("sort didn't sort - 1M ints") } @@ -99,7 +99,7 @@ func BenchmarkSortString1K(b *testing.B) { data[i] = strconv.Itoa(i ^ 0x2cc) } b.StartTimer() - SortStrings(data) + Strings(data) b.StopTimer() } } @@ -112,7 +112,7 @@ func BenchmarkSortInt1K(b *testing.B) { data[i] = i ^ 0x2cc } b.StartTimer() - SortInts(data) + Ints(data) b.StopTimer() } } @@ -125,7 +125,7 @@ func BenchmarkSortInt64K(b *testing.B) { data[i] = i ^ 0xcccc } b.StartTimer() - SortInts(data) + Ints(data) b.StopTimer() } } @@ -161,7 +161,7 @@ func (d *testingData) Len() int { return len(d.data) } func (d *testingData) Less(i, j int) bool { return d.data[i] < d.data[j] } func (d *testingData) Swap(i, j int) { if d.nswap >= d.maxswap { - d.t.Errorf("%s: used %d swaps sorting array of %d", d.desc, d.nswap, len(d.data)) + d.t.Errorf("%s: used %d swaps sorting slice of %d", d.desc, d.nswap, len(d.data)) d.t.FailNow() } d.nswap++ @@ -241,9 +241,9 @@ func TestBentleyMcIlroy(t *testing.T) { for i := 0; i < n; i++ { mdata[i] = data[i] } - // SortInts is known to be correct + // Ints is known to be correct // because mode Sort runs after mode _Copy. - SortInts(mdata) + Ints(mdata) case _Dither: for i := 0; i < n; i++ { mdata[i] = data[i] + i%5 @@ -255,13 +255,13 @@ func TestBentleyMcIlroy(t *testing.T) { Sort(d) // If we were testing C qsort, we'd have to make a copy - // of the array and sort it ourselves and then compare + // of the slice and sort it ourselves and then compare // x against it, to ensure that qsort was only permuting // the data, not (for example) overwriting it with zeros. // // In go, we don't have to be so paranoid: since the only // mutating method Sort can call is TestingData.swap, - // it suffices here just to check that the final array is sorted. + // it suffices here just to check that the final slice is sorted. if !IntsAreSorted(mdata) { t.Errorf("%s: ints not sorted", desc) t.Errorf("\t%v", mdata) diff --git a/src/pkg/strconv/atoi.go b/src/pkg/strconv/atoi.go index f7b845672..e1154782b 100644 --- a/src/pkg/strconv/atoi.go +++ b/src/pkg/strconv/atoi.go @@ -42,6 +42,8 @@ func cutoff64(base int) uint64 { // digits, err.Error = os.EINVAL; if the value corresponding // to s cannot be represented by a uint64, err.Error = os.ERANGE. func Btoui64(s string, b int) (n uint64, err os.Error) { + var cutoff uint64 + s0 := s switch { case len(s) < 1: @@ -68,12 +70,12 @@ func Btoui64(s string, b int) (n uint64, err os.Error) { } default: - err = os.ErrorString("invalid base " + Itoa(b)) + err = os.NewError("invalid base " + Itoa(b)) goto Error } n = 0 - cutoff := cutoff64(b) + cutoff = cutoff64(b) for i := 0; i < len(s); i++ { var v byte diff --git a/src/pkg/strconv/fp_test.go b/src/pkg/strconv/fp_test.go index 34baeee39..3096957f5 100644 --- a/src/pkg/strconv/fp_test.go +++ b/src/pkg/strconv/fp_test.go @@ -28,7 +28,7 @@ func pow2(i int) float64 { // Wrapper around strconv.Atof64. Handles dddddp+ddd (binary exponent) // itself, passes the rest on to strconv.Atof64. func myatof64(s string) (f float64, ok bool) { - a := strings.Split(s, "p", 2) + a := strings.SplitN(s, "p", 2) if len(a) == 2 { n, err := strconv.Atoi64(a[0]) if err != nil { @@ -72,7 +72,7 @@ func myatof64(s string) (f float64, ok bool) { // Wrapper around strconv.Atof32. Handles dddddp+ddd (binary exponent) // itself, passes the rest on to strconv.Atof32. func myatof32(s string) (f float32, ok bool) { - a := strings.Split(s, "p", 2) + a := strings.SplitN(s, "p", 2) if len(a) == 2 { n, err := strconv.Atoi(a[0]) if err != nil { @@ -116,7 +116,7 @@ func TestFp(t *testing.T) { if len(line) == 0 || line[0] == '#' { continue } - a := strings.Split(line, " ", -1) + a := strings.Split(line, " ") if len(a) != 4 { t.Error("testfp.txt:", lineno, ": wrong field count") continue diff --git a/src/pkg/strconv/quote.go b/src/pkg/strconv/quote.go index 98b19d3a2..05e49d32d 100644 --- a/src/pkg/strconv/quote.go +++ b/src/pkg/strconv/quote.go @@ -24,7 +24,10 @@ func quoteWith(s string, quote byte, ASCIIonly bool) string { rune, width = utf8.DecodeRuneInString(s) } if width == 1 && rune == utf8.RuneError { - goto printEscX + buf.WriteString(`\x`) + buf.WriteByte(lowerhex[s[0]>>4]) + buf.WriteByte(lowerhex[s[0]&0xF]) + continue } if rune == int(quote) || rune == '\\' { // always backslashed buf.WriteByte('\\') @@ -58,7 +61,6 @@ func quoteWith(s string, quote byte, ASCIIonly bool) string { default: switch { case rune < ' ': - printEscX: buf.WriteString(`\x`) buf.WriteByte(lowerhex[s[0]>>4]) buf.WriteByte(lowerhex[s[0]&0xF]) diff --git a/src/pkg/strings/reader.go b/src/pkg/strings/reader.go index 10b0278e1..8423f7e45 100644 --- a/src/pkg/strings/reader.go +++ b/src/pkg/strings/reader.go @@ -49,7 +49,7 @@ func (r *Reader) ReadByte() (b byte, err os.Error) { // read yet. func (r *Reader) UnreadByte() os.Error { if r.i <= 0 { - return os.ErrorString("strings.Reader: at beginning of string") + return os.NewError("strings.Reader: at beginning of string") } r.i-- r.prevRune = -1 @@ -80,7 +80,7 @@ func (r *Reader) ReadRune() (rune int, size int, err os.Error) { // The last method called on r must have been ReadRune. func (r *Reader) UnreadRune() os.Error { if r.prevRune < 0 { - return os.ErrorString("strings.Reader: previous operation was not ReadRune") + return os.NewError("strings.Reader: previous operation was not ReadRune") } r.i = r.prevRune r.prevRune = -1 diff --git a/src/pkg/strings/strings.go b/src/pkg/strings/strings.go index bfd057180..6afbc7dc2 100644 --- a/src/pkg/strings/strings.go +++ b/src/pkg/strings/strings.go @@ -198,26 +198,40 @@ func genSplit(s, sep string, sepSave, n int) []string { return a[0 : na+1] } -// Split slices s into substrings separated by sep and returns a slice of +// SplitN slices s into substrings separated by sep and returns a slice of // the substrings between those separators. -// If sep is empty, Split splits after each UTF-8 sequence. +// If sep is empty, SplitN splits after each UTF-8 sequence. // The count determines the number of substrings to return: // n > 0: at most n substrings; the last substring will be the unsplit remainder. // n == 0: the result is nil (zero substrings) // n < 0: all substrings -func Split(s, sep string, n int) []string { return genSplit(s, sep, 0, n) } +func SplitN(s, sep string, n int) []string { return genSplit(s, sep, 0, n) } -// SplitAfter slices s into substrings after each instance of sep and +// SplitAfterN slices s into substrings after each instance of sep and // returns a slice of those substrings. -// If sep is empty, Split splits after each UTF-8 sequence. +// If sep is empty, SplitAfterN splits after each UTF-8 sequence. // The count determines the number of substrings to return: // n > 0: at most n substrings; the last substring will be the unsplit remainder. // n == 0: the result is nil (zero substrings) // n < 0: all substrings -func SplitAfter(s, sep string, n int) []string { +func SplitAfterN(s, sep string, n int) []string { return genSplit(s, sep, len(sep), n) } +// Split slices s into all substrings separated by sep and returns a slice of +// the substrings between those separators. +// If sep is empty, Split splits after each UTF-8 sequence. +// It is equivalent to SplitN with a count of -1. +func Split(s, sep string) []string { return genSplit(s, sep, 0, -1) } + +// SplitAfter slices s into all substrings after each instance of sep and +// returns a slice of those substrings. +// If sep is empty, SplitAfter splits after each UTF-8 sequence. +// It is equivalent to SplitAfterN with a count of -1. +func SplitAfter(s, sep string) []string { + return genSplit(s, sep, len(sep), -1) +} + // Fields splits the string s around each instance of one or more consecutive white space // characters, returning an array of substrings of s or an empty list if s contains only white space. func Fields(s string) []string { diff --git a/src/pkg/strings/strings_test.go b/src/pkg/strings/strings_test.go index a1a635ddd..c54617339 100644 --- a/src/pkg/strings/strings_test.go +++ b/src/pkg/strings/strings_test.go @@ -186,7 +186,7 @@ var explodetests = []ExplodeTest{ func TestExplode(t *testing.T) { for _, tt := range explodetests { - a := Split(tt.s, "", tt.n) + a := SplitN(tt.s, "", tt.n) if !eq(a, tt.a) { t.Errorf("explode(%q, %d) = %v; want %v", tt.s, tt.n, a, tt.a) continue @@ -223,7 +223,7 @@ var splittests = []SplitTest{ func TestSplit(t *testing.T) { for _, tt := range splittests { - a := Split(tt.s, tt.sep, tt.n) + a := SplitN(tt.s, tt.sep, tt.n) if !eq(a, tt.a) { t.Errorf("Split(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, a, tt.a) continue @@ -235,6 +235,12 @@ func TestSplit(t *testing.T) { if s != tt.s { t.Errorf("Join(Split(%q, %q, %d), %q) = %q", tt.s, tt.sep, tt.n, tt.sep, s) } + if tt.n < 0 { + b := Split(tt.s, tt.sep) + if !reflect.DeepEqual(a, b) { + t.Errorf("Split disagrees with SplitN(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, b, a) + } + } } } @@ -256,7 +262,7 @@ var splitaftertests = []SplitTest{ func TestSplitAfter(t *testing.T) { for _, tt := range splitaftertests { - a := SplitAfter(tt.s, tt.sep, tt.n) + a := SplitAfterN(tt.s, tt.sep, tt.n) if !eq(a, tt.a) { t.Errorf(`Split(%q, %q, %d) = %v; want %v`, tt.s, tt.sep, tt.n, a, tt.a) continue @@ -265,6 +271,12 @@ func TestSplitAfter(t *testing.T) { if s != tt.s { t.Errorf(`Join(Split(%q, %q, %d), %q) = %q`, tt.s, tt.sep, tt.n, tt.sep, s) } + if tt.n < 0 { + b := SplitAfter(tt.s, tt.sep) + if !reflect.DeepEqual(a, b) { + t.Errorf("SplitAfter disagrees with SplitAfterN(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, b, a) + } + } } } @@ -623,8 +635,8 @@ func equal(m string, s1, s2 string, t *testing.T) bool { if s1 == s2 { return true } - e1 := Split(s1, "", -1) - e2 := Split(s2, "", -1) + e1 := Split(s1, "") + e2 := Split(s2, "") for i, c1 := range e1 { if i > len(e2) { break diff --git a/src/pkg/sync/mutex.go b/src/pkg/sync/mutex.go index 13f03cad3..2d46c8994 100644 --- a/src/pkg/sync/mutex.go +++ b/src/pkg/sync/mutex.go @@ -17,8 +17,8 @@ import ( // Mutexes can be created as part of other structures; // the zero value for a Mutex is an unlocked mutex. type Mutex struct { - key int32 - sema uint32 + state int32 + sema uint32 } // A Locker represents an object that can be locked and unlocked. @@ -27,15 +27,41 @@ type Locker interface { Unlock() } +const ( + mutexLocked = 1 << iota // mutex is locked + mutexWoken + mutexWaiterShift = iota +) + // Lock locks m. // If the lock is already in use, the calling goroutine // blocks until the mutex is available. func (m *Mutex) Lock() { - if atomic.AddInt32(&m.key, 1) == 1 { - // changed from 0 to 1; we hold lock + // Fast path: grab unlocked mutex. + if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) { return } - runtime.Semacquire(&m.sema) + + awoke := false + for { + old := m.state + new := old | mutexLocked + if old&mutexLocked != 0 { + new = old + 1<<mutexWaiterShift + } + if awoke { + // The goroutine has been woken from sleep, + // so we need to reset the flag in either case. + new &^= mutexWoken + } + if atomic.CompareAndSwapInt32(&m.state, old, new) { + if old&mutexLocked == 0 { + break + } + runtime.Semacquire(&m.sema) + awoke = true + } + } } // Unlock unlocks m. @@ -45,14 +71,25 @@ func (m *Mutex) Lock() { // It is allowed for one goroutine to lock a Mutex and then // arrange for another goroutine to unlock it. func (m *Mutex) Unlock() { - switch v := atomic.AddInt32(&m.key, -1); { - case v == 0: - // changed from 1 to 0; no contention - return - case v == -1: - // changed from 0 to -1: wasn't locked - // (or there are 4 billion goroutines waiting) + // Fast path: drop lock bit. + new := atomic.AddInt32(&m.state, -mutexLocked) + if (new+mutexLocked)&mutexLocked == 0 { panic("sync: unlock of unlocked mutex") } - runtime.Semrelease(&m.sema) + + old := new + for { + // If there are no waiters or a goroutine has already + // been woken or grabbed the lock, no need to wake anyone. + if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 { + return + } + // Grab the right to wake someone. + new = (old - 1<<mutexWaiterShift) | mutexWoken + if atomic.CompareAndSwapInt32(&m.state, old, new) { + runtime.Semrelease(&m.sema) + return + } + old = m.state + } } diff --git a/src/pkg/sync/mutex_test.go b/src/pkg/sync/mutex_test.go index f5c20ca49..d5ada8567 100644 --- a/src/pkg/sync/mutex_test.go +++ b/src/pkg/sync/mutex_test.go @@ -9,6 +9,7 @@ package sync_test import ( "runtime" . "sync" + "sync/atomic" "testing" ) @@ -43,7 +44,7 @@ func BenchmarkContendedSemaphore(b *testing.B) { s := new(uint32) *s = 1 c := make(chan bool) - runtime.GOMAXPROCS(2) + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) b.StartTimer() go HammerSemaphore(s, b.N/2, c) @@ -72,24 +73,6 @@ func TestMutex(t *testing.T) { } } -func BenchmarkUncontendedMutex(b *testing.B) { - m := new(Mutex) - HammerMutex(m, b.N, make(chan bool, 2)) -} - -func BenchmarkContendedMutex(b *testing.B) { - b.StopTimer() - m := new(Mutex) - c := make(chan bool) - runtime.GOMAXPROCS(2) - b.StartTimer() - - go HammerMutex(m, b.N/2, c) - go HammerMutex(m, b.N/2, c) - <-c - <-c -} - func TestMutexPanic(t *testing.T) { defer func() { if recover() == nil { @@ -102,3 +85,83 @@ func TestMutexPanic(t *testing.T) { mu.Unlock() mu.Unlock() } + +func BenchmarkMutexUncontended(b *testing.B) { + type PaddedMutex struct { + Mutex + pad [128]uint8 + } + const CallsPerSched = 1000 + procs := runtime.GOMAXPROCS(-1) + N := int32(b.N / CallsPerSched) + c := make(chan bool, procs) + for p := 0; p < procs; p++ { + go func() { + var mu PaddedMutex + for atomic.AddInt32(&N, -1) >= 0 { + runtime.Gosched() + for g := 0; g < CallsPerSched; g++ { + mu.Lock() + mu.Unlock() + } + } + c <- true + }() + } + for p := 0; p < procs; p++ { + <-c + } +} + +func benchmarkMutex(b *testing.B, slack, work bool) { + const ( + CallsPerSched = 1000 + LocalWork = 100 + GoroutineSlack = 10 + ) + procs := runtime.GOMAXPROCS(-1) + if slack { + procs *= GoroutineSlack + } + N := int32(b.N / CallsPerSched) + c := make(chan bool, procs) + var mu Mutex + for p := 0; p < procs; p++ { + go func() { + foo := 0 + for atomic.AddInt32(&N, -1) >= 0 { + runtime.Gosched() + for g := 0; g < CallsPerSched; g++ { + mu.Lock() + mu.Unlock() + if work { + for i := 0; i < LocalWork; i++ { + foo *= 2 + foo /= 2 + } + } + } + } + c <- foo == 42 + }() + } + for p := 0; p < procs; p++ { + <-c + } +} + +func BenchmarkMutex(b *testing.B) { + benchmarkMutex(b, false, false) +} + +func BenchmarkMutexSlack(b *testing.B) { + benchmarkMutex(b, true, false) +} + +func BenchmarkMutexWork(b *testing.B) { + benchmarkMutex(b, false, true) +} + +func BenchmarkMutexWorkSlack(b *testing.B) { + benchmarkMutex(b, true, true) +} diff --git a/src/pkg/sync/once.go b/src/pkg/sync/once.go index b6f5f5a87..447b71dcb 100644 --- a/src/pkg/sync/once.go +++ b/src/pkg/sync/once.go @@ -4,10 +4,14 @@ package sync +import ( + "sync/atomic" +) + // Once is an object that will perform exactly one action. type Once struct { m Mutex - done bool + done int32 } // Do calls the function f if and only if the method is being called for the @@ -26,10 +30,14 @@ type Once struct { // Do to be called, it will deadlock. // func (o *Once) Do(f func()) { + if atomic.AddInt32(&o.done, 0) == 1 { + return + } + // Slow-path. o.m.Lock() defer o.m.Unlock() - if !o.done { - o.done = true + if o.done == 0 { f() + atomic.CompareAndSwapInt32(&o.done, 0, 1) } } diff --git a/src/pkg/sync/once_test.go b/src/pkg/sync/once_test.go index 155954a49..157a3667a 100644 --- a/src/pkg/sync/once_test.go +++ b/src/pkg/sync/once_test.go @@ -6,6 +6,8 @@ package sync_test import ( . "sync" + "sync/atomic" + "runtime" "testing" ) @@ -35,3 +37,26 @@ func TestOnce(t *testing.T) { t.Errorf("once failed: %d is not 1", *o) } } + +func BenchmarkOnce(b *testing.B) { + const CallsPerSched = 1000 + procs := runtime.GOMAXPROCS(-1) + N := int32(b.N / CallsPerSched) + var once Once + f := func() {} + c := make(chan bool, procs) + for p := 0; p < procs; p++ { + go func() { + for atomic.AddInt32(&N, -1) >= 0 { + runtime.Gosched() + for g := 0; g < CallsPerSched; g++ { + once.Do(f) + } + } + c <- true + }() + } + for p := 0; p < procs; p++ { + <-c + } +} diff --git a/src/pkg/syscall/Makefile b/src/pkg/syscall/Makefile index 9284fcc5d..212b6f85d 100644 --- a/src/pkg/syscall/Makefile +++ b/src/pkg/syscall/Makefile @@ -17,24 +17,32 @@ GOFILES=\ ztypes_$(GOOS)_$(GOARCH).go\ GOFILES_freebsd=\ + bpf_bsd.go\ exec_unix.go\ route_bsd.go\ + sockcmsg_unix.go\ syscall_bsd.go\ syscall_unix.go\ GOFILES_darwin=\ + bpf_bsd.go\ exec_unix.go\ route_bsd.go\ + sockcmsg_unix.go\ syscall_bsd.go\ syscall_unix.go\ GOFILES_linux=\ exec_unix.go\ + lsf_linux.go\ netlink_linux.go\ + sockcmsg_unix.go\ syscall_unix.go\ GOFILES_windows=\ exec_windows.go\ + zerrors_windows.go\ + ztypes_windows.go\ GOFILES_plan9=\ exec_plan9.go\ diff --git a/src/pkg/syscall/asm_windows_386.s b/src/pkg/syscall/asm_windows_386.s index 3d9f6fc94..a7b95643d 100644 --- a/src/pkg/syscall/asm_windows_386.s +++ b/src/pkg/syscall/asm_windows_386.s @@ -3,5 +3,5 @@ // license that can be found in the LICENSE file. // -// System calls for 386, Windows are implemented in ../runtime/windows/syscall.cgo +// System calls for 386, Windows are implemented in ../runtime/windows/syscall.goc // diff --git a/src/pkg/syscall/asm_windows_amd64.s b/src/pkg/syscall/asm_windows_amd64.s new file mode 100644 index 000000000..8b38710c7 --- /dev/null +++ b/src/pkg/syscall/asm_windows_amd64.s @@ -0,0 +1,7 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +// System calls for amd64, Windows are implemented in ../runtime/windows/syscall.goc +// diff --git a/src/pkg/syscall/bpf_bsd.go b/src/pkg/syscall/bpf_bsd.go new file mode 100644 index 000000000..1eac9a3d8 --- /dev/null +++ b/src/pkg/syscall/bpf_bsd.go @@ -0,0 +1,167 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Berkeley packet filter for BSD variants + +package syscall + +import ( + "unsafe" +) + +func BpfStmt(code, k int) *BpfInsn { + return &BpfInsn{Code: uint16(code), K: uint32(k)} +} + +func BpfJump(code, k, jt, jf int) *BpfInsn { + return &BpfInsn{Code: uint16(code), Jt: uint8(jt), Jf: uint8(jf), K: uint32(k)} +} + +func BpfBuflen(fd int) (int, int) { + var l int + _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGBLEN, uintptr(unsafe.Pointer(&l))) + if e := int(ep); e != 0 { + return 0, e + } + return l, 0 +} + +func SetBpfBuflen(fd, l int) (int, int) { + _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSBLEN, uintptr(unsafe.Pointer(&l))) + if e := int(ep); e != 0 { + return 0, e + } + return l, 0 +} + +func BpfDatalink(fd int) (int, int) { + var t int + _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGDLT, uintptr(unsafe.Pointer(&t))) + if e := int(ep); e != 0 { + return 0, e + } + return t, 0 +} + +func SetBpfDatalink(fd, t int) (int, int) { + _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSDLT, uintptr(unsafe.Pointer(&t))) + if e := int(ep); e != 0 { + return 0, e + } + return t, 0 +} + +func SetBpfPromisc(fd, m int) int { + _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCPROMISC, uintptr(unsafe.Pointer(&m))) + if e := int(ep); e != 0 { + return e + } + return 0 +} + +func FlushBpf(fd int) int { + _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCFLUSH, 0) + if e := int(ep); e != 0 { + return e + } + return 0 +} + +type ivalue struct { + name [IFNAMSIZ]byte + value int16 +} + +func BpfInterface(fd int, name string) (string, int) { + var iv ivalue + _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGETIF, uintptr(unsafe.Pointer(&iv))) + if e := int(ep); e != 0 { + return "", e + } + return name, 0 +} + +func SetBpfInterface(fd int, name string) int { + var iv ivalue + copy(iv.name[:], []byte(name)) + _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSETIF, uintptr(unsafe.Pointer(&iv))) + if e := int(ep); e != 0 { + return e + } + return 0 +} + +func BpfTimeout(fd int) (*Timeval, int) { + var tv Timeval + _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGRTIMEOUT, uintptr(unsafe.Pointer(&tv))) + if e := int(ep); e != 0 { + return nil, e + } + return &tv, 0 +} + +func SetBpfTimeout(fd int, tv *Timeval) int { + _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSRTIMEOUT, uintptr(unsafe.Pointer(tv))) + if e := int(ep); e != 0 { + return e + } + return 0 +} + +func BpfStats(fd int) (*BpfStat, int) { + var s BpfStat + _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGSTATS, uintptr(unsafe.Pointer(&s))) + if e := int(ep); e != 0 { + return nil, e + } + return &s, 0 +} + +func SetBpfImmediate(fd, m int) int { + _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCIMMEDIATE, uintptr(unsafe.Pointer(&m))) + if e := int(ep); e != 0 { + return e + } + return 0 +} + +func SetBpf(fd int, i []BpfInsn) int { + var p BpfProgram + p.Len = uint32(len(i)) + p.Insns = (*BpfInsn)(unsafe.Pointer(&i[0])) + _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSETF, uintptr(unsafe.Pointer(&p))) + if e := int(ep); e != 0 { + return e + } + return 0 +} + +func CheckBpfVersion(fd int) int { + var v BpfVersion + _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCVERSION, uintptr(unsafe.Pointer(&v))) + if e := int(ep); e != 0 { + return e + } + if v.Major != BPF_MAJOR_VERSION || v.Minor != BPF_MINOR_VERSION { + return EINVAL + } + return 0 +} + +func BpfHeadercmpl(fd int) (int, int) { + var f int + _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCGHDRCMPLT, uintptr(unsafe.Pointer(&f))) + if e := int(ep); e != 0 { + return 0, e + } + return f, 0 +} + +func SetBpfHeadercmpl(fd, f int) int { + _, _, ep := Syscall(SYS_IOCTL, uintptr(fd), BIOCSHDRCMPLT, uintptr(unsafe.Pointer(&f))) + if e := int(ep); e != 0 { + return e + } + return 0 +} diff --git a/src/pkg/syscall/exec_plan9.go b/src/pkg/syscall/exec_plan9.go index 962b39b78..66ab1fced 100644 --- a/src/pkg/syscall/exec_plan9.go +++ b/src/pkg/syscall/exec_plan9.go @@ -62,7 +62,7 @@ var ForkLock sync.RWMutex // Convert array of string to array // of NUL-terminated byte pointer. -func StringArrayPtr(ss []string) []*byte { +func StringSlicePtr(ss []string) []*byte { bb := make([]*byte, len(ss)+1) for i := 0; i < len(ss); i++ { bb[i] = StringBytePtr(ss[i]) @@ -169,7 +169,7 @@ func init() { // no rescheduling, no malloc calls, and no new stack segments. // The calls to RawSyscall are okay because they are assembly // functions that do not grow the stack. -func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, chroot, dir *byte, attr *ProcAttr, fdsToClose []int, pipe int) (pid int, err Error) { +func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, attr *ProcAttr, fdsToClose []int, pipe int, rflag int) (pid int, err Error) { // Declare all variables at top in case any // declarations require heap allocation (e.g., errbuf). var ( @@ -190,7 +190,7 @@ func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, chroot, dir * // About to call fork. // No more allocation or calls of non-assembly functions. - r1, _, _ = RawSyscall(SYS_RFORK, uintptr(RFPROC|RFFDG|RFREND|clearenv), 0, 0) + r1, _, _ = RawSyscall(SYS_RFORK, uintptr(RFPROC|RFFDG|RFREND|clearenv|rflag), 0, 0) if r1 != 0 { if int(r1) == -1 { @@ -338,14 +338,18 @@ type envItem struct { } type ProcAttr struct { - Dir string // Current working directory. - Env []string // Environment. - Files []int // File descriptors. - Chroot string // Chroot. + Dir string // Current working directory. + Env []string // Environment. + Files []int // File descriptors. + Sys *SysProcAttr } -var zeroAttributes ProcAttr +type SysProcAttr struct { + Rfork int // additional flags to pass to rfork +} +var zeroProcAttr ProcAttr +var zeroSysProcAttr SysProcAttr func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err Error) { var ( @@ -356,7 +360,11 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err Error) ) if attr == nil { - attr = &zeroAttributes + attr = &zeroProcAttr + } + sys := attr.Sys + if sys == nil { + sys = &zeroSysProcAttr } p[0] = -1 @@ -364,12 +372,8 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err Error) // Convert args to C form. argv0p := StringBytePtr(argv0) - argvp := StringArrayPtr(argv) + argvp := StringSlicePtr(argv) - var chroot *byte - if attr.Chroot != "" { - chroot = StringBytePtr(attr.Chroot) - } var dir *byte if attr.Dir != "" { dir = StringBytePtr(attr.Dir) @@ -439,7 +443,7 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err Error) fdsToClose = append(fdsToClose, p[0]) // Kick off child. - pid, err = forkAndExecInChild(argv0p, argvp, envvParsed, chroot, dir, attr, fdsToClose, p[1]) + pid, err = forkAndExecInChild(argv0p, argvp, envvParsed, dir, attr, fdsToClose, p[1], sys.Rfork) if err != nil { if p[0] >= 0 { @@ -514,7 +518,7 @@ func Exec(argv0 string, argv []string, envv []string) (err Error) { _, _, e := Syscall(SYS_EXEC, uintptr(unsafe.Pointer(StringBytePtr(argv0))), - uintptr(unsafe.Pointer(&StringArrayPtr(argv)[0])), + uintptr(unsafe.Pointer(&StringSlicePtr(argv)[0])), 0) return NewError(e) diff --git a/src/pkg/syscall/exec_unix.go b/src/pkg/syscall/exec_unix.go index b6cb1baa2..46f05efef 100644 --- a/src/pkg/syscall/exec_unix.go +++ b/src/pkg/syscall/exec_unix.go @@ -62,7 +62,7 @@ var ForkLock sync.RWMutex // Convert array of string to array // of NUL-terminated byte pointer. -func StringArrayPtr(ss []string) []*byte { +func StringSlicePtr(ss []string) []*byte { bb := make([]*byte, len(ss)+1) for i := 0; i < len(ss); i++ { bb[i] = StringBytePtr(ss[i]) @@ -96,7 +96,7 @@ func SetNonblock(fd int, nonblocking bool) (errno int) { // no rescheduling, no malloc calls, and no new stack segments. // The calls to RawSyscall are okay because they are assembly // functions that do not grow the stack. -func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, pipe int) (pid int, err int) { +func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err int) { // Declare all variables at top in case any // declarations require heap allocation (e.g., err1). var r1, r2, err1 uintptr @@ -131,7 +131,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // Fork succeeded, now in child. // Enable tracing if requested. - if attr.Ptrace { + if sys.Ptrace { _, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0) if err1 != 0 { goto childerror @@ -139,13 +139,21 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } // Session ID - if attr.Setsid { + if sys.Setsid { _, _, err1 = RawSyscall(SYS_SETSID, 0, 0, 0) if err1 != 0 { goto childerror } } + // Set process group + if sys.Setpgid { + _, _, err1 = RawSyscall(SYS_SETPGID, 0, 0, 0) + if err1 != 0 { + goto childerror + } + } + // Chroot if chroot != nil { _, _, err1 = RawSyscall(SYS_CHROOT, uintptr(unsafe.Pointer(chroot)), 0, 0) @@ -155,21 +163,21 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } // User and groups - if attr.Credential != nil { - ngroups := uintptr(len(attr.Credential.Groups)) + if cred := sys.Credential; cred != nil { + ngroups := uintptr(len(cred.Groups)) groups := uintptr(0) if ngroups > 0 { - groups = uintptr(unsafe.Pointer(&attr.Credential.Groups[0])) + groups = uintptr(unsafe.Pointer(&cred.Groups[0])) } _, _, err1 = RawSyscall(SYS_SETGROUPS, ngroups, groups, 0) if err1 != 0 { goto childerror } - _, _, err1 = RawSyscall(SYS_SETGID, uintptr(attr.Credential.Gid), 0, 0) + _, _, err1 = RawSyscall(SYS_SETGID, uintptr(cred.Gid), 0, 0) if err1 != 0 { goto childerror } - _, _, err1 = RawSyscall(SYS_SETUID, uintptr(attr.Credential.Uid), 0, 0) + _, _, err1 = RawSyscall(SYS_SETUID, uintptr(cred.Uid), 0, 0) if err1 != 0 { goto childerror } @@ -241,6 +249,22 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr RawSyscall(SYS_CLOSE, uintptr(i), 0, 0) } + // Detach fd 0 from tty + if sys.Noctty { + _, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCNOTTY), 0) + if err1 != 0 { + goto childerror + } + } + + // Make fd 0 the tty + if sys.Setctty { + _, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCSCTTY), 0) + if err1 != 0 { + goto childerror + } + } + // Time to exec. _, _, err1 = RawSyscall(SYS_EXECVE, uintptr(unsafe.Pointer(argv0)), @@ -249,7 +273,7 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr childerror: // send error code on pipe - RawSyscall(SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&err1)), uintptr(unsafe.Sizeof(err1))) + RawSyscall(SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1)) for { RawSyscall(SYS_EXIT, 253, 0, 0) } @@ -260,23 +284,35 @@ childerror: panic("unreached") } +// Credential holds user and group identities to be assumed +// by a child process started by StartProcess. type Credential struct { Uid uint32 // User ID. Gid uint32 // Group ID. Groups []uint32 // Supplementary group IDs. } +// ProcAttr holds attributes that will be applied to a new process started +// by StartProcess. type ProcAttr struct { - Setsid bool // Create session. - Ptrace bool // Enable tracing. - Dir string // Current working directory. - Env []string // Environment. - Files []int // File descriptors. + Dir string // Current working directory. + Env []string // Environment. + Files []int // File descriptors. + Sys *SysProcAttr +} + +type SysProcAttr struct { Chroot string // Chroot. Credential *Credential // Credential. + Ptrace bool // Enable tracing. + Setsid bool // Create session. + Setpgid bool // Set process group ID to new pid (SYSV setpgrp) + Setctty bool // Set controlling terminal to fd 0 + Noctty bool // Detach fd 0 from controlling terminal } -var zeroAttributes ProcAttr +var zeroProcAttr ProcAttr +var zeroSysProcAttr SysProcAttr func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { var p [2]int @@ -285,7 +321,11 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { var wstatus WaitStatus if attr == nil { - attr = &zeroAttributes + attr = &zeroProcAttr + } + sys := attr.Sys + if sys == nil { + sys = &zeroSysProcAttr } p[0] = -1 @@ -293,16 +333,16 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { // Convert args to C form. argv0p := StringBytePtr(argv0) - argvp := StringArrayPtr(argv) - envvp := StringArrayPtr(attr.Env) + argvp := StringSlicePtr(argv) + envvp := StringSlicePtr(attr.Env) if OS == "freebsd" && len(argv[0]) > len(argv0) { argvp[0] = argv0p } var chroot *byte - if attr.Chroot != "" { - chroot = StringBytePtr(attr.Chroot) + if sys.Chroot != "" { + chroot = StringBytePtr(sys.Chroot) } var dir *byte if attr.Dir != "" { @@ -326,24 +366,18 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { } // Kick off child. - pid, err = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, p[1]) + pid, err = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, sys, p[1]) if err != 0 { - error: - if p[0] >= 0 { - Close(p[0]) - Close(p[1]) - } - ForkLock.Unlock() - return 0, err + goto error } ForkLock.Unlock() // Read child error status from pipe. Close(p[1]) - n, err = read(p[0], (*byte)(unsafe.Pointer(&err1)), unsafe.Sizeof(err1)) + n, err = read(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1))) Close(p[0]) if err != 0 || n != 0 { - if n == unsafe.Sizeof(err1) { + if n == int(unsafe.Sizeof(err1)) { err = int(err1) } if err == 0 { @@ -361,6 +395,14 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err int) { // Read got EOF, so pipe closed on exec, so exec succeeded. return pid, 0 + +error: + if p[0] >= 0 { + Close(p[0]) + Close(p[1]) + } + ForkLock.Unlock() + return 0, err } // Combination of fork and exec, careful to be thread safe. @@ -378,7 +420,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, func Exec(argv0 string, argv []string, envv []string) (err int) { _, _, err1 := RawSyscall(SYS_EXECVE, uintptr(unsafe.Pointer(StringBytePtr(argv0))), - uintptr(unsafe.Pointer(&StringArrayPtr(argv)[0])), - uintptr(unsafe.Pointer(&StringArrayPtr(envv)[0]))) + uintptr(unsafe.Pointer(&StringSlicePtr(argv)[0])), + uintptr(unsafe.Pointer(&StringSlicePtr(envv)[0]))) return int(err1) } diff --git a/src/pkg/syscall/exec_windows.go b/src/pkg/syscall/exec_windows.go index b25f4a650..e8b540ad1 100644 --- a/src/pkg/syscall/exec_windows.go +++ b/src/pkg/syscall/exec_windows.go @@ -121,11 +121,11 @@ func createEnvBlock(envv []string) *uint16 { return &utf16.Encode([]int(string(b)))[0] } -func CloseOnExec(fd int) { - SetHandleInformation(int32(fd), HANDLE_FLAG_INHERIT, 0) +func CloseOnExec(fd Handle) { + SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0) } -func SetNonblock(fd int, nonblocking bool) (errno int) { +func SetNonblock(fd Handle, nonblocking bool) (errno int) { return 0 } @@ -218,22 +218,32 @@ func joinExeDirAndFName(dir, p string) (name string, err int) { } type ProcAttr struct { - Dir string - Env []string - Files []int + Dir string + Env []string + Files []Handle + Sys *SysProcAttr +} + +type SysProcAttr struct { HideWindow bool CmdLine string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess } -var zeroAttributes ProcAttr +var zeroProcAttr ProcAttr +var zeroSysProcAttr SysProcAttr func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, err int) { if len(argv0) == 0 { return 0, 0, EWINDOWS } if attr == nil { - attr = &zeroAttributes + attr = &zeroProcAttr } + sys := attr.Sys + if sys == nil { + sys = &zeroSysProcAttr + } + if len(attr.Files) > 3 { return 0, 0, EWINDOWS } @@ -257,8 +267,8 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, // Windows CreateProcess takes the command line as a single string: // use attr.CmdLine if set, else build the command line by escaping // and joining each argument with spaces - if attr.CmdLine != "" { - cmdline = attr.CmdLine + if sys.CmdLine != "" { + cmdline = sys.CmdLine } else { cmdline = makeCmdLine(argv) } @@ -280,20 +290,20 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, defer ForkLock.Unlock() p, _ := GetCurrentProcess() - fd := make([]int32, len(attr.Files)) + fd := make([]Handle, len(attr.Files)) for i := range attr.Files { if attr.Files[i] > 0 { - err := DuplicateHandle(p, int32(attr.Files[i]), p, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) + err := DuplicateHandle(p, Handle(attr.Files[i]), p, &fd[i], 0, true, DUPLICATE_SAME_ACCESS) if err != 0 { return 0, 0, err } - defer CloseHandle(int32(fd[i])) + defer CloseHandle(Handle(fd[i])) } } si := new(StartupInfo) si.Cb = uint32(unsafe.Sizeof(*si)) si.Flags = STARTF_USESTDHANDLES - if attr.HideWindow { + if sys.HideWindow { si.Flags |= STARTF_USESHOWWINDOW si.ShowWindow = SW_HIDE } @@ -307,7 +317,7 @@ func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid, handle int, if err != 0 { return 0, 0, err } - defer CloseHandle(pi.Thread) + defer CloseHandle(Handle(pi.Thread)) return int(pi.ProcessId), int(pi.Process), 0 } diff --git a/src/pkg/syscall/lsf_linux.go b/src/pkg/syscall/lsf_linux.go new file mode 100644 index 000000000..f2bd2b757 --- /dev/null +++ b/src/pkg/syscall/lsf_linux.go @@ -0,0 +1,78 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Linux socket filter + +package syscall + +import ( + "unsafe" +) + +func LsfStmt(code, k int) *SockFilter { + return &SockFilter{Code: uint16(code), K: uint32(k)} +} + +func LsfJump(code, k, jt, jf int) *SockFilter { + return &SockFilter{Code: uint16(code), Jt: uint8(jt), Jf: uint8(jf), K: uint32(k)} +} + +func LsfSocket(ifindex, proto int) (int, int) { + var lsall SockaddrLinklayer + s, e := Socket(AF_PACKET, SOCK_RAW, proto) + if e != 0 { + return 0, e + } + p := (*[2]byte)(unsafe.Pointer(&lsall.Protocol)) + p[0] = byte(proto >> 8) + p[1] = byte(proto) + lsall.Ifindex = ifindex + e = Bind(s, &lsall) + if e != 0 { + Close(s) + return 0, e + } + return s, 0 +} + +type iflags struct { + name [IFNAMSIZ]byte + flags uint16 +} + +func SetLsfPromisc(name string, m bool) int { + s, e := Socket(AF_INET, SOCK_DGRAM, 0) + if e != 0 { + return e + } + defer Close(s) + var ifl iflags + copy(ifl.name[:], []byte(name)) + _, _, ep := Syscall(SYS_IOCTL, uintptr(s), SIOCGIFFLAGS, uintptr(unsafe.Pointer(&ifl))) + if e := int(ep); e != 0 { + return e + } + if m { + ifl.flags |= uint16(IFF_PROMISC) + } else { + ifl.flags &= ^uint16(IFF_PROMISC) + } + _, _, ep = Syscall(SYS_IOCTL, uintptr(s), SIOCSIFFLAGS, uintptr(unsafe.Pointer(&ifl))) + if e := int(ep); e != 0 { + return e + } + return 0 +} + +func AttachLsf(fd int, i []SockFilter) int { + var p SockFprog + p.Len = uint16(len(i)) + p.Filter = (*SockFilter)(unsafe.Pointer(&i[0])) + return setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, uintptr(unsafe.Pointer(&p)), unsafe.Sizeof(p)) +} + +func DetachLsf(fd int) int { + var dummy int + return setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, uintptr(unsafe.Pointer(&dummy)), unsafe.Sizeof(dummy)) +} diff --git a/src/pkg/syscall/mkall.sh b/src/pkg/syscall/mkall.sh index 0a4ca0326..7d0c1ac2a 100755 --- a/src/pkg/syscall/mkall.sh +++ b/src/pkg/syscall/mkall.sh @@ -78,6 +78,7 @@ GOOSARCH="${GOOS}_${GOARCH}" # defaults mksyscall="./mksyscall.pl" mkerrors="./mkerrors.sh" +zerrors="zerrors_$GOOSARCH.go" run="sh" case "$1" in @@ -120,14 +121,13 @@ freebsd_amd64) darwin_386) mkerrors="$mkerrors -f -m32" mksyscall="./mksyscall.pl -l32" - mksysnum="./mksysnum_darwin.pl /home/rsc/pub/xnu-1228/bsd/kern/syscalls.master" + mksysnum="./mksysnum_darwin.pl /usr/include/sys/syscall.h" mktypes="godefs -gsyscall -f-m32" ;; darwin_amd64) mkerrors="$mkerrors -f -m64" - mksysnum="./mksysnum_darwin.pl /home/rsc/pub/xnu-1228/bsd/kern/syscalls.master" + mksysnum="./mksysnum_darwin.pl /usr/include/sys/syscall.h" mktypes="godefs -gsyscall -f-m64" - mkerrors="./mkerrors.sh" ;; linux_386) mkerrors="$mkerrors -f -m32" @@ -151,6 +151,14 @@ windows_386) mksysnum= mktypes= mkerrors="./mkerrors_windows.sh -f -m32" + zerrors="zerrors_windows.go" + ;; +windows_amd64) + mksyscall="./mksyscall_windows.pl" + mksysnum= + mktypes= + mkerrors="./mkerrors_windows.sh -f -m32" + zerrors="zerrors_windows.go" ;; plan9_386) mkerrors= @@ -165,7 +173,7 @@ plan9_386) esac ( - if [ -n "$mkerrors" ]; then echo "$mkerrors |gofmt >zerrors_$GOOSARCH.go"; fi + if [ -n "$mkerrors" ]; then echo "$mkerrors |gofmt >$zerrors"; fi syscall_goos="syscall_$GOOS.go" case "$GOOS" in darwin | freebsd) diff --git a/src/pkg/syscall/mkerrors.sh b/src/pkg/syscall/mkerrors.sh index 1949ebfad..c90cd1c00 100755 --- a/src/pkg/syscall/mkerrors.sh +++ b/src/pkg/syscall/mkerrors.sh @@ -30,7 +30,9 @@ includes_Linux=' #include <sys/stat.h> #include <sys/types.h> #include <linux/if_addr.h> +#include <linux/if_ether.h> #include <linux/if_tun.h> +#include <linux/filter.h> #include <linux/netlink.h> #include <linux/reboot.h> #include <linux/rtnetlink.h> @@ -47,6 +49,7 @@ includes_Darwin=' #define _DARWIN_USE_64_BIT_INODE #include <sys/types.h> #include <sys/event.h> +#include <sys/ptrace.h> #include <sys/socket.h> #include <sys/sockio.h> #include <sys/sysctl.h> @@ -59,6 +62,7 @@ includes_Darwin=' #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/ip_mroute.h> +#include <termios.h> ' includes_FreeBSD=' @@ -68,11 +72,13 @@ includes_FreeBSD=' #include <sys/sockio.h> #include <sys/sysctl.h> #include <sys/wait.h> +#include <sys/ioctl.h> #include <net/bpf.h> #include <net/if.h> #include <net/if_types.h> #include <net/route.h> #include <netinet/in.h> +#include <termios.h> #include <netinet/ip.h> #include <netinet/ip_mroute.h> ' @@ -136,12 +142,13 @@ done $2 == "CTL_MAXNAME" || $2 ~ /^(MS|MNT)_/ || $2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ || - $2 ~ /^(O|F|FD|NAME|S|PTRACE)_/ || + $2 ~ /^(O|F|FD|NAME|S|PTRACE|PT)_/ || $2 ~ /^LINUX_REBOOT_CMD_/ || $2 ~ /^LINUX_REBOOT_MAGIC[12]$/ || $2 !~ "NLA_TYPE_MASK" && - $2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|RTM|RTN|RTPROT|RTA|RTAX|RTNH|ARPHRD)_/ || + $2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|RTM|RTN|RTPROT|RTA|RTAX|RTNH|ARPHRD|ETH_P)_/ || $2 ~ /^SIOC/ || + $2 ~ /^TIOC/ || $2 ~ /^(IFF|IFT|NET_RT|RTM|RTF|RTV|RTA|RTAX)_/ || $2 ~ /^BIOC/ || $2 !~ /^(BPF_TIMEVAL)$/ && diff --git a/src/pkg/syscall/mksyscall.pl b/src/pkg/syscall/mksyscall.pl index ecf4abdd4..ed6525972 100755 --- a/src/pkg/syscall/mksyscall.pl +++ b/src/pkg/syscall/mksyscall.pl @@ -19,11 +19,12 @@ # block, as otherwise the system call could cause all goroutines to # hang. -$cmdline = "mksyscall.pl " . join(' ', @ARGV); -$errors = 0; -$_32bit = ""; -$nacl = 0; -$plan9 = 0; +use strict; + +my $cmdline = "mksyscall.pl " . join(' ', @ARGV); +my $errors = 0; +my $_32bit = ""; +my $plan9 = 0; if($ARGV[0] eq "-b32") { $_32bit = "big-endian"; @@ -32,10 +33,6 @@ if($ARGV[0] eq "-b32") { $_32bit = "little-endian"; shift; } -if($ARGV[0] eq "-nacl") { - $nacl = 1; - shift; -} if($ARGV[0] eq "-plan9") { $plan9 = 1; shift; @@ -66,7 +63,7 @@ sub parseparam($) { return ($1, $2); } -$text = ""; +my $text = ""; while(<>) { chomp; s/\s+/ /g; diff --git a/src/pkg/syscall/mksyscall_windows.pl b/src/pkg/syscall/mksyscall_windows.pl index fb5a1272b..c3cb142ed 100755 --- a/src/pkg/syscall/mksyscall_windows.pl +++ b/src/pkg/syscall/mksyscall_windows.pl @@ -23,9 +23,13 @@ # //sys LoadLibrary(libname string) (handle uint32, errno int) [failretval==-1] = LoadLibraryA # and is [failretval==0] by default. -$cmdline = "mksyscall_windows.pl " . join(' ', @ARGV); -$errors = 0; -$_32bit = ""; +use strict; + +my $cmdline = "mksyscall_windows.pl " . join(' ', @ARGV); +my $errors = 0; +my $_32bit = ""; + +binmode STDOUT; if($ARGV[0] eq "-b32") { $_32bit = "big-endian"; @@ -60,10 +64,10 @@ sub parseparam($) { return ($1, $2); } -$text = ""; -$vars = ""; -$mods = ""; -$modnames = ""; +my $text = ""; +my $vars = ""; +my $mods = ""; +my $modnames = ""; while(<>) { chomp; s/\s+/ /g; @@ -89,7 +93,7 @@ while(<>) { if($modname eq "") { $modname = "kernel32"; } - $modvname = "mod$modname"; + my $modvname = "mod$modname"; if($modnames !~ /$modname/) { $modnames .= ".$modname"; $mods .= "\t$modvname = loadDll(\"$modname.dll\")\n"; @@ -101,7 +105,7 @@ while(<>) { } # System call pointer variable name. - $sysvarname = "proc$sysname"; + my $sysvarname = "proc$sysname"; # Returned value when failed if($failcond eq "") { @@ -109,17 +113,13 @@ while(<>) { } # Decide which version of api is used: ascii or unicode. - if($sysname !~ /W$/) { - $strconvfunc = "StringBytePtr"; - } else { - $strconvfunc = "StringToUTF16Ptr"; - } + my $strconvfunc = $sysname !~ /W$/ ? "StringBytePtr" : "StringToUTF16Ptr"; # Winapi proc address variable. $vars .= sprintf "\t%s = getSysProcAddr(%s, \"%s\")\n", $sysvarname, $modvname, $sysname; # Go function header. - my $out = join(', ', @out); + $out = join(', ', @out); if($out ne "") { $out = " ($out)"; } @@ -240,7 +240,7 @@ while(<>) { $failexpr = "$name $failcond"; } } - $failexpr =~ s/(=)([0-9A-Za-z\-+])/\1 \2/; # gofmt compatible + $failexpr =~ s/(=)([0-9A-Za-z\-+])/$1 $2/; # gofmt compatible if($name eq "errno") { # Set errno to "last error" only if returned value indicate failure $body .= "\tif $failexpr {\n"; diff --git a/src/pkg/syscall/mksysnum_darwin.pl b/src/pkg/syscall/mksysnum_darwin.pl index d078a1836..fd4375b2f 100755 --- a/src/pkg/syscall/mksysnum_darwin.pl +++ b/src/pkg/syscall/mksysnum_darwin.pl @@ -3,8 +3,9 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. # -# Generate system call table for Darwin from master list -# (for example, xnu-1228/bsd/kern/syscalls.master). +# Generate system call table for Darwin from sys/syscall.h + +use strict; my $command = "mksysnum_darwin.pl " . join(' ', @ARGV); @@ -18,18 +19,11 @@ const ( EOF while(<>){ - if(/^([0-9]+)\s+ALL\s+({ \S+\s+(\w+).*})/){ - my $num = $1; - my $proto = $2; - my $name = "SYS_$3"; + if(/^#define\s+SYS_(\w+)\s+([0-9]+)/){ + my $name = $1; + my $num = $2; $name =~ y/a-z/A-Z/; - - # There are multiple entries for enosys and nosys, so comment them out. - if($name =~ /^SYS_E?NOSYS$/){ - $name = "// $name"; - } - - print " $name = $num; // $proto\n"; + print " SYS_$name = $num;" } } diff --git a/src/pkg/syscall/mksysnum_freebsd.pl b/src/pkg/syscall/mksysnum_freebsd.pl index 03f7d9e25..54872b2f4 100755 --- a/src/pkg/syscall/mksysnum_freebsd.pl +++ b/src/pkg/syscall/mksysnum_freebsd.pl @@ -6,6 +6,8 @@ # Generate system call table for FreeBSD from master list # (for example, /usr/src/sys/kern/syscalls.master). +use strict; + my $command = "mksysnum_freebsd.pl " . join(' ', @ARGV); print <<EOF; diff --git a/src/pkg/syscall/mksysnum_linux.pl b/src/pkg/syscall/mksysnum_linux.pl index e97c87f44..ecf364188 100755 --- a/src/pkg/syscall/mksysnum_linux.pl +++ b/src/pkg/syscall/mksysnum_linux.pl @@ -3,6 +3,8 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. +use strict; + my $command = "mksysnum_linux.pl ". join(' ', @ARGV); print <<EOF; diff --git a/src/pkg/syscall/route_bsd.go b/src/pkg/syscall/route_bsd.go index 79a8793d5..7821a6d29 100644 --- a/src/pkg/syscall/route_bsd.go +++ b/src/pkg/syscall/route_bsd.go @@ -10,8 +10,6 @@ import ( "unsafe" ) -const darwinAMD64 = OS == "darwin" && ARCH == "amd64" - // Round the length of a raw sockaddr up to align it properly. func rsaAlignOf(salen int) int { salign := sizeofPtr @@ -59,7 +57,7 @@ type RoutingMessage interface { sockaddr() []Sockaddr } -const anyMessageLen = unsafe.Sizeof(anyMessage{}) +const anyMessageLen = int(unsafe.Sizeof(anyMessage{})) type anyMessage struct { Msglen uint16 diff --git a/src/pkg/syscall/sockcmsg_unix.go b/src/pkg/syscall/sockcmsg_unix.go new file mode 100644 index 000000000..f0c05eaf3 --- /dev/null +++ b/src/pkg/syscall/sockcmsg_unix.go @@ -0,0 +1,65 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Socket control messages + +package syscall + +import ( + "unsafe" +) + +// Round the length of a raw sockaddr up to align it propery. +func cmsgAlignOf(salen int) int { + salign := sizeofPtr + // NOTE: It seems like 64-bit Darwin kernel still requires 32-bit + // aligned access to BSD subsystem. + if darwinAMD64 { + salign = 4 + } + if salen == 0 { + return salign + } + return (salen + salign - 1) & ^(salign - 1) +} + +func cmsgLen(datalen int) int { + return cmsgAlignOf(SizeofCmsghdr) + datalen +} + +type SocketControlMessage struct { + Header Cmsghdr + Data []byte +} + +func ParseSocketControlMessage(buf []byte) ([]SocketControlMessage, int) { + var ( + h *Cmsghdr + dbuf []byte + e int + cmsgs []SocketControlMessage + ) + + for len(buf) >= cmsgLen(0) { + h, dbuf, e = socketControlMessageHeaderAndData(buf) + if e != 0 { + break + } + m := SocketControlMessage{} + m.Header = *h + m.Data = dbuf[:int(h.Len)-cmsgAlignOf(SizeofCmsghdr)] + cmsgs = append(cmsgs, m) + buf = buf[cmsgAlignOf(int(h.Len)):] + } + + return cmsgs, e +} + +func socketControlMessageHeaderAndData(buf []byte) (*Cmsghdr, []byte, int) { + h := (*Cmsghdr)(unsafe.Pointer(&buf[0])) + if h.Len < SizeofCmsghdr || int(h.Len) > len(buf) { + return nil, nil, EINVAL + } + return h, buf[cmsgAlignOf(SizeofCmsghdr):], 0 +} diff --git a/src/pkg/syscall/syscall.go b/src/pkg/syscall/syscall.go index 157abaa8b..9f777f59e 100644 --- a/src/pkg/syscall/syscall.go +++ b/src/pkg/syscall/syscall.go @@ -13,11 +13,6 @@ // errno is an operating system error number describing the failure. package syscall -import ( - "sync" - "unsafe" -) - // StringByteSlice returns a NUL-terminated slice of bytes // containing the text of s. func StringByteSlice(s string) []byte { @@ -33,63 +28,3 @@ func StringBytePtr(s string) *byte { return &StringByteSlice(s)[0] } // Single-word zero for use when we need a valid pointer to 0 bytes. // See mksyscall.sh. var _zero uintptr - -// Mmap manager, for use by operating system-specific implementations. - -type mmapper struct { - sync.Mutex - active map[*byte][]byte // active mappings; key is last byte in mapping - mmap func(addr, length uintptr, prot, flags, fd int, offset int64) (uintptr, int) - munmap func(addr uintptr, length uintptr) int -} - -func (m *mmapper) Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, errno int) { - if length <= 0 { - return nil, EINVAL - } - - // Map the requested memory. - addr, errno := m.mmap(0, uintptr(length), prot, flags, fd, offset) - if errno != 0 { - return nil, errno - } - - // Slice memory layout - var sl = struct { - addr uintptr - len int - cap int - }{addr, length, length} - - // Use unsafe to turn sl into a []byte. - b := *(*[]byte)(unsafe.Pointer(&sl)) - - // Register mapping in m and return it. - p := &b[cap(b)-1] - m.Lock() - defer m.Unlock() - m.active[p] = b - return b, 0 -} - -func (m *mmapper) Munmap(data []byte) (errno int) { - if len(data) == 0 || len(data) != cap(data) { - return EINVAL - } - - // Find the base of the mapping. - p := &data[cap(data)-1] - m.Lock() - defer m.Unlock() - b := m.active[p] - if b == nil || &b[0] != &data[0] { - return EINVAL - } - - // Unmap the memory and update m. - if errno := m.munmap(uintptr(unsafe.Pointer(&b[0])), uintptr(len(b))); errno != 0 { - return errno - } - m.active[p] = nil, false - return 0 -} diff --git a/src/pkg/syscall/syscall_bsd.go b/src/pkg/syscall/syscall_bsd.go index 89bcc7f0e..2df75917b 100644 --- a/src/pkg/syscall/syscall_bsd.go +++ b/src/pkg/syscall/syscall_bsd.go @@ -155,7 +155,7 @@ func Sleep(ns int64) (errno int) { //sys connect(s int, addr uintptr, addrlen _Socklen) (errno int) //sysnb socket(domain int, typ int, proto int) (fd int, errno int) //sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errno int) -//sys setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) +//sys setsockopt(s int, level int, name int, val uintptr, vallen uintptr) (errno int) //sysnb getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) //sysnb getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) //sys Shutdown(s int, how int) (errno int) @@ -400,7 +400,7 @@ func SetsockoptIPv6Mreq(fd, level, opt int, mreq *IPv6Mreq) (errno int) { } func SetsockoptString(fd, level, opt int, s string) (errno int) { - return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&[]byte(s)[0])), len(s)) + return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&[]byte(s)[0])), uintptr(len(s))) } //sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, errno int) @@ -425,6 +425,80 @@ func Sendto(fd int, p []byte, flags int, to Sockaddr) (errno int) { return sendto(fd, p, flags, ptr, n) } +//sys recvmsg(s int, msg *Msghdr, flags int) (n int, errno int) + +func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, errno int) { + var msg Msghdr + var rsa RawSockaddrAny + msg.Name = (*byte)(unsafe.Pointer(&rsa)) + msg.Namelen = uint32(SizeofSockaddrAny) + var iov Iovec + if len(p) > 0 { + iov.Base = (*byte)(unsafe.Pointer(&p[0])) + iov.SetLen(len(p)) + } + var dummy byte + if len(oob) > 0 { + // receive at least one normal byte + if len(p) == 0 { + iov.Base = &dummy + iov.SetLen(1) + } + msg.Control = (*byte)(unsafe.Pointer(&oob[0])) + msg.SetControllen(len(oob)) + } + msg.Iov = &iov + msg.Iovlen = 1 + if n, errno = recvmsg(fd, &msg, flags); errno != 0 { + return + } + oobn = int(msg.Controllen) + recvflags = int(msg.Flags) + // source address is only specified if the socket is unconnected + if rsa.Addr.Family != AF_UNSPEC { + from, errno = anyToSockaddr(&rsa) + } + return +} + +//sys sendmsg(s int, msg *Msghdr, flags int) (errno int) + +func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (errno int) { + var ptr uintptr + var salen _Socklen + if to != nil { + var err int + ptr, salen, err = to.sockaddr() + if err != 0 { + return err + } + } + var msg Msghdr + msg.Name = (*byte)(unsafe.Pointer(ptr)) + msg.Namelen = uint32(salen) + var iov Iovec + if len(p) > 0 { + iov.Base = (*byte)(unsafe.Pointer(&p[0])) + iov.SetLen(len(p)) + } + var dummy byte + if len(oob) > 0 { + // send at least one normal byte + if len(p) == 0 { + iov.Base = &dummy + iov.SetLen(1) + } + msg.Control = (*byte)(unsafe.Pointer(&oob[0])) + msg.SetControllen(len(oob)) + } + msg.Iov = &iov + msg.Iovlen = 1 + if errno = sendmsg(fd, &msg, flags); errno != 0 { + return + } + return +} + // TODO: // FreeBSD has IP_SENDIF. Darwin probably needs BSDLLCTest, see: // http://developer.apple.com/mac/library/samplecode/BSDLLCTest/index.html @@ -451,7 +525,7 @@ func Kevent(kq int, changes, events []Kevent_t, timeout *Timespec) (n int, errno // Translate "kern.hostname" to []_C_int{0,1,2,3}. func nametomib(name string) (mib []_C_int, errno int) { - const siz = uintptr(unsafe.Sizeof(mib[0])) + const siz = unsafe.Sizeof(mib[0]) // NOTE(rsc): It seems strange to set the buffer to have // size CTL_MAXNAME+2 but use only CTL_MAXNAME @@ -540,14 +614,6 @@ func Futimes(fd int, tv []Timeval) (errno int) { //sys fcntl(fd int, cmd int, arg int) (val int, errno int) -func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, errno int) { - return 0, 0, 0, nil, EAFNOSUPPORT -} - -func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (errno int) { - return EAFNOSUPPORT -} - // TODO: wrap // Acct(name nil-string) (errno int) // Gethostuuid(uuid *byte, timeout *Timespec) (errno int) diff --git a/src/pkg/syscall/syscall_darwin.go b/src/pkg/syscall/syscall_darwin.go index 9e153b73d..fabd48178 100644 --- a/src/pkg/syscall/syscall_darwin.go +++ b/src/pkg/syscall/syscall_darwin.go @@ -56,6 +56,10 @@ func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, return origlen - len(buf), count, names } +//sys ptrace(request int, pid int, addr uintptr, data uintptr) (errno int) +func PtraceAttach(pid int) (errno int) { return ptrace(PT_ATTACH, pid, 0, 0) } +func PtraceDetach(pid int) (errno int) { return ptrace(PT_DETACH, pid, 0, 0) } + // TODO func Sendfile(outfd int, infd int, offset *int64, count int) (written int, errno int) { return -1, ENOSYS diff --git a/src/pkg/syscall/syscall_darwin_386.go b/src/pkg/syscall/syscall_darwin_386.go index 5101ba6c7..d76b22844 100644 --- a/src/pkg/syscall/syscall_darwin_386.go +++ b/src/pkg/syscall/syscall_darwin_386.go @@ -40,4 +40,16 @@ func SetKevent(k *Kevent_t, fd, mode, flags int) { k.Flags = uint16(flags) } +func (iov *Iovec) SetLen(length int) { + iov.Len = uint32(length) +} + +func (msghdr *Msghdr) SetControllen(length int) { + msghdr.Controllen = uint32(length) +} + +func (cmsg *Cmsghdr) SetLen(length int) { + cmsg.Len = uint32(length) +} + func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) // sic diff --git a/src/pkg/syscall/syscall_darwin_amd64.go b/src/pkg/syscall/syscall_darwin_amd64.go index acf7a5554..ed4372304 100644 --- a/src/pkg/syscall/syscall_darwin_amd64.go +++ b/src/pkg/syscall/syscall_darwin_amd64.go @@ -39,3 +39,15 @@ func SetKevent(k *Kevent_t, fd, mode, flags int) { k.Filter = int16(mode) k.Flags = uint16(flags) } + +func (iov *Iovec) SetLen(length int) { + iov.Len = uint64(length) +} + +func (msghdr *Msghdr) SetControllen(length int) { + msghdr.Controllen = uint32(length) +} + +func (cmsg *Cmsghdr) SetLen(length int) { + cmsg.Len = uint32(length) +} diff --git a/src/pkg/syscall/syscall_freebsd_386.go b/src/pkg/syscall/syscall_freebsd_386.go index d0fa506c7..d3b5a1bfe 100644 --- a/src/pkg/syscall/syscall_freebsd_386.go +++ b/src/pkg/syscall/syscall_freebsd_386.go @@ -29,4 +29,16 @@ func SetKevent(k *Kevent_t, fd, mode, flags int) { k.Flags = uint16(flags) } +func (iov *Iovec) SetLen(length int) { + iov.Len = uint32(length) +} + +func (msghdr *Msghdr) SetControllen(length int) { + msghdr.Controllen = uint32(length) +} + +func (cmsg *Cmsghdr) SetLen(length int) { + cmsg.Len = uint32(length) +} + func Syscall9(num, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) // sic diff --git a/src/pkg/syscall/syscall_freebsd_amd64.go b/src/pkg/syscall/syscall_freebsd_amd64.go index ef5aff6ef..8c1ddf6db 100644 --- a/src/pkg/syscall/syscall_freebsd_amd64.go +++ b/src/pkg/syscall/syscall_freebsd_amd64.go @@ -28,3 +28,15 @@ func SetKevent(k *Kevent_t, fd, mode, flags int) { k.Filter = int16(mode) k.Flags = uint16(flags) } + +func (iov *Iovec) SetLen(length int) { + iov.Len = uint64(length) +} + +func (msghdr *Msghdr) SetControllen(length int) { + msghdr.Controllen = uint32(length) +} + +func (cmsg *Cmsghdr) SetLen(length int) { + cmsg.Len = uint32(length) +} diff --git a/src/pkg/syscall/syscall_linux.go b/src/pkg/syscall/syscall_linux.go index 63682d23c..1d6fc76c7 100644 --- a/src/pkg/syscall/syscall_linux.go +++ b/src/pkg/syscall/syscall_linux.go @@ -472,7 +472,7 @@ func SetsockoptIPv6Mreq(fd, level, opt int, mreq *IPv6Mreq) (errno int) { } func SetsockoptString(fd, level, opt int, s string) (errno int) { - return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&[]byte(s)[0])), len(s)) + return setsockopt(fd, level, opt, uintptr(unsafe.Pointer(&[]byte(s)[0])), uintptr(len(s))) } func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, errno int) { @@ -529,17 +529,17 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (errno int) { var ptr uintptr - var nsock _Socklen + var salen _Socklen if to != nil { var err int - ptr, nsock, err = to.sockaddr() + ptr, salen, err = to.sockaddr() if err != 0 { return err } } var msg Msghdr msg.Name = (*byte)(unsafe.Pointer(ptr)) - msg.Namelen = uint32(nsock) + msg.Namelen = uint32(salen) var iov Iovec if len(p) > 0 { iov.Base = (*byte)(unsafe.Pointer(&p[0])) diff --git a/src/pkg/syscall/syscall_linux_386.go b/src/pkg/syscall/syscall_linux_386.go index 5195179a2..44891de87 100644 --- a/src/pkg/syscall/syscall_linux_386.go +++ b/src/pkg/syscall/syscall_linux_386.go @@ -146,8 +146,8 @@ func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errn return } -func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) { - _, errno = socketcall(_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0) +func setsockopt(s int, level int, name int, val uintptr, vallen uintptr) (errno int) { + _, errno = socketcall(_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), val, vallen, 0) return } @@ -190,13 +190,13 @@ func Shutdown(s, how int) (errno int) { } func Fstatfs(fd int, buf *Statfs_t) (errno int) { - _, _, e1 := Syscall(SYS_FSTATFS64, uintptr(fd), uintptr(unsafe.Sizeof(*buf)), uintptr(unsafe.Pointer(buf))) + _, _, e1 := Syscall(SYS_FSTATFS64, uintptr(fd), unsafe.Sizeof(*buf), uintptr(unsafe.Pointer(buf))) errno = int(e1) return } func Statfs(path string, buf *Statfs_t) (errno int) { - _, _, e1 := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(unsafe.Sizeof(*buf)), uintptr(unsafe.Pointer(buf))) + _, _, e1 := Syscall(SYS_STATFS64, uintptr(unsafe.Pointer(StringBytePtr(path))), unsafe.Sizeof(*buf), uintptr(unsafe.Pointer(buf))) errno = int(e1) return } diff --git a/src/pkg/syscall/syscall_linux_amd64.go b/src/pkg/syscall/syscall_linux_amd64.go index db9524668..8b206ad0a 100644 --- a/src/pkg/syscall/syscall_linux_amd64.go +++ b/src/pkg/syscall/syscall_linux_amd64.go @@ -42,7 +42,7 @@ package syscall //sysnb getgroups(n int, list *_Gid_t) (nn int, errno int) //sysnb setgroups(n int, list *_Gid_t) (errno int) //sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errno int) -//sys setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) +//sys setsockopt(s int, level int, name int, val uintptr, vallen uintptr) (errno int) //sysnb socket(domain int, typ int, proto int) (fd int, errno int) //sysnb socketpair(domain int, typ int, proto int, fd *[2]int) (errno int) //sysnb getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) diff --git a/src/pkg/syscall/syscall_linux_arm.go b/src/pkg/syscall/syscall_linux_arm.go index 37845301f..8c03c765c 100644 --- a/src/pkg/syscall/syscall_linux_arm.go +++ b/src/pkg/syscall/syscall_linux_arm.go @@ -71,7 +71,7 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, errno int) //sysnb getgroups(n int, list *_Gid_t) (nn int, errno int) = SYS_GETGROUPS32 //sysnb setgroups(n int, list *_Gid_t) (errno int) = SYS_SETGROUPS32 //sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errno int) -//sys setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) +//sys setsockopt(s int, level int, name int, val uintptr, vallen uintptr) (errno int) //sysnb socket(domain int, typ int, proto int) (fd int, errno int) //sysnb getpeername(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) //sysnb getsockname(fd int, rsa *RawSockaddrAny, addrlen *_Socklen) (errno int) diff --git a/src/pkg/syscall/syscall_plan9.go b/src/pkg/syscall/syscall_plan9.go index 730126f23..4104050fd 100644 --- a/src/pkg/syscall/syscall_plan9.go +++ b/src/pkg/syscall/syscall_plan9.go @@ -35,7 +35,7 @@ var ( Stdout = 1 Stderr = 2 - EISDIR Error = NewError("file is a directory") + EISDIR = NewError("file is a directory") ) func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err string) @@ -200,6 +200,17 @@ type Waitmsg struct { Msg string } +func (w Waitmsg) Exited() bool { return true } +func (w Waitmsg) Signaled() bool { return false } + +func (w Waitmsg) ExitStatus() int { + if len(w.Msg) == 0 { + // a normal exit returns no message + return 0 + } + return 1 +} + //sys await(s []byte) (n int, err Error) func Await(w *Waitmsg) (err Error) { var buf [512]byte @@ -230,7 +241,7 @@ func Await(w *Waitmsg) (err Error) { w.Time[0] = uint32(atoi(f[1])) w.Time[1] = uint32(atoi(f[2])) w.Time[2] = uint32(atoi(f[3])) - w.Msg = string(f[4]) + w.Msg = cstring(f[4]) return } @@ -327,11 +338,6 @@ func Getgroups() (gids []int, err Error) { return make([]int, 0), nil } -// TODO -func Sendfile(outfd int, infd int, offset *int64, count int) (written int, errno int) { - return -1, ENOSYS -} - //sys Dup(oldfd int, newfd int) (fd int, err Error) //sys Open(path string, mode int) (fd int, err Error) //sys Create(path string, mode int, perm uint32) (fd int, err Error) diff --git a/src/pkg/syscall/syscall_unix.go b/src/pkg/syscall/syscall_unix.go index a77e40bc6..20c8a135f 100644 --- a/src/pkg/syscall/syscall_unix.go +++ b/src/pkg/syscall/syscall_unix.go @@ -4,12 +4,20 @@ package syscall +import ( + "sync" + "unsafe" +) + + var ( Stdin = 0 Stdout = 1 Stderr = 2 ) +const darwinAMD64 = OS == "darwin" && ARCH == "amd64" + func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) @@ -21,3 +29,63 @@ func Errstr(errno int) string { } return errors[errno] } + +// Mmap manager, for use by operating system-specific implementations. + +type mmapper struct { + sync.Mutex + active map[*byte][]byte // active mappings; key is last byte in mapping + mmap func(addr, length uintptr, prot, flags, fd int, offset int64) (uintptr, int) + munmap func(addr uintptr, length uintptr) int +} + +func (m *mmapper) Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, errno int) { + if length <= 0 { + return nil, EINVAL + } + + // Map the requested memory. + addr, errno := m.mmap(0, uintptr(length), prot, flags, fd, offset) + if errno != 0 { + return nil, errno + } + + // Slice memory layout + var sl = struct { + addr uintptr + len int + cap int + }{addr, length, length} + + // Use unsafe to turn sl into a []byte. + b := *(*[]byte)(unsafe.Pointer(&sl)) + + // Register mapping in m and return it. + p := &b[cap(b)-1] + m.Lock() + defer m.Unlock() + m.active[p] = b + return b, 0 +} + +func (m *mmapper) Munmap(data []byte) (errno int) { + if len(data) == 0 || len(data) != cap(data) { + return EINVAL + } + + // Find the base of the mapping. + p := &data[cap(data)-1] + m.Lock() + defer m.Unlock() + b := m.active[p] + if b == nil || &b[0] != &data[0] { + return EINVAL + } + + // Unmap the memory and update m. + if errno := m.munmap(uintptr(unsafe.Pointer(&b[0])), uintptr(len(b))); errno != 0 { + return errno + } + m.active[p] = nil, false + return 0 +} diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go index d01664d12..5b8143aac 100644 --- a/src/pkg/syscall/syscall_windows.go +++ b/src/pkg/syscall/syscall_windows.go @@ -13,6 +13,10 @@ import ( const OS = "windows" +type Handle uintptr + +const InvalidHandle = ^Handle(0) + /* small demo to detect version of windows you are running: @@ -77,10 +81,10 @@ func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2, err uintptr) func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2, err uintptr) -func loadlibraryex(filename uintptr) (handle uint32) -func getprocaddress(handle uint32, procname uintptr) (proc uintptr) +func loadlibraryex(filename uintptr) (handle uintptr) +func getprocaddress(handle uintptr, procname uintptr) (proc uintptr) -func loadDll(fname string) uint32 { +func loadDll(fname string) uintptr { m := loadlibraryex(uintptr(unsafe.Pointer(StringBytePtr(fname)))) if m == 0 { panic("syscall: could not LoadLibraryEx " + fname) @@ -88,7 +92,7 @@ func loadDll(fname string) uint32 { return m } -func getSysProcAddr(m uint32, pname string) uintptr { +func getSysProcAddr(m uintptr, pname string) uintptr { p := getprocaddress(m, uintptr(unsafe.Pointer(StringBytePtr(pname)))) if p == 0 { panic("syscall: could not GetProcAddress for " + pname) @@ -96,6 +100,8 @@ func getSysProcAddr(m uint32, pname string) uintptr { return p } +func Getpagesize() int { return 4096 } + // Converts a Go function to a function pointer conforming // to the stdcall calling convention. This is useful when // interoperating with Windows code requiring callbacks. @@ -110,22 +116,22 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, errno // windows api calls //sys GetLastError() (lasterrno int) -//sys LoadLibrary(libname string) (handle uint32, errno int) = LoadLibraryW -//sys FreeLibrary(handle uint32) (errno int) -//sys GetProcAddress(module uint32, procname string) (proc uint32, errno int) +//sys LoadLibrary(libname string) (handle Handle, errno int) = LoadLibraryW +//sys FreeLibrary(handle Handle) (errno int) +//sys GetProcAddress(module Handle, procname string) (proc Handle, errno int) //sys GetVersion() (ver uint32, errno int) //sys FormatMessage(flags uint32, msgsrc uint32, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, errno int) = FormatMessageW //sys ExitProcess(exitcode uint32) -//sys CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle int32, errno int) [failretval==-1] = CreateFileW -//sys ReadFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (errno int) -//sys WriteFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (errno int) -//sys SetFilePointer(handle int32, lowoffset int32, highoffsetptr *int32, whence uint32) (newlowoffset uint32, errno int) [failretval==0xffffffff] -//sys CloseHandle(handle int32) (errno int) -//sys GetStdHandle(stdhandle int32) (handle int32, errno int) [failretval==-1] -//sys FindFirstFile(name *uint16, data *Win32finddata) (handle int32, errno int) [failretval==-1] = FindFirstFileW -//sys FindNextFile(handle int32, data *Win32finddata) (errno int) = FindNextFileW -//sys FindClose(handle int32) (errno int) -//sys GetFileInformationByHandle(handle int32, data *ByHandleFileInformation) (errno int) +//sys CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle Handle, errno int) [failretval==InvalidHandle] = CreateFileW +//sys ReadFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (errno int) +//sys WriteFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (errno int) +//sys SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence uint32) (newlowoffset uint32, errno int) [failretval==0xffffffff] +//sys CloseHandle(handle Handle) (errno int) +//sys GetStdHandle(stdhandle int) (handle Handle, errno int) [failretval==InvalidHandle] +//sys FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, errno int) [failretval==InvalidHandle] = FindFirstFileW +//sys FindNextFile(handle Handle, data *Win32finddata) (errno int) = FindNextFileW +//sys FindClose(handle Handle) (errno int) +//sys GetFileInformationByHandle(handle Handle, data *ByHandleFileInformation) (errno int) //sys GetCurrentDirectory(buflen uint32, buf *uint16) (n uint32, errno int) = GetCurrentDirectoryW //sys SetCurrentDirectory(path *uint16) (errno int) = SetCurrentDirectoryW //sys CreateDirectory(path *uint16, sa *SecurityAttributes) (errno int) = CreateDirectoryW @@ -133,46 +139,47 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, errno //sys DeleteFile(path *uint16) (errno int) = DeleteFileW //sys MoveFile(from *uint16, to *uint16) (errno int) = MoveFileW //sys GetComputerName(buf *uint16, n *uint32) (errno int) = GetComputerNameW -//sys SetEndOfFile(handle int32) (errno int) +//sys SetEndOfFile(handle Handle) (errno int) //sys GetSystemTimeAsFileTime(time *Filetime) //sys sleep(msec uint32) = Sleep //sys GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, errno int) [failretval==0xffffffff] -//sys CreateIoCompletionPort(filehandle int32, cphandle int32, key uint32, threadcnt uint32) (handle int32, errno int) -//sys GetQueuedCompletionStatus(cphandle int32, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (errno int) -//sys CancelIo(s uint32) (errno int) +//sys CreateIoCompletionPort(filehandle Handle, cphandle Handle, key uint32, threadcnt uint32) (handle Handle, errno int) +//sys GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (errno int) +//sys CancelIo(s Handle) (errno int) //sys CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (errno int) = CreateProcessW -//sys OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle int32, errno int) -//sys TerminateProcess(handle int32, exitcode uint32) (errno int) -//sys GetExitCodeProcess(handle int32, exitcode *uint32) (errno int) +//sys OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, errno int) +//sys TerminateProcess(handle Handle, exitcode uint32) (errno int) +//sys GetExitCodeProcess(handle Handle, exitcode *uint32) (errno int) //sys GetStartupInfo(startupInfo *StartupInfo) (errno int) = GetStartupInfoW -//sys GetCurrentProcess() (pseudoHandle int32, errno int) -//sys DuplicateHandle(hSourceProcessHandle int32, hSourceHandle int32, hTargetProcessHandle int32, lpTargetHandle *int32, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (errno int) -//sys WaitForSingleObject(handle int32, waitMilliseconds uint32) (event uint32, errno int) [failretval==0xffffffff] +//sys GetCurrentProcess() (pseudoHandle Handle, errno int) +//sys DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetProcessHandle Handle, lpTargetHandle *Handle, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (errno int) +//sys WaitForSingleObject(handle Handle, waitMilliseconds uint32) (event uint32, errno int) [failretval==0xffffffff] //sys GetTempPath(buflen uint32, buf *uint16) (n uint32, errno int) = GetTempPathW -//sys CreatePipe(readhandle *uint32, writehandle *uint32, sa *SecurityAttributes, size uint32) (errno int) -//sys GetFileType(filehandle uint32) (n uint32, errno int) -//sys CryptAcquireContext(provhandle *uint32, container *uint16, provider *uint16, provtype uint32, flags uint32) (errno int) = advapi32.CryptAcquireContextW -//sys CryptReleaseContext(provhandle uint32, flags uint32) (errno int) = advapi32.CryptReleaseContext -//sys CryptGenRandom(provhandle uint32, buflen uint32, buf *byte) (errno int) = advapi32.CryptGenRandom +//sys CreatePipe(readhandle *Handle, writehandle *Handle, sa *SecurityAttributes, size uint32) (errno int) +//sys GetFileType(filehandle Handle) (n uint32, errno int) +//sys CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16, provtype uint32, flags uint32) (errno int) = advapi32.CryptAcquireContextW +//sys CryptReleaseContext(provhandle Handle, flags uint32) (errno int) = advapi32.CryptReleaseContext +//sys CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (errno int) = advapi32.CryptGenRandom //sys GetEnvironmentStrings() (envs *uint16, errno int) [failretval==nil] = kernel32.GetEnvironmentStringsW //sys FreeEnvironmentStrings(envs *uint16) (errno int) = kernel32.FreeEnvironmentStringsW //sys GetEnvironmentVariable(name *uint16, buffer *uint16, size uint32) (n uint32, errno int) = kernel32.GetEnvironmentVariableW //sys SetEnvironmentVariable(name *uint16, value *uint16) (errno int) = kernel32.SetEnvironmentVariableW -//sys SetFileTime(handle int32, ctime *Filetime, atime *Filetime, wtime *Filetime) (errno int) +//sys SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (errno int) //sys GetFileAttributes(name *uint16) (attrs uint32, errno int) [failretval==INVALID_FILE_ATTRIBUTES] = kernel32.GetFileAttributesW //sys SetFileAttributes(name *uint16, attrs uint32) (errno int) = kernel32.SetFileAttributesW //sys GetCommandLine() (cmd *uint16) = kernel32.GetCommandLineW //sys CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, errno int) [failretval==nil] = shell32.CommandLineToArgvW -//sys LocalFree(hmem uint32) (handle uint32, errno int) [failretval!=0] -//sys SetHandleInformation(handle int32, mask uint32, flags uint32) (errno int) -//sys FlushFileBuffers(handle int32) (errno int) +//sys LocalFree(hmem Handle) (handle Handle, errno int) [failretval!=0] +//sys SetHandleInformation(handle Handle, mask uint32, flags uint32) (errno int) +//sys FlushFileBuffers(handle Handle) (errno int) //sys GetFullPathName(path *uint16, buflen uint32, buf *uint16, fname **uint16) (n uint32, errno int) = kernel32.GetFullPathNameW -//sys CreateFileMapping(fhandle int32, sa *SecurityAttributes, prot uint32, maxSizeHigh uint32, maxSizeLow uint32, name *uint16) (handle int32, errno int) = kernel32.CreateFileMappingW -//sys MapViewOfFile(handle int32, access uint32, offsetHigh uint32, offsetLow uint32, length uintptr) (addr uintptr, errno int) +//sys CreateFileMapping(fhandle Handle, sa *SecurityAttributes, prot uint32, maxSizeHigh uint32, maxSizeLow uint32, name *uint16) (handle Handle, errno int) = kernel32.CreateFileMappingW +//sys MapViewOfFile(handle Handle, access uint32, offsetHigh uint32, offsetLow uint32, length uintptr) (addr uintptr, errno int) //sys UnmapViewOfFile(addr uintptr) (errno int) //sys FlushViewOfFile(addr uintptr, length uintptr) (errno int) //sys VirtualLock(addr uintptr, length uintptr) (errno int) //sys VirtualUnlock(addr uintptr, length uintptr) (errno int) +//sys TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint32, overlapped *Overlapped, transmitFileBuf *TransmitFileBuffers, flags uint32) (errno int) = wsock32.TransmitFile // syscall interface implementation for other packages @@ -204,9 +211,9 @@ func makeInheritSa() *SecurityAttributes { return &sa } -func Open(path string, mode int, perm uint32) (fd int, errno int) { +func Open(path string, mode int, perm uint32) (fd Handle, errno int) { if len(path) == 0 { - return -1, ERROR_FILE_NOT_FOUND + return InvalidHandle, ERROR_FILE_NOT_FOUND } var access uint32 switch mode & (O_RDONLY | O_WRONLY | O_RDWR) { @@ -243,12 +250,12 @@ func Open(path string, mode int, perm uint32) (fd int, errno int) { createmode = OPEN_EXISTING } h, e := CreateFile(StringToUTF16Ptr(path), access, sharemode, sa, createmode, FILE_ATTRIBUTE_NORMAL, 0) - return int(h), int(e) + return h, int(e) } -func Read(fd int, p []byte) (n int, errno int) { +func Read(fd Handle, p []byte) (n int, errno int) { var done uint32 - e := ReadFile(int32(fd), p, &done, nil) + e := ReadFile(fd, p, &done, nil) if e != 0 { if e == ERROR_BROKEN_PIPE { // NOTE(brainman): work around ERROR_BROKEN_PIPE is returned on reading EOF from stdin @@ -259,16 +266,16 @@ func Read(fd int, p []byte) (n int, errno int) { return int(done), 0 } -func Write(fd int, p []byte) (n int, errno int) { +func Write(fd Handle, p []byte) (n int, errno int) { var done uint32 - e := WriteFile(int32(fd), p, &done, nil) + e := WriteFile(fd, p, &done, nil) if e != 0 { return 0, e } return int(done), 0 } -func Seek(fd int, offset int64, whence int) (newoffset int64, errno int) { +func Seek(fd Handle, offset int64, whence int) (newoffset int64, errno int) { var w uint32 switch whence { case 0: @@ -281,19 +288,19 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, errno int) { hi := int32(offset >> 32) lo := int32(offset) // use GetFileType to check pipe, pipe can't do seek - ft, _ := GetFileType(uint32(fd)) + ft, _ := GetFileType(fd) if ft == FILE_TYPE_PIPE { return 0, EPIPE } - rlo, e := SetFilePointer(int32(fd), lo, &hi, w) + rlo, e := SetFilePointer(fd, lo, &hi, w) if e != 0 { return 0, e } return int64(hi)<<32 + int64(rlo), 0 } -func Close(fd int) (errno int) { - return CloseHandle(int32(fd)) +func Close(fd Handle) (errno int) { + return CloseHandle(fd) } var ( @@ -302,9 +309,9 @@ var ( Stderr = getStdHandle(STD_ERROR_HANDLE) ) -func getStdHandle(h int32) (fd int) { +func getStdHandle(h int) (fd Handle) { r, _ := GetStdHandle(h) - return int(r) + return r } func Stat(path string, stat *Stat_t) (errno int) { @@ -383,7 +390,7 @@ func ComputerName() (name string, errno int) { return string(utf16.Decode(b[0:n])), 0 } -func Ftruncate(fd int, length int64) (errno int) { +func Ftruncate(fd Handle, length int64) (errno int) { curoffset, e := Seek(fd, 0, 1) if e != 0 { return e @@ -393,7 +400,7 @@ func Ftruncate(fd int, length int64) (errno int) { if e != 0 { return e } - e = SetEndOfFile(int32(fd)) + e = SetEndOfFile(fd) if e != 0 { return e } @@ -412,17 +419,17 @@ func Sleep(nsec int64) (errno int) { return 0 } -func Pipe(p []int) (errno int) { +func Pipe(p []Handle) (errno int) { if len(p) != 2 { return EINVAL } - var r, w uint32 + var r, w Handle e := CreatePipe(&r, &w, makeInheritSa(), 0) if e != 0 { return e } - p[0] = int(r) - p[1] = int(w) + p[0] = r + p[1] = w return 0 } @@ -436,14 +443,14 @@ func Utimes(path string, tv []Timeval) (errno int) { if e != 0 { return e } - defer Close(int(h)) + defer Close(h) a := NsecToFiletime(tv[0].Nanoseconds()) w := NsecToFiletime(tv[1].Nanoseconds()) return SetFileTime(h, nil, &a, &w) } -func Fsync(fd int) (errno int) { - return FlushFileBuffers(int32(fd)) +func Fsync(fd Handle) (errno int) { + return FlushFileBuffers(fd) } func Chmod(path string, mode uint32) (errno int) { @@ -467,26 +474,29 @@ func Chmod(path string, mode uint32) (errno int) { //sys WSAStartup(verreq uint32, data *WSAData) (sockerrno int) = wsock32.WSAStartup //sys WSACleanup() (errno int) [failretval==-1] = wsock32.WSACleanup -//sys socket(af int32, typ int32, protocol int32) (handle int32, errno int) [failretval==-1] = wsock32.socket -//sys setsockopt(s int32, level int32, optname int32, optval *byte, optlen int32) (errno int) [failretval==-1] = wsock32.setsockopt -//sys bind(s int32, name uintptr, namelen int32) (errno int) [failretval==-1] = wsock32.bind -//sys connect(s int32, name uintptr, namelen int32) (errno int) [failretval==-1] = wsock32.connect -//sys getsockname(s int32, rsa *RawSockaddrAny, addrlen *int32) (errno int) [failretval==-1] = wsock32.getsockname -//sys getpeername(s int32, rsa *RawSockaddrAny, addrlen *int32) (errno int) [failretval==-1] = wsock32.getpeername -//sys listen(s int32, backlog int32) (errno int) [failretval==-1] = wsock32.listen -//sys shutdown(s int32, how int32) (errno int) [failretval==-1] = wsock32.shutdown -//sys Closesocket(s int32) (errno int) [failretval==-1] = wsock32.closesocket -//sys AcceptEx(ls uint32, as uint32, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (errno int) = wsock32.AcceptEx +//sys WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (errno int) [failretval==-1] = ws2_32.WSAIoctl +//sys socket(af int32, typ int32, protocol int32) (handle Handle, errno int) [failretval==InvalidHandle] = wsock32.socket +//sys setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (errno int) [failretval==-1] = wsock32.setsockopt +//sys bind(s Handle, name uintptr, namelen int32) (errno int) [failretval==-1] = wsock32.bind +//sys connect(s Handle, name uintptr, namelen int32) (errno int) [failretval==-1] = wsock32.connect +//sys getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (errno int) [failretval==-1] = wsock32.getsockname +//sys getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (errno int) [failretval==-1] = wsock32.getpeername +//sys listen(s Handle, backlog int32) (errno int) [failretval==-1] = wsock32.listen +//sys shutdown(s Handle, how int32) (errno int) [failretval==-1] = wsock32.shutdown +//sys Closesocket(s Handle) (errno int) [failretval==-1] = wsock32.closesocket +//sys AcceptEx(ls Handle, as Handle, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (errno int) = wsock32.AcceptEx //sys GetAcceptExSockaddrs(buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, lrsa **RawSockaddrAny, lrsalen *int32, rrsa **RawSockaddrAny, rrsalen *int32) = wsock32.GetAcceptExSockaddrs -//sys WSARecv(s uint32, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSARecv -//sys WSASend(s uint32, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSASend -//sys WSARecvFrom(s uint32, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSARecvFrom -//sys WSASendTo(s uint32, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSASendTo +//sys WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSARecv +//sys WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSASend +//sys WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSARecvFrom +//sys WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (errno int) [failretval==-1] = ws2_32.WSASendTo //sys GetHostByName(name string) (h *Hostent, errno int) [failretval==nil] = ws2_32.gethostbyname //sys GetServByName(name string, proto string) (s *Servent, errno int) [failretval==nil] = ws2_32.getservbyname //sys Ntohs(netshort uint16) (u uint16) = ws2_32.ntohs //sys DnsQuery(name string, qtype uint16, options uint32, extra *byte, qrs **DNSRecord, pr *byte) (status uint32) = dnsapi.DnsQuery_W //sys DnsRecordListFree(rl *DNSRecord, freetype uint32) = dnsapi.DnsRecordListFree +//sys GetIfEntry(pIfRow *MibIfRow) (errcode int) = iphlpapi.GetIfEntry +//sys GetAdaptersInfo(ai *IpAdapterInfo, ol *uint32) (errcode int) = iphlpapi.GetAdaptersInfo // For testing: clients can set this flag to force // creation of IPv6 sockets to return EAFNOSUPPORT. @@ -574,62 +584,62 @@ func (rsa *RawSockaddrAny) Sockaddr() (Sockaddr, int) { return nil, EAFNOSUPPORT } -func Socket(domain, typ, proto int) (fd, errno int) { +func Socket(domain, typ, proto int) (fd Handle, errno int) { if domain == AF_INET6 && SocketDisableIPv6 { - return -1, EAFNOSUPPORT + return InvalidHandle, EAFNOSUPPORT } h, e := socket(int32(domain), int32(typ), int32(proto)) - return int(h), int(e) + return h, int(e) } -func SetsockoptInt(fd, level, opt int, value int) (errno int) { +func SetsockoptInt(fd Handle, level, opt int, value int) (errno int) { v := int32(value) - return int(setsockopt(int32(fd), int32(level), int32(opt), (*byte)(unsafe.Pointer(&v)), int32(unsafe.Sizeof(v)))) + return int(setsockopt(fd, int32(level), int32(opt), (*byte)(unsafe.Pointer(&v)), int32(unsafe.Sizeof(v)))) } -func Bind(fd int, sa Sockaddr) (errno int) { +func Bind(fd Handle, sa Sockaddr) (errno int) { ptr, n, err := sa.sockaddr() if err != 0 { return err } - return bind(int32(fd), ptr, n) + return bind(fd, ptr, n) } -func Connect(fd int, sa Sockaddr) (errno int) { +func Connect(fd Handle, sa Sockaddr) (errno int) { ptr, n, err := sa.sockaddr() if err != 0 { return err } - return connect(int32(fd), ptr, n) + return connect(fd, ptr, n) } -func Getsockname(fd int) (sa Sockaddr, errno int) { +func Getsockname(fd Handle) (sa Sockaddr, errno int) { var rsa RawSockaddrAny l := int32(unsafe.Sizeof(rsa)) - if errno = getsockname(int32(fd), &rsa, &l); errno != 0 { + if errno = getsockname(fd, &rsa, &l); errno != 0 { return } return rsa.Sockaddr() } -func Getpeername(fd int) (sa Sockaddr, errno int) { +func Getpeername(fd Handle) (sa Sockaddr, errno int) { var rsa RawSockaddrAny l := int32(unsafe.Sizeof(rsa)) - if errno = getpeername(int32(fd), &rsa, &l); errno != 0 { + if errno = getpeername(fd, &rsa, &l); errno != 0 { return } return rsa.Sockaddr() } -func Listen(s int, n int) (errno int) { - return int(listen(int32(s), int32(n))) +func Listen(s Handle, n int) (errno int) { + return int(listen(s, int32(n))) } -func Shutdown(fd, how int) (errno int) { - return int(shutdown(int32(fd), int32(how))) +func Shutdown(fd Handle, how int) (errno int) { + return int(shutdown(fd, int32(how))) } -func WSASendto(s uint32, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to Sockaddr, overlapped *Overlapped, croutine *byte) (errno int) { +func WSASendto(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to Sockaddr, overlapped *Overlapped, croutine *byte) (errno int) { rsa, l, err := to.sockaddr() if err != 0 { return err @@ -666,10 +676,12 @@ func (w WaitStatus) TrapCause() int { return -1 } // TODO(brainman): fix all needed for net -func Accept(fd int) (nfd int, sa Sockaddr, errno int) { return 0, nil, EWINDOWS } -func Recvfrom(fd int, p []byte, flags int) (n int, from Sockaddr, errno int) { return 0, nil, EWINDOWS } -func Sendto(fd int, p []byte, flags int, to Sockaddr) (errno int) { return EWINDOWS } -func SetsockoptTimeval(fd, level, opt int, tv *Timeval) (errno int) { return EWINDOWS } +func Accept(fd Handle) (nfd Handle, sa Sockaddr, errno int) { return 0, nil, EWINDOWS } +func Recvfrom(fd Handle, p []byte, flags int) (n int, from Sockaddr, errno int) { + return 0, nil, EWINDOWS +} +func Sendto(fd Handle, p []byte, flags int, to Sockaddr) (errno int) { return EWINDOWS } +func SetsockoptTimeval(fd Handle, level, opt int, tv *Timeval) (errno int) { return EWINDOWS } type Linger struct { Onoff int32 @@ -691,25 +703,25 @@ type IPv6Mreq struct { Interface uint32 } -func SetsockoptLinger(fd, level, opt int, l *Linger) (errno int) { return EWINDOWS } -func SetsockoptIPMreq(fd, level, opt int, mreq *IPMreq) (errno int) { return EWINDOWS } -func SetsockoptIPv6Mreq(fd, level, opt int, mreq *IPv6Mreq) (errno int) { return EWINDOWS } -func BindToDevice(fd int, device string) (errno int) { return EWINDOWS } +func SetsockoptLinger(fd Handle, level, opt int, l *Linger) (errno int) { return EWINDOWS } +func SetsockoptIPMreq(fd Handle, level, opt int, mreq *IPMreq) (errno int) { return EWINDOWS } +func SetsockoptIPv6Mreq(fd Handle, level, opt int, mreq *IPv6Mreq) (errno int) { return EWINDOWS } +func BindToDevice(fd Handle, device string) (errno int) { return EWINDOWS } // TODO(brainman): fix all needed for os func Getpid() (pid int) { return -1 } func Getppid() (ppid int) { return -1 } -func Fchdir(fd int) (errno int) { return EWINDOWS } +func Fchdir(fd Handle) (errno int) { return EWINDOWS } func Link(oldpath, newpath string) (errno int) { return EWINDOWS } func Symlink(path, link string) (errno int) { return EWINDOWS } func Readlink(path string, buf []byte) (n int, errno int) { return 0, EWINDOWS } -func Fchmod(fd int, mode uint32) (errno int) { return EWINDOWS } +func Fchmod(fd Handle, mode uint32) (errno int) { return EWINDOWS } func Chown(path string, uid int, gid int) (errno int) { return EWINDOWS } func Lchown(path string, uid int, gid int) (errno int) { return EWINDOWS } -func Fchown(fd int, uid int, gid int) (errno int) { return EWINDOWS } +func Fchown(fd Handle, uid int, gid int) (errno int) { return EWINDOWS } func Getuid() (uid int) { return -1 } func Geteuid() (euid int) { return -1 } @@ -719,11 +731,11 @@ func Getgroups() (gids []int, errno int) { return nil, EWINDOWS } // TODO(brainman): fix all this meaningless code, it is here to compile exec.go -func read(fd int, buf *byte, nbuf int) (n int, errno int) { +func read(fd Handle, buf *byte, nbuf int) (n int, errno int) { return 0, EWINDOWS } -func fcntl(fd, cmd, arg int) (val int, errno int) { +func fcntl(fd Handle, cmd, arg int) (val int, errno int) { return 0, EWINDOWS } diff --git a/src/pkg/syscall/syscall_windows_386.go b/src/pkg/syscall/syscall_windows_386.go index 1ce025b31..61d2d8cb6 100644 --- a/src/pkg/syscall/syscall_windows_386.go +++ b/src/pkg/syscall/syscall_windows_386.go @@ -3,5 +3,3 @@ // license that can be found in the LICENSE file. package syscall - -func Getpagesize() int { return 4096 } diff --git a/src/pkg/syscall/syscall_windows_amd64.go b/src/pkg/syscall/syscall_windows_amd64.go new file mode 100644 index 000000000..61d2d8cb6 --- /dev/null +++ b/src/pkg/syscall/syscall_windows_amd64.go @@ -0,0 +1,5 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package syscall diff --git a/src/pkg/syscall/types_linux.c b/src/pkg/syscall/types_linux.c index ce7f96764..abb2a91a7 100644 --- a/src/pkg/syscall/types_linux.c +++ b/src/pkg/syscall/types_linux.c @@ -38,6 +38,7 @@ Input to godefs. See also mkerrors.sh and mkall.sh #include <sys/user.h> #include <sys/utsname.h> #include <sys/wait.h> +#include <linux/filter.h> #include <linux/netlink.h> #include <linux/rtnetlink.h> #include <time.h> @@ -225,6 +226,16 @@ typedef struct ifaddrmsg $IfAddrmsg; typedef struct rtmsg $RtMsg; typedef struct rtnexthop $RtNexthop; +// Linux socket filter + +enum { + $SizeofSockFilter = sizeof(struct sock_filter), + $SizeofSockFprog = sizeof(struct sock_fprog), +}; + +typedef struct sock_filter $SockFilter; +typedef struct sock_fprog $SockFprog; + // Inotify typedef struct inotify_event $InotifyEvent; diff --git a/src/pkg/syscall/types_plan9.c b/src/pkg/syscall/types_plan9.c index 6308ce08b..1da9d377c 100644 --- a/src/pkg/syscall/types_plan9.c +++ b/src/pkg/syscall/types_plan9.c @@ -19,20 +19,18 @@ enum { OREAD = 0, // open for read OWRITE = 1, // write ORDWR = 2, // read and write - - $O_RDONLY = OREAD, - $O_WRONLY = OWRITE, - $O_RDWR = ORDWR, - OEXEC = 3, // execute, == read but check execute permission OTRUNC = 16, // or'ed in (except for exec), truncate file first OCEXEC = 32, // or'ed in, close on exec - - $O_CLOEXEC = OCEXEC, - ORCLOSE = 64, // or'ed in, remove on close OEXCL = 0x1000, // or'ed in, exclusive use (create only) - $O_EXCL = OEXCL, + + $O_RDONLY = OREAD, + $O_WRONLY = OWRITE, + $O_RDWR = ORDWR, + $O_TRUNC = OTRUNC, + $O_CLOEXEC = OCEXEC, + $O_EXCL = OEXCL, $STATMAX = 65535U, $ERRMAX = 128, diff --git a/src/pkg/syscall/zerrors_darwin_386.go b/src/pkg/syscall/zerrors_darwin_386.go index 5ee64ee70..33cc7fd88 100644 --- a/src/pkg/syscall/zerrors_darwin_386.go +++ b/src/pkg/syscall/zerrors_darwin_386.go @@ -165,6 +165,13 @@ const ( EBUSY = 0x10 ECANCELED = 0x59 ECHILD = 0xa + ECHO = 0x8 + ECHOCTL = 0x40 + ECHOE = 0x2 + ECHOK = 0x4 + ECHOKE = 0x1 + ECHONL = 0x10 + ECHOPRT = 0x20 ECONNABORTED = 0x35 ECONNREFUSED = 0x3d ECONNRESET = 0x36 @@ -282,6 +289,9 @@ const ( EV_TRIGGER = 0x100 EWOULDBLOCK = 0x23 EXDEV = 0x12 + EXTA = 0x4b00 + EXTB = 0x9600 + EXTPROC = 0x800 FD_CLOEXEC = 0x1 FD_SETSIZE = 0x400 F_ADDFILESIGS = 0x3d @@ -709,6 +719,24 @@ const ( PROT_NONE = 0 PROT_READ = 0x1 PROT_WRITE = 0x2 + PT_ATTACH = 0xa + PT_ATTACHEXC = 0xe + PT_CONTINUE = 0x7 + PT_DENY_ATTACH = 0x1f + PT_DETACH = 0xb + PT_FIRSTMACH = 0x20 + PT_FORCEQUOTA = 0x1e + PT_KILL = 0x8 + PT_READ_D = 0x2 + PT_READ_I = 0x1 + PT_READ_U = 0x3 + PT_SIGEXC = 0xc + PT_STEP = 0x9 + PT_THUPDATE = 0xd + PT_TRACE_ME = 0 + PT_WRITE_D = 0x5 + PT_WRITE_I = 0x4 + PT_WRITE_U = 0x6 RTAX_AUTHOR = 0x6 RTAX_BRD = 0x7 RTAX_DST = 0 @@ -978,6 +1006,75 @@ const ( TCP_NODELAY = 0x1 TCP_NOOPT = 0x8 TCP_NOPUSH = 0x4 + TIOCCBRK = 0x2000747a + TIOCCDTR = 0x20007478 + TIOCCONS = 0x80047462 + TIOCDCDTIMESTAMP = 0x40087458 + TIOCDRAIN = 0x2000745e + TIOCDSIMICROCODE = 0x20007455 + TIOCEXCL = 0x2000740d + TIOCEXT = 0x80047460 + TIOCFLUSH = 0x80047410 + TIOCGDRAINWAIT = 0x40047456 + TIOCGETA = 0x402c7413 + TIOCGETD = 0x4004741a + TIOCGPGRP = 0x40047477 + TIOCGWINSZ = 0x40087468 + TIOCIXOFF = 0x20007480 + TIOCIXON = 0x20007481 + TIOCMBIC = 0x8004746b + TIOCMBIS = 0x8004746c + TIOCMGDTRWAIT = 0x4004745a + TIOCMGET = 0x4004746a + TIOCMODG = 0x40047403 + TIOCMODS = 0x80047404 + TIOCMSDTRWAIT = 0x8004745b + TIOCMSET = 0x8004746d + TIOCM_CAR = 0x40 + TIOCM_CD = 0x40 + TIOCM_CTS = 0x20 + TIOCM_DSR = 0x100 + TIOCM_DTR = 0x2 + TIOCM_LE = 0x1 + TIOCM_RI = 0x80 + TIOCM_RNG = 0x80 + TIOCM_RTS = 0x4 + TIOCM_SR = 0x10 + TIOCM_ST = 0x8 + TIOCNOTTY = 0x20007471 + TIOCNXCL = 0x2000740e + TIOCOUTQ = 0x40047473 + TIOCPKT = 0x80047470 + TIOCPKT_DATA = 0 + TIOCPKT_DOSTOP = 0x20 + TIOCPKT_FLUSHREAD = 0x1 + TIOCPKT_FLUSHWRITE = 0x2 + TIOCPKT_IOCTL = 0x40 + TIOCPKT_NOSTOP = 0x10 + TIOCPKT_START = 0x8 + TIOCPKT_STOP = 0x4 + TIOCPTYGNAME = 0x40807453 + TIOCPTYGRANT = 0x20007454 + TIOCPTYUNLK = 0x20007452 + TIOCREMOTE = 0x80047469 + TIOCSBRK = 0x2000747b + TIOCSCONS = 0x20007463 + TIOCSCTTY = 0x20007461 + TIOCSDRAINWAIT = 0x80047457 + TIOCSDTR = 0x20007479 + TIOCSETA = 0x802c7414 + TIOCSETAF = 0x802c7416 + TIOCSETAW = 0x802c7415 + TIOCSETD = 0x8004741b + TIOCSIG = 0x2000745f + TIOCSPGRP = 0x80047476 + TIOCSTART = 0x2000746e + TIOCSTAT = 0x20007465 + TIOCSTI = 0x80017472 + TIOCSTOP = 0x2000746f + TIOCSWINSZ = 0x80087467 + TIOCTIMESTAMP = 0x40087459 + TIOCUCNTL = 0x80047466 WCONTINUED = 0x10 WCOREFLAG = 0x80 WEXITED = 0x4 diff --git a/src/pkg/syscall/zerrors_darwin_amd64.go b/src/pkg/syscall/zerrors_darwin_amd64.go index 65a48d6e7..571ce907c 100644 --- a/src/pkg/syscall/zerrors_darwin_amd64.go +++ b/src/pkg/syscall/zerrors_darwin_amd64.go @@ -1,7 +1,7 @@ -// mkerrors.sh +// mkerrors.sh -f -m64 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT -// godefs -c gcc -gsyscall _const.c +// godefs -c gcc -f -m64 -gsyscall -f -m64 _const.c // MACHINE GENERATED - DO NOT EDIT. @@ -165,6 +165,13 @@ const ( EBUSY = 0x10 ECANCELED = 0x59 ECHILD = 0xa + ECHO = 0x8 + ECHOCTL = 0x40 + ECHOE = 0x2 + ECHOK = 0x4 + ECHOKE = 0x1 + ECHONL = 0x10 + ECHOPRT = 0x20 ECONNABORTED = 0x35 ECONNREFUSED = 0x3d ECONNRESET = 0x36 @@ -282,6 +289,9 @@ const ( EV_TRIGGER = 0x100 EWOULDBLOCK = 0x23 EXDEV = 0x12 + EXTA = 0x4b00 + EXTB = 0x9600 + EXTPROC = 0x800 FD_CLOEXEC = 0x1 FD_SETSIZE = 0x400 F_ADDFILESIGS = 0x3d @@ -709,6 +719,24 @@ const ( PROT_NONE = 0 PROT_READ = 0x1 PROT_WRITE = 0x2 + PT_ATTACH = 0xa + PT_ATTACHEXC = 0xe + PT_CONTINUE = 0x7 + PT_DENY_ATTACH = 0x1f + PT_DETACH = 0xb + PT_FIRSTMACH = 0x20 + PT_FORCEQUOTA = 0x1e + PT_KILL = 0x8 + PT_READ_D = 0x2 + PT_READ_I = 0x1 + PT_READ_U = 0x3 + PT_SIGEXC = 0xc + PT_STEP = 0x9 + PT_THUPDATE = 0xd + PT_TRACE_ME = 0 + PT_WRITE_D = 0x5 + PT_WRITE_I = 0x4 + PT_WRITE_U = 0x6 RTAX_AUTHOR = 0x6 RTAX_BRD = 0x7 RTAX_DST = 0 @@ -978,6 +1006,75 @@ const ( TCP_NODELAY = 0x1 TCP_NOOPT = 0x8 TCP_NOPUSH = 0x4 + TIOCCBRK = 0x2000747a + TIOCCDTR = 0x20007478 + TIOCCONS = 0x80047462 + TIOCDCDTIMESTAMP = 0x40107458 + TIOCDRAIN = 0x2000745e + TIOCDSIMICROCODE = 0x20007455 + TIOCEXCL = 0x2000740d + TIOCEXT = 0x80047460 + TIOCFLUSH = 0x80047410 + TIOCGDRAINWAIT = 0x40047456 + TIOCGETA = 0x40487413 + TIOCGETD = 0x4004741a + TIOCGPGRP = 0x40047477 + TIOCGWINSZ = 0x40087468 + TIOCIXOFF = 0x20007480 + TIOCIXON = 0x20007481 + TIOCMBIC = 0x8004746b + TIOCMBIS = 0x8004746c + TIOCMGDTRWAIT = 0x4004745a + TIOCMGET = 0x4004746a + TIOCMODG = 0x40047403 + TIOCMODS = 0x80047404 + TIOCMSDTRWAIT = 0x8004745b + TIOCMSET = 0x8004746d + TIOCM_CAR = 0x40 + TIOCM_CD = 0x40 + TIOCM_CTS = 0x20 + TIOCM_DSR = 0x100 + TIOCM_DTR = 0x2 + TIOCM_LE = 0x1 + TIOCM_RI = 0x80 + TIOCM_RNG = 0x80 + TIOCM_RTS = 0x4 + TIOCM_SR = 0x10 + TIOCM_ST = 0x8 + TIOCNOTTY = 0x20007471 + TIOCNXCL = 0x2000740e + TIOCOUTQ = 0x40047473 + TIOCPKT = 0x80047470 + TIOCPKT_DATA = 0 + TIOCPKT_DOSTOP = 0x20 + TIOCPKT_FLUSHREAD = 0x1 + TIOCPKT_FLUSHWRITE = 0x2 + TIOCPKT_IOCTL = 0x40 + TIOCPKT_NOSTOP = 0x10 + TIOCPKT_START = 0x8 + TIOCPKT_STOP = 0x4 + TIOCPTYGNAME = 0x40807453 + TIOCPTYGRANT = 0x20007454 + TIOCPTYUNLK = 0x20007452 + TIOCREMOTE = 0x80047469 + TIOCSBRK = 0x2000747b + TIOCSCONS = 0x20007463 + TIOCSCTTY = 0x20007461 + TIOCSDRAINWAIT = 0x80047457 + TIOCSDTR = 0x20007479 + TIOCSETA = 0x80487414 + TIOCSETAF = 0x80487416 + TIOCSETAW = 0x80487415 + TIOCSETD = 0x8004741b + TIOCSIG = 0x2000745f + TIOCSPGRP = 0x80047476 + TIOCSTART = 0x2000746e + TIOCSTAT = 0x20007465 + TIOCSTI = 0x80017472 + TIOCSTOP = 0x2000746f + TIOCSWINSZ = 0x80087467 + TIOCTIMESTAMP = 0x40107459 + TIOCUCNTL = 0x80047466 WCONTINUED = 0x10 WCOREFLAG = 0x80 WEXITED = 0x4 diff --git a/src/pkg/syscall/zerrors_freebsd_386.go b/src/pkg/syscall/zerrors_freebsd_386.go index 52e42487b..d045cab08 100644 --- a/src/pkg/syscall/zerrors_freebsd_386.go +++ b/src/pkg/syscall/zerrors_freebsd_386.go @@ -327,6 +327,13 @@ const ( EBUSY = 0x10 ECANCELED = 0x55 ECHILD = 0xa + ECHO = 0x8 + ECHOCTL = 0x40 + ECHOE = 0x2 + ECHOK = 0x4 + ECHOKE = 0x1 + ECHONL = 0x10 + ECHOPRT = 0x20 ECONNABORTED = 0x35 ECONNREFUSED = 0x3d ECONNRESET = 0x36 @@ -432,6 +439,9 @@ const ( EV_SYSFLAGS = 0xf000 EWOULDBLOCK = 0x23 EXDEV = 0x12 + EXTA = 0x4b00 + EXTB = 0x9600 + EXTPROC = 0x800 FD_CLOEXEC = 0x1 FD_SETSIZE = 0x400 F_CANCEL = 0x5 @@ -1224,6 +1234,68 @@ const ( TCP_NODELAY = 0x1 TCP_NOOPT = 0x8 TCP_NOPUSH = 0x4 + TIOCCBRK = 0x2000747a + TIOCCDTR = 0x20007478 + TIOCCONS = 0x80047462 + TIOCDRAIN = 0x2000745e + TIOCEXCL = 0x2000740d + TIOCEXT = 0x80047460 + TIOCFLUSH = 0x80047410 + TIOCGDRAINWAIT = 0x40047456 + TIOCGETA = 0x402c7413 + TIOCGETD = 0x4004741a + TIOCGPGRP = 0x40047477 + TIOCGPTN = 0x4004740f + TIOCGSID = 0x40047463 + TIOCGWINSZ = 0x40087468 + TIOCMBIC = 0x8004746b + TIOCMBIS = 0x8004746c + TIOCMGDTRWAIT = 0x4004745a + TIOCMGET = 0x4004746a + TIOCMSDTRWAIT = 0x8004745b + TIOCMSET = 0x8004746d + TIOCM_CAR = 0x40 + TIOCM_CD = 0x40 + TIOCM_CTS = 0x20 + TIOCM_DCD = 0x40 + TIOCM_DSR = 0x100 + TIOCM_DTR = 0x2 + TIOCM_LE = 0x1 + TIOCM_RI = 0x80 + TIOCM_RNG = 0x80 + TIOCM_RTS = 0x4 + TIOCM_SR = 0x10 + TIOCM_ST = 0x8 + TIOCNOTTY = 0x20007471 + TIOCNXCL = 0x2000740e + TIOCOUTQ = 0x40047473 + TIOCPKT = 0x80047470 + TIOCPKT_DATA = 0 + TIOCPKT_DOSTOP = 0x20 + TIOCPKT_FLUSHREAD = 0x1 + TIOCPKT_FLUSHWRITE = 0x2 + TIOCPKT_IOCTL = 0x40 + TIOCPKT_NOSTOP = 0x10 + TIOCPKT_START = 0x8 + TIOCPKT_STOP = 0x4 + TIOCPTMASTER = 0x2000741c + TIOCSBRK = 0x2000747b + TIOCSCTTY = 0x20007461 + TIOCSDRAINWAIT = 0x80047457 + TIOCSDTR = 0x20007479 + TIOCSETA = 0x802c7414 + TIOCSETAF = 0x802c7416 + TIOCSETAW = 0x802c7415 + TIOCSETD = 0x8004741b + TIOCSIG = 0x2004745f + TIOCSPGRP = 0x80047476 + TIOCSTART = 0x2000746e + TIOCSTAT = 0x20007465 + TIOCSTI = 0x80017472 + TIOCSTOP = 0x2000746f + TIOCSWINSZ = 0x80087467 + TIOCTIMESTAMP = 0x40087459 + TIOCUCNTL = 0x80047466 WCONTINUED = 0x4 WCOREFLAG = 0x80 WLINUXCLONE = 0x80000000 diff --git a/src/pkg/syscall/zerrors_freebsd_amd64.go b/src/pkg/syscall/zerrors_freebsd_amd64.go index 9b632ba93..871b3818c 100644 --- a/src/pkg/syscall/zerrors_freebsd_amd64.go +++ b/src/pkg/syscall/zerrors_freebsd_amd64.go @@ -327,6 +327,13 @@ const ( EBUSY = 0x10 ECANCELED = 0x55 ECHILD = 0xa + ECHO = 0x8 + ECHOCTL = 0x40 + ECHOE = 0x2 + ECHOK = 0x4 + ECHOKE = 0x1 + ECHONL = 0x10 + ECHOPRT = 0x20 ECONNABORTED = 0x35 ECONNREFUSED = 0x3d ECONNRESET = 0x36 @@ -432,6 +439,9 @@ const ( EV_SYSFLAGS = 0xf000 EWOULDBLOCK = 0x23 EXDEV = 0x12 + EXTA = 0x4b00 + EXTB = 0x9600 + EXTPROC = 0x800 FD_CLOEXEC = 0x1 FD_SETSIZE = 0x400 F_CANCEL = 0x5 @@ -1224,6 +1234,68 @@ const ( TCP_NODELAY = 0x1 TCP_NOOPT = 0x8 TCP_NOPUSH = 0x4 + TIOCCBRK = 0x2000747a + TIOCCDTR = 0x20007478 + TIOCCONS = 0x80047462 + TIOCDRAIN = 0x2000745e + TIOCEXCL = 0x2000740d + TIOCEXT = 0x80047460 + TIOCFLUSH = 0x80047410 + TIOCGDRAINWAIT = 0x40047456 + TIOCGETA = 0x402c7413 + TIOCGETD = 0x4004741a + TIOCGPGRP = 0x40047477 + TIOCGPTN = 0x4004740f + TIOCGSID = 0x40047463 + TIOCGWINSZ = 0x40087468 + TIOCMBIC = 0x8004746b + TIOCMBIS = 0x8004746c + TIOCMGDTRWAIT = 0x4004745a + TIOCMGET = 0x4004746a + TIOCMSDTRWAIT = 0x8004745b + TIOCMSET = 0x8004746d + TIOCM_CAR = 0x40 + TIOCM_CD = 0x40 + TIOCM_CTS = 0x20 + TIOCM_DCD = 0x40 + TIOCM_DSR = 0x100 + TIOCM_DTR = 0x2 + TIOCM_LE = 0x1 + TIOCM_RI = 0x80 + TIOCM_RNG = 0x80 + TIOCM_RTS = 0x4 + TIOCM_SR = 0x10 + TIOCM_ST = 0x8 + TIOCNOTTY = 0x20007471 + TIOCNXCL = 0x2000740e + TIOCOUTQ = 0x40047473 + TIOCPKT = 0x80047470 + TIOCPKT_DATA = 0 + TIOCPKT_DOSTOP = 0x20 + TIOCPKT_FLUSHREAD = 0x1 + TIOCPKT_FLUSHWRITE = 0x2 + TIOCPKT_IOCTL = 0x40 + TIOCPKT_NOSTOP = 0x10 + TIOCPKT_START = 0x8 + TIOCPKT_STOP = 0x4 + TIOCPTMASTER = 0x2000741c + TIOCSBRK = 0x2000747b + TIOCSCTTY = 0x20007461 + TIOCSDRAINWAIT = 0x80047457 + TIOCSDTR = 0x20007479 + TIOCSETA = 0x802c7414 + TIOCSETAF = 0x802c7416 + TIOCSETAW = 0x802c7415 + TIOCSETD = 0x8004741b + TIOCSIG = 0x2004745f + TIOCSPGRP = 0x80047476 + TIOCSTART = 0x2000746e + TIOCSTAT = 0x20007465 + TIOCSTI = 0x80017472 + TIOCSTOP = 0x2000746f + TIOCSWINSZ = 0x80087467 + TIOCTIMESTAMP = 0x40107459 + TIOCUCNTL = 0x80047466 WCONTINUED = 0x4 WCOREFLAG = 0x80 WLINUXCLONE = 0x80000000 diff --git a/src/pkg/syscall/zerrors_linux_386.go b/src/pkg/syscall/zerrors_linux_386.go index 73caed44c..298754052 100644 --- a/src/pkg/syscall/zerrors_linux_386.go +++ b/src/pkg/syscall/zerrors_linux_386.go @@ -106,6 +106,46 @@ const ( ARPHRD_TUNNEL6 = 0x301 ARPHRD_VOID = 0xffff ARPHRD_X25 = 0x10f + BPF_A = 0x10 + BPF_ABS = 0x20 + BPF_ADD = 0 + BPF_ALU = 0x4 + BPF_AND = 0x50 + BPF_B = 0x10 + BPF_DIV = 0x30 + BPF_H = 0x8 + BPF_IMM = 0 + BPF_IND = 0x40 + BPF_JA = 0 + BPF_JEQ = 0x10 + BPF_JGE = 0x30 + BPF_JGT = 0x20 + BPF_JMP = 0x5 + BPF_JSET = 0x40 + BPF_K = 0 + BPF_LD = 0 + BPF_LDX = 0x1 + BPF_LEN = 0x80 + BPF_LSH = 0x60 + BPF_MAJOR_VERSION = 0x1 + BPF_MAXINSNS = 0x1000 + BPF_MEM = 0x60 + BPF_MEMWORDS = 0x10 + BPF_MINOR_VERSION = 0x1 + BPF_MISC = 0x7 + BPF_MSH = 0xa0 + BPF_MUL = 0x20 + BPF_NEG = 0x80 + BPF_OR = 0x40 + BPF_RET = 0x6 + BPF_RSH = 0x70 + BPF_ST = 0x2 + BPF_STX = 0x3 + BPF_SUB = 0x10 + BPF_TAX = 0 + BPF_TXA = 0x80 + BPF_W = 0 + BPF_X = 0x8 DT_BLK = 0x6 DT_CHR = 0x2 DT_DIR = 0x4 @@ -256,6 +296,68 @@ const ( ESRMNT = 0x45 ESTALE = 0x74 ESTRPIPE = 0x56 + ETH_P_1588 = 0x88f7 + ETH_P_8021Q = 0x8100 + ETH_P_802_2 = 0x4 + ETH_P_802_3 = 0x1 + ETH_P_AARP = 0x80f3 + ETH_P_ALL = 0x3 + ETH_P_AOE = 0x88a2 + ETH_P_ARCNET = 0x1a + ETH_P_ARP = 0x806 + ETH_P_ATALK = 0x809b + ETH_P_ATMFATE = 0x8884 + ETH_P_ATMMPOA = 0x884c + ETH_P_AX25 = 0x2 + ETH_P_BPQ = 0x8ff + ETH_P_CAN = 0xc + ETH_P_CONTROL = 0x16 + ETH_P_CUST = 0x6006 + ETH_P_DDCMP = 0x6 + ETH_P_DEC = 0x6000 + ETH_P_DIAG = 0x6005 + ETH_P_DNA_DL = 0x6001 + ETH_P_DNA_RC = 0x6002 + ETH_P_DNA_RT = 0x6003 + ETH_P_DSA = 0x1b + ETH_P_ECONET = 0x18 + ETH_P_EDSA = 0xdada + ETH_P_FCOE = 0x8906 + ETH_P_FIP = 0x8914 + ETH_P_HDLC = 0x19 + ETH_P_IEEE802154 = 0xf6 + ETH_P_IEEEPUP = 0xa00 + ETH_P_IEEEPUPAT = 0xa01 + ETH_P_IP = 0x800 + ETH_P_IPV6 = 0x86dd + ETH_P_IPX = 0x8137 + ETH_P_IRDA = 0x17 + ETH_P_LAT = 0x6004 + ETH_P_LOCALTALK = 0x9 + ETH_P_LOOP = 0x60 + ETH_P_MOBITEX = 0x15 + ETH_P_MPLS_MC = 0x8848 + ETH_P_MPLS_UC = 0x8847 + ETH_P_PAE = 0x888e + ETH_P_PAUSE = 0x8808 + ETH_P_PHONET = 0xf5 + ETH_P_PPPTALK = 0x10 + ETH_P_PPP_DISC = 0x8863 + ETH_P_PPP_MP = 0x8 + ETH_P_PPP_SES = 0x8864 + ETH_P_PUP = 0x200 + ETH_P_PUPAT = 0x201 + ETH_P_RARP = 0x8035 + ETH_P_SCA = 0x6007 + ETH_P_SLOW = 0x8809 + ETH_P_SNAP = 0x5 + ETH_P_TEB = 0x6558 + ETH_P_TIPC = 0x88ca + ETH_P_TRAILER = 0x1c + ETH_P_TR_802_2 = 0x11 + ETH_P_WAN_PPP = 0x7 + ETH_P_WCCP = 0x883e + ETH_P_X25 = 0x805 ETIME = 0x3e ETIMEDOUT = 0x6e ETOOMANYREFS = 0x6d @@ -1002,6 +1104,69 @@ const ( TCP_QUICKACK = 0xc TCP_SYNCNT = 0x7 TCP_WINDOW_CLAMP = 0xa + TIOCCBRK = 0x5428 + TIOCCONS = 0x541d + TIOCEXCL = 0x540c + TIOCGETD = 0x5424 + TIOCGHAYESESP = 0x545e + TIOCGICOUNT = 0x545d + TIOCGLCKTRMIOS = 0x5456 + TIOCGPGRP = 0x540f + TIOCGPTN = 0x80045430 + TIOCGRS485 = 0x542e + TIOCGSERIAL = 0x541e + TIOCGSID = 0x5429 + TIOCGSOFTCAR = 0x5419 + TIOCGWINSZ = 0x5413 + TIOCINQ = 0x541b + TIOCLINUX = 0x541c + TIOCMBIC = 0x5417 + TIOCMBIS = 0x5416 + TIOCMGET = 0x5415 + TIOCMIWAIT = 0x545c + TIOCMSET = 0x5418 + TIOCM_CAR = 0x40 + TIOCM_CD = 0x40 + TIOCM_CTS = 0x20 + TIOCM_DSR = 0x100 + TIOCM_DTR = 0x2 + TIOCM_LE = 0x1 + TIOCM_RI = 0x80 + TIOCM_RNG = 0x80 + TIOCM_RTS = 0x4 + TIOCM_SR = 0x10 + TIOCM_ST = 0x8 + TIOCNOTTY = 0x5422 + TIOCNXCL = 0x540d + TIOCOUTQ = 0x5411 + TIOCPKT = 0x5420 + TIOCPKT_DATA = 0 + TIOCPKT_DOSTOP = 0x20 + TIOCPKT_FLUSHREAD = 0x1 + TIOCPKT_FLUSHWRITE = 0x2 + TIOCPKT_NOSTOP = 0x10 + TIOCPKT_START = 0x8 + TIOCPKT_STOP = 0x4 + TIOCSBRK = 0x5427 + TIOCSCTTY = 0x540e + TIOCSERCONFIG = 0x5453 + TIOCSERGETLSR = 0x5459 + TIOCSERGETMULTI = 0x545a + TIOCSERGSTRUCT = 0x5458 + TIOCSERGWILD = 0x5454 + TIOCSERSETMULTI = 0x545b + TIOCSERSWILD = 0x5455 + TIOCSER_TEMT = 0x1 + TIOCSETD = 0x5423 + TIOCSHAYESESP = 0x545f + TIOCSLCKTRMIOS = 0x5457 + TIOCSPGRP = 0x5410 + TIOCSPTLCK = 0x40045431 + TIOCSRS485 = 0x542f + TIOCSSERIAL = 0x541f + TIOCSSOFTCAR = 0x541a + TIOCSTI = 0x5412 + TIOCSWINSZ = 0x5414 TUNGETFEATURES = 0x800454cf TUNGETIFF = 0x800454d2 TUNGETSNDBUF = 0x800454d3 diff --git a/src/pkg/syscall/zerrors_linux_amd64.go b/src/pkg/syscall/zerrors_linux_amd64.go index 89260740d..728eefdde 100644 --- a/src/pkg/syscall/zerrors_linux_amd64.go +++ b/src/pkg/syscall/zerrors_linux_amd64.go @@ -106,6 +106,46 @@ const ( ARPHRD_TUNNEL6 = 0x301 ARPHRD_VOID = 0xffff ARPHRD_X25 = 0x10f + BPF_A = 0x10 + BPF_ABS = 0x20 + BPF_ADD = 0 + BPF_ALU = 0x4 + BPF_AND = 0x50 + BPF_B = 0x10 + BPF_DIV = 0x30 + BPF_H = 0x8 + BPF_IMM = 0 + BPF_IND = 0x40 + BPF_JA = 0 + BPF_JEQ = 0x10 + BPF_JGE = 0x30 + BPF_JGT = 0x20 + BPF_JMP = 0x5 + BPF_JSET = 0x40 + BPF_K = 0 + BPF_LD = 0 + BPF_LDX = 0x1 + BPF_LEN = 0x80 + BPF_LSH = 0x60 + BPF_MAJOR_VERSION = 0x1 + BPF_MAXINSNS = 0x1000 + BPF_MEM = 0x60 + BPF_MEMWORDS = 0x10 + BPF_MINOR_VERSION = 0x1 + BPF_MISC = 0x7 + BPF_MSH = 0xa0 + BPF_MUL = 0x20 + BPF_NEG = 0x80 + BPF_OR = 0x40 + BPF_RET = 0x6 + BPF_RSH = 0x70 + BPF_ST = 0x2 + BPF_STX = 0x3 + BPF_SUB = 0x10 + BPF_TAX = 0 + BPF_TXA = 0x80 + BPF_W = 0 + BPF_X = 0x8 DT_BLK = 0x6 DT_CHR = 0x2 DT_DIR = 0x4 @@ -256,6 +296,68 @@ const ( ESRMNT = 0x45 ESTALE = 0x74 ESTRPIPE = 0x56 + ETH_P_1588 = 0x88f7 + ETH_P_8021Q = 0x8100 + ETH_P_802_2 = 0x4 + ETH_P_802_3 = 0x1 + ETH_P_AARP = 0x80f3 + ETH_P_ALL = 0x3 + ETH_P_AOE = 0x88a2 + ETH_P_ARCNET = 0x1a + ETH_P_ARP = 0x806 + ETH_P_ATALK = 0x809b + ETH_P_ATMFATE = 0x8884 + ETH_P_ATMMPOA = 0x884c + ETH_P_AX25 = 0x2 + ETH_P_BPQ = 0x8ff + ETH_P_CAN = 0xc + ETH_P_CONTROL = 0x16 + ETH_P_CUST = 0x6006 + ETH_P_DDCMP = 0x6 + ETH_P_DEC = 0x6000 + ETH_P_DIAG = 0x6005 + ETH_P_DNA_DL = 0x6001 + ETH_P_DNA_RC = 0x6002 + ETH_P_DNA_RT = 0x6003 + ETH_P_DSA = 0x1b + ETH_P_ECONET = 0x18 + ETH_P_EDSA = 0xdada + ETH_P_FCOE = 0x8906 + ETH_P_FIP = 0x8914 + ETH_P_HDLC = 0x19 + ETH_P_IEEE802154 = 0xf6 + ETH_P_IEEEPUP = 0xa00 + ETH_P_IEEEPUPAT = 0xa01 + ETH_P_IP = 0x800 + ETH_P_IPV6 = 0x86dd + ETH_P_IPX = 0x8137 + ETH_P_IRDA = 0x17 + ETH_P_LAT = 0x6004 + ETH_P_LOCALTALK = 0x9 + ETH_P_LOOP = 0x60 + ETH_P_MOBITEX = 0x15 + ETH_P_MPLS_MC = 0x8848 + ETH_P_MPLS_UC = 0x8847 + ETH_P_PAE = 0x888e + ETH_P_PAUSE = 0x8808 + ETH_P_PHONET = 0xf5 + ETH_P_PPPTALK = 0x10 + ETH_P_PPP_DISC = 0x8863 + ETH_P_PPP_MP = 0x8 + ETH_P_PPP_SES = 0x8864 + ETH_P_PUP = 0x200 + ETH_P_PUPAT = 0x201 + ETH_P_RARP = 0x8035 + ETH_P_SCA = 0x6007 + ETH_P_SLOW = 0x8809 + ETH_P_SNAP = 0x5 + ETH_P_TEB = 0x6558 + ETH_P_TIPC = 0x88ca + ETH_P_TRAILER = 0x1c + ETH_P_TR_802_2 = 0x11 + ETH_P_WAN_PPP = 0x7 + ETH_P_WCCP = 0x883e + ETH_P_X25 = 0x805 ETIME = 0x3e ETIMEDOUT = 0x6e ETOOMANYREFS = 0x6d @@ -1003,6 +1105,69 @@ const ( TCP_QUICKACK = 0xc TCP_SYNCNT = 0x7 TCP_WINDOW_CLAMP = 0xa + TIOCCBRK = 0x5428 + TIOCCONS = 0x541d + TIOCEXCL = 0x540c + TIOCGETD = 0x5424 + TIOCGHAYESESP = 0x545e + TIOCGICOUNT = 0x545d + TIOCGLCKTRMIOS = 0x5456 + TIOCGPGRP = 0x540f + TIOCGPTN = 0x80045430 + TIOCGRS485 = 0x542e + TIOCGSERIAL = 0x541e + TIOCGSID = 0x5429 + TIOCGSOFTCAR = 0x5419 + TIOCGWINSZ = 0x5413 + TIOCINQ = 0x541b + TIOCLINUX = 0x541c + TIOCMBIC = 0x5417 + TIOCMBIS = 0x5416 + TIOCMGET = 0x5415 + TIOCMIWAIT = 0x545c + TIOCMSET = 0x5418 + TIOCM_CAR = 0x40 + TIOCM_CD = 0x40 + TIOCM_CTS = 0x20 + TIOCM_DSR = 0x100 + TIOCM_DTR = 0x2 + TIOCM_LE = 0x1 + TIOCM_RI = 0x80 + TIOCM_RNG = 0x80 + TIOCM_RTS = 0x4 + TIOCM_SR = 0x10 + TIOCM_ST = 0x8 + TIOCNOTTY = 0x5422 + TIOCNXCL = 0x540d + TIOCOUTQ = 0x5411 + TIOCPKT = 0x5420 + TIOCPKT_DATA = 0 + TIOCPKT_DOSTOP = 0x20 + TIOCPKT_FLUSHREAD = 0x1 + TIOCPKT_FLUSHWRITE = 0x2 + TIOCPKT_NOSTOP = 0x10 + TIOCPKT_START = 0x8 + TIOCPKT_STOP = 0x4 + TIOCSBRK = 0x5427 + TIOCSCTTY = 0x540e + TIOCSERCONFIG = 0x5453 + TIOCSERGETLSR = 0x5459 + TIOCSERGETMULTI = 0x545a + TIOCSERGSTRUCT = 0x5458 + TIOCSERGWILD = 0x5454 + TIOCSERSETMULTI = 0x545b + TIOCSERSWILD = 0x5455 + TIOCSER_TEMT = 0x1 + TIOCSETD = 0x5423 + TIOCSHAYESESP = 0x545f + TIOCSLCKTRMIOS = 0x5457 + TIOCSPGRP = 0x5410 + TIOCSPTLCK = 0x40045431 + TIOCSRS485 = 0x542f + TIOCSSERIAL = 0x541f + TIOCSSOFTCAR = 0x541a + TIOCSTI = 0x5412 + TIOCSWINSZ = 0x5414 TUNGETFEATURES = 0x800454cf TUNGETIFF = 0x800454d2 TUNGETSNDBUF = 0x800454d3 diff --git a/src/pkg/syscall/zerrors_linux_arm.go b/src/pkg/syscall/zerrors_linux_arm.go index 50cdaf18a..7d572712f 100644 --- a/src/pkg/syscall/zerrors_linux_arm.go +++ b/src/pkg/syscall/zerrors_linux_arm.go @@ -106,6 +106,46 @@ const ( ARPHRD_TUNNEL6 = 0x301 ARPHRD_VOID = 0xffff ARPHRD_X25 = 0x10f + BPF_A = 0x10 + BPF_ABS = 0x20 + BPF_ADD = 0 + BPF_ALU = 0x4 + BPF_AND = 0x50 + BPF_B = 0x10 + BPF_DIV = 0x30 + BPF_H = 0x8 + BPF_IMM = 0 + BPF_IND = 0x40 + BPF_JA = 0 + BPF_JEQ = 0x10 + BPF_JGE = 0x30 + BPF_JGT = 0x20 + BPF_JMP = 0x5 + BPF_JSET = 0x40 + BPF_K = 0 + BPF_LD = 0 + BPF_LDX = 0x1 + BPF_LEN = 0x80 + BPF_LSH = 0x60 + BPF_MAJOR_VERSION = 0x1 + BPF_MAXINSNS = 0x1000 + BPF_MEM = 0x60 + BPF_MEMWORDS = 0x10 + BPF_MINOR_VERSION = 0x1 + BPF_MISC = 0x7 + BPF_MSH = 0xa0 + BPF_MUL = 0x20 + BPF_NEG = 0x80 + BPF_OR = 0x40 + BPF_RET = 0x6 + BPF_RSH = 0x70 + BPF_ST = 0x2 + BPF_STX = 0x3 + BPF_SUB = 0x10 + BPF_TAX = 0 + BPF_TXA = 0x80 + BPF_W = 0 + BPF_X = 0x8 DT_BLK = 0x6 DT_CHR = 0x2 DT_DIR = 0x4 @@ -258,6 +298,68 @@ const ( ESRMNT = 0x45 ESTALE = 0x74 ESTRPIPE = 0x56 + ETH_P_1588 = 0x88f7 + ETH_P_8021Q = 0x8100 + ETH_P_802_2 = 0x4 + ETH_P_802_3 = 0x1 + ETH_P_AARP = 0x80f3 + ETH_P_ALL = 0x3 + ETH_P_AOE = 0x88a2 + ETH_P_ARCNET = 0x1a + ETH_P_ARP = 0x806 + ETH_P_ATALK = 0x809b + ETH_P_ATMFATE = 0x8884 + ETH_P_ATMMPOA = 0x884c + ETH_P_AX25 = 0x2 + ETH_P_BPQ = 0x8ff + ETH_P_CAN = 0xc + ETH_P_CONTROL = 0x16 + ETH_P_CUST = 0x6006 + ETH_P_DDCMP = 0x6 + ETH_P_DEC = 0x6000 + ETH_P_DIAG = 0x6005 + ETH_P_DNA_DL = 0x6001 + ETH_P_DNA_RC = 0x6002 + ETH_P_DNA_RT = 0x6003 + ETH_P_DSA = 0x1b + ETH_P_ECONET = 0x18 + ETH_P_EDSA = 0xdada + ETH_P_FCOE = 0x8906 + ETH_P_FIP = 0x8914 + ETH_P_HDLC = 0x19 + ETH_P_IEEE802154 = 0xf6 + ETH_P_IEEEPUP = 0xa00 + ETH_P_IEEEPUPAT = 0xa01 + ETH_P_IP = 0x800 + ETH_P_IPV6 = 0x86dd + ETH_P_IPX = 0x8137 + ETH_P_IRDA = 0x17 + ETH_P_LAT = 0x6004 + ETH_P_LOCALTALK = 0x9 + ETH_P_LOOP = 0x60 + ETH_P_MOBITEX = 0x15 + ETH_P_MPLS_MC = 0x8848 + ETH_P_MPLS_UC = 0x8847 + ETH_P_PAE = 0x888e + ETH_P_PAUSE = 0x8808 + ETH_P_PHONET = 0xf5 + ETH_P_PPPTALK = 0x10 + ETH_P_PPP_DISC = 0x8863 + ETH_P_PPP_MP = 0x8 + ETH_P_PPP_SES = 0x8864 + ETH_P_PUP = 0x200 + ETH_P_PUPAT = 0x201 + ETH_P_RARP = 0x8035 + ETH_P_SCA = 0x6007 + ETH_P_SLOW = 0x8809 + ETH_P_SNAP = 0x5 + ETH_P_TEB = 0x6558 + ETH_P_TIPC = 0x88ca + ETH_P_TRAILER = 0x1c + ETH_P_TR_802_2 = 0x11 + ETH_P_WAN_PPP = 0x7 + ETH_P_WCCP = 0x883e + ETH_P_X25 = 0x805 ETIME = 0x3e ETIMEDOUT = 0x6e ETOOMANYREFS = 0x6d @@ -696,6 +798,9 @@ const ( PTRACE_SINGLESTEP = 0x9 PTRACE_SYSCALL = 0x18 PTRACE_TRACEME = 0 + PT_DATA_ADDR = 0x10004 + PT_TEXT_ADDR = 0x10000 + PT_TEXT_END_ADDR = 0x10008 RTAX_ADVMSS = 0x8 RTAX_CWND = 0x7 RTAX_FEATURES = 0xc @@ -993,6 +1098,65 @@ const ( TCP_QUICKACK = 0xc TCP_SYNCNT = 0x7 TCP_WINDOW_CLAMP = 0xa + TIOCCBRK = 0x5428 + TIOCCONS = 0x541d + TIOCEXCL = 0x540c + TIOCGETD = 0x5424 + TIOCGICOUNT = 0x545d + TIOCGLCKTRMIOS = 0x5456 + TIOCGPGRP = 0x540f + TIOCGPTN = 0x80045430 + TIOCGSERIAL = 0x541e + TIOCGSID = 0x5429 + TIOCGSOFTCAR = 0x5419 + TIOCGWINSZ = 0x5413 + TIOCINQ = 0x541b + TIOCLINUX = 0x541c + TIOCMBIC = 0x5417 + TIOCMBIS = 0x5416 + TIOCMGET = 0x5415 + TIOCMIWAIT = 0x545c + TIOCMSET = 0x5418 + TIOCM_CAR = 0x40 + TIOCM_CD = 0x40 + TIOCM_CTS = 0x20 + TIOCM_DSR = 0x100 + TIOCM_DTR = 0x2 + TIOCM_LE = 0x1 + TIOCM_RI = 0x80 + TIOCM_RNG = 0x80 + TIOCM_RTS = 0x4 + TIOCM_SR = 0x10 + TIOCM_ST = 0x8 + TIOCNOTTY = 0x5422 + TIOCNXCL = 0x540d + TIOCOUTQ = 0x5411 + TIOCPKT = 0x5420 + TIOCPKT_DATA = 0 + TIOCPKT_DOSTOP = 0x20 + TIOCPKT_FLUSHREAD = 0x1 + TIOCPKT_FLUSHWRITE = 0x2 + TIOCPKT_NOSTOP = 0x10 + TIOCPKT_START = 0x8 + TIOCPKT_STOP = 0x4 + TIOCSBRK = 0x5427 + TIOCSCTTY = 0x540e + TIOCSERCONFIG = 0x5453 + TIOCSERGETLSR = 0x5459 + TIOCSERGETMULTI = 0x545a + TIOCSERGSTRUCT = 0x5458 + TIOCSERGWILD = 0x5454 + TIOCSERSETMULTI = 0x545b + TIOCSERSWILD = 0x5455 + TIOCSER_TEMT = 0x1 + TIOCSETD = 0x5423 + TIOCSLCKTRMIOS = 0x5457 + TIOCSPGRP = 0x5410 + TIOCSPTLCK = 0x40045431 + TIOCSSERIAL = 0x541f + TIOCSSOFTCAR = 0x541a + TIOCSTI = 0x5412 + TIOCSWINSZ = 0x5414 TUNGETFEATURES = 0x800454cf TUNGETIFF = 0x800454d2 TUNGETSNDBUF = 0x800454d3 diff --git a/src/pkg/syscall/zerrors_plan9_386.go b/src/pkg/syscall/zerrors_plan9_386.go index 78b5c72bb..e452079f5 100644 --- a/src/pkg/syscall/zerrors_plan9_386.go +++ b/src/pkg/syscall/zerrors_plan9_386.go @@ -4,10 +4,9 @@ package syscall const ( // Invented values to support what package os expects. O_CREAT = 0x02000 + O_APPEND = 0x00400 O_NOCTTY = 0x00000 - O_TRUNC = 0x00000 O_NONBLOCK = 0x00000 - O_APPEND = 0x00000 O_SYNC = 0x00000 O_ASYNC = 0x00000 diff --git a/src/pkg/syscall/zerrors_windows.go b/src/pkg/syscall/zerrors_windows.go new file mode 100644 index 000000000..ae4506fac --- /dev/null +++ b/src/pkg/syscall/zerrors_windows.go @@ -0,0 +1,283 @@ +// mkerrors_windows.sh -f -m32 +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +package syscall + +// Go names for Windows errors. +const ( + ENOENT = ERROR_FILE_NOT_FOUND + ENOTDIR = ERROR_DIRECTORY +) + +// Windows reserves errors >= 1<<29 for application use. +const APPLICATION_ERROR = 1 << 29 + +// Invented values to support what package os and others expects. +const ( + E2BIG = APPLICATION_ERROR + iota + EACCES + EADDRINUSE + EADDRNOTAVAIL + EADV + EAFNOSUPPORT + EAGAIN + EALREADY + EBADE + EBADF + EBADFD + EBADMSG + EBADR + EBADRQC + EBADSLT + EBFONT + EBUSY + ECANCELED + ECHILD + ECHRNG + ECOMM + ECONNABORTED + ECONNREFUSED + ECONNRESET + EDEADLK + EDEADLOCK + EDESTADDRREQ + EDOM + EDOTDOT + EDQUOT + EEXIST + EFAULT + EFBIG + EHOSTDOWN + EHOSTUNREACH + EIDRM + EILSEQ + EINPROGRESS + EINTR + EINVAL + EIO + EISCONN + EISDIR + EISNAM + EKEYEXPIRED + EKEYREJECTED + EKEYREVOKED + EL2HLT + EL2NSYNC + EL3HLT + EL3RST + ELIBACC + ELIBBAD + ELIBEXEC + ELIBMAX + ELIBSCN + ELNRNG + ELOOP + EMEDIUMTYPE + EMFILE + EMLINK + EMSGSIZE + EMULTIHOP + ENAMETOOLONG + ENAVAIL + ENETDOWN + ENETRESET + ENETUNREACH + ENFILE + ENOANO + ENOBUFS + ENOCSI + ENODATA + ENODEV + ENOEXEC + ENOKEY + ENOLCK + ENOLINK + ENOMEDIUM + ENOMEM + ENOMSG + ENONET + ENOPKG + ENOPROTOOPT + ENOSPC + ENOSR + ENOSTR + ENOSYS + ENOTBLK + ENOTCONN + ENOTEMPTY + ENOTNAM + ENOTRECOVERABLE + ENOTSOCK + ENOTSUP + ENOTTY + ENOTUNIQ + ENXIO + EOPNOTSUPP + EOVERFLOW + EOWNERDEAD + EPERM + EPFNOSUPPORT + EPIPE + EPROTO + EPROTONOSUPPORT + EPROTOTYPE + ERANGE + EREMCHG + EREMOTE + EREMOTEIO + ERESTART + EROFS + ESHUTDOWN + ESOCKTNOSUPPORT + ESPIPE + ESRCH + ESRMNT + ESTALE + ESTRPIPE + ETIME + ETIMEDOUT + ETOOMANYREFS + ETXTBSY + EUCLEAN + EUNATCH + EUSERS + EWOULDBLOCK + EXDEV + EXFULL + EWINDOWS +) + +// Error strings for invented errors +var errors = [...]string{ + E2BIG - APPLICATION_ERROR: "argument list too long", + EACCES - APPLICATION_ERROR: "permission denied", + EADDRINUSE - APPLICATION_ERROR: "address already in use", + EADDRNOTAVAIL - APPLICATION_ERROR: "cannot assign requested address", + EADV - APPLICATION_ERROR: "advertise error", + EAFNOSUPPORT - APPLICATION_ERROR: "address family not supported by protocol", + EAGAIN - APPLICATION_ERROR: "resource temporarily unavailable", + EALREADY - APPLICATION_ERROR: "operation already in progress", + EBADE - APPLICATION_ERROR: "invalid exchange", + EBADF - APPLICATION_ERROR: "bad file descriptor", + EBADFD - APPLICATION_ERROR: "file descriptor in bad state", + EBADMSG - APPLICATION_ERROR: "bad message", + EBADR - APPLICATION_ERROR: "invalid request descriptor", + EBADRQC - APPLICATION_ERROR: "invalid request code", + EBADSLT - APPLICATION_ERROR: "invalid slot", + EBFONT - APPLICATION_ERROR: "bad font file format", + EBUSY - APPLICATION_ERROR: "device or resource busy", + ECANCELED - APPLICATION_ERROR: "operation canceled", + ECHILD - APPLICATION_ERROR: "no child processes", + ECHRNG - APPLICATION_ERROR: "channel number out of range", + ECOMM - APPLICATION_ERROR: "communication error on send", + ECONNABORTED - APPLICATION_ERROR: "software caused connection abort", + ECONNREFUSED - APPLICATION_ERROR: "connection refused", + ECONNRESET - APPLICATION_ERROR: "connection reset by peer", + EDEADLK - APPLICATION_ERROR: "resource deadlock avoided", + EDEADLOCK - APPLICATION_ERROR: "resource deadlock avoided", + EDESTADDRREQ - APPLICATION_ERROR: "destination address required", + EDOM - APPLICATION_ERROR: "numerical argument out of domain", + EDOTDOT - APPLICATION_ERROR: "RFS specific error", + EDQUOT - APPLICATION_ERROR: "disk quota exceeded", + EEXIST - APPLICATION_ERROR: "file exists", + EFAULT - APPLICATION_ERROR: "bad address", + EFBIG - APPLICATION_ERROR: "file too large", + EHOSTDOWN - APPLICATION_ERROR: "host is down", + EHOSTUNREACH - APPLICATION_ERROR: "no route to host", + EIDRM - APPLICATION_ERROR: "identifier removed", + EILSEQ - APPLICATION_ERROR: "invalid or incomplete multibyte or wide character", + EINPROGRESS - APPLICATION_ERROR: "operation now in progress", + EINTR - APPLICATION_ERROR: "interrupted system call", + EINVAL - APPLICATION_ERROR: "invalid argument", + EIO - APPLICATION_ERROR: "input/output error", + EISCONN - APPLICATION_ERROR: "transport endpoint is already connected", + EISDIR - APPLICATION_ERROR: "is a directory", + EISNAM - APPLICATION_ERROR: "is a named type file", + EKEYEXPIRED - APPLICATION_ERROR: "key has expired", + EKEYREJECTED - APPLICATION_ERROR: "key was rejected by service", + EKEYREVOKED - APPLICATION_ERROR: "key has been revoked", + EL2HLT - APPLICATION_ERROR: "level 2 halted", + EL2NSYNC - APPLICATION_ERROR: "level 2 not synchronized", + EL3HLT - APPLICATION_ERROR: "level 3 halted", + EL3RST - APPLICATION_ERROR: "level 3 reset", + ELIBACC - APPLICATION_ERROR: "can not access a needed shared library", + ELIBBAD - APPLICATION_ERROR: "accessing a corrupted shared library", + ELIBEXEC - APPLICATION_ERROR: "cannot exec a shared library directly", + ELIBMAX - APPLICATION_ERROR: "attempting to link in too many shared libraries", + ELIBSCN - APPLICATION_ERROR: ".lib section in a.out corrupted", + ELNRNG - APPLICATION_ERROR: "link number out of range", + ELOOP - APPLICATION_ERROR: "too many levels of symbolic links", + EMEDIUMTYPE - APPLICATION_ERROR: "wrong medium type", + EMFILE - APPLICATION_ERROR: "too many open files", + EMLINK - APPLICATION_ERROR: "too many links", + EMSGSIZE - APPLICATION_ERROR: "message too long", + EMULTIHOP - APPLICATION_ERROR: "multihop attempted", + ENAMETOOLONG - APPLICATION_ERROR: "file name too long", + ENAVAIL - APPLICATION_ERROR: "no XENIX semaphores available", + ENETDOWN - APPLICATION_ERROR: "network is down", + ENETRESET - APPLICATION_ERROR: "network dropped connection on reset", + ENETUNREACH - APPLICATION_ERROR: "network is unreachable", + ENFILE - APPLICATION_ERROR: "too many open files in system", + ENOANO - APPLICATION_ERROR: "no anode", + ENOBUFS - APPLICATION_ERROR: "no buffer space available", + ENOCSI - APPLICATION_ERROR: "no CSI structure available", + ENODATA - APPLICATION_ERROR: "no data available", + ENODEV - APPLICATION_ERROR: "no such device", + ENOEXEC - APPLICATION_ERROR: "exec format error", + ENOKEY - APPLICATION_ERROR: "required key not available", + ENOLCK - APPLICATION_ERROR: "no locks available", + ENOLINK - APPLICATION_ERROR: "link has been severed", + ENOMEDIUM - APPLICATION_ERROR: "no medium found", + ENOMEM - APPLICATION_ERROR: "cannot allocate memory", + ENOMSG - APPLICATION_ERROR: "no message of desired type", + ENONET - APPLICATION_ERROR: "machine is not on the network", + ENOPKG - APPLICATION_ERROR: "package not installed", + ENOPROTOOPT - APPLICATION_ERROR: "protocol not available", + ENOSPC - APPLICATION_ERROR: "no space left on device", + ENOSR - APPLICATION_ERROR: "out of streams resources", + ENOSTR - APPLICATION_ERROR: "device not a stream", + ENOSYS - APPLICATION_ERROR: "function not implemented", + ENOTBLK - APPLICATION_ERROR: "block device required", + ENOTCONN - APPLICATION_ERROR: "transport endpoint is not connected", + ENOTEMPTY - APPLICATION_ERROR: "directory not empty", + ENOTNAM - APPLICATION_ERROR: "not a XENIX named type file", + ENOTRECOVERABLE - APPLICATION_ERROR: "state not recoverable", + ENOTSOCK - APPLICATION_ERROR: "socket operation on non-socket", + ENOTSUP - APPLICATION_ERROR: "operation not supported", + ENOTTY - APPLICATION_ERROR: "inappropriate ioctl for device", + ENOTUNIQ - APPLICATION_ERROR: "name not unique on network", + ENXIO - APPLICATION_ERROR: "no such device or address", + EOPNOTSUPP - APPLICATION_ERROR: "operation not supported", + EOVERFLOW - APPLICATION_ERROR: "value too large for defined data type", + EOWNERDEAD - APPLICATION_ERROR: "owner died", + EPERM - APPLICATION_ERROR: "operation not permitted", + EPFNOSUPPORT - APPLICATION_ERROR: "protocol family not supported", + EPIPE - APPLICATION_ERROR: "broken pipe", + EPROTO - APPLICATION_ERROR: "protocol error", + EPROTONOSUPPORT - APPLICATION_ERROR: "protocol not supported", + EPROTOTYPE - APPLICATION_ERROR: "protocol wrong type for socket", + ERANGE - APPLICATION_ERROR: "numerical result out of range", + EREMCHG - APPLICATION_ERROR: "remote address changed", + EREMOTE - APPLICATION_ERROR: "object is remote", + EREMOTEIO - APPLICATION_ERROR: "remote I/O error", + ERESTART - APPLICATION_ERROR: "interrupted system call should be restarted", + EROFS - APPLICATION_ERROR: "read-only file system", + ESHUTDOWN - APPLICATION_ERROR: "cannot send after transport endpoint shutdown", + ESOCKTNOSUPPORT - APPLICATION_ERROR: "socket type not supported", + ESPIPE - APPLICATION_ERROR: "illegal seek", + ESRCH - APPLICATION_ERROR: "no such process", + ESRMNT - APPLICATION_ERROR: "srmount error", + ESTALE - APPLICATION_ERROR: "stale NFS file handle", + ESTRPIPE - APPLICATION_ERROR: "streams pipe error", + ETIME - APPLICATION_ERROR: "timer expired", + ETIMEDOUT - APPLICATION_ERROR: "connection timed out", + ETOOMANYREFS - APPLICATION_ERROR: "too many references: cannot splice", + ETXTBSY - APPLICATION_ERROR: "text file busy", + EUCLEAN - APPLICATION_ERROR: "structure needs cleaning", + EUNATCH - APPLICATION_ERROR: "protocol driver not attached", + EUSERS - APPLICATION_ERROR: "too many users", + EWOULDBLOCK - APPLICATION_ERROR: "resource temporarily unavailable", + EXDEV - APPLICATION_ERROR: "invalid cross-device link", + EXFULL - APPLICATION_ERROR: "exchange full", + EWINDOWS - APPLICATION_ERROR: "not supported by windows", +} diff --git a/src/pkg/syscall/zerrors_windows_386.go b/src/pkg/syscall/zerrors_windows_386.go index ae4506fac..d1008bd03 100644 --- a/src/pkg/syscall/zerrors_windows_386.go +++ b/src/pkg/syscall/zerrors_windows_386.go @@ -1,283 +1,5 @@ -// mkerrors_windows.sh -f -m32 -// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. package syscall - -// Go names for Windows errors. -const ( - ENOENT = ERROR_FILE_NOT_FOUND - ENOTDIR = ERROR_DIRECTORY -) - -// Windows reserves errors >= 1<<29 for application use. -const APPLICATION_ERROR = 1 << 29 - -// Invented values to support what package os and others expects. -const ( - E2BIG = APPLICATION_ERROR + iota - EACCES - EADDRINUSE - EADDRNOTAVAIL - EADV - EAFNOSUPPORT - EAGAIN - EALREADY - EBADE - EBADF - EBADFD - EBADMSG - EBADR - EBADRQC - EBADSLT - EBFONT - EBUSY - ECANCELED - ECHILD - ECHRNG - ECOMM - ECONNABORTED - ECONNREFUSED - ECONNRESET - EDEADLK - EDEADLOCK - EDESTADDRREQ - EDOM - EDOTDOT - EDQUOT - EEXIST - EFAULT - EFBIG - EHOSTDOWN - EHOSTUNREACH - EIDRM - EILSEQ - EINPROGRESS - EINTR - EINVAL - EIO - EISCONN - EISDIR - EISNAM - EKEYEXPIRED - EKEYREJECTED - EKEYREVOKED - EL2HLT - EL2NSYNC - EL3HLT - EL3RST - ELIBACC - ELIBBAD - ELIBEXEC - ELIBMAX - ELIBSCN - ELNRNG - ELOOP - EMEDIUMTYPE - EMFILE - EMLINK - EMSGSIZE - EMULTIHOP - ENAMETOOLONG - ENAVAIL - ENETDOWN - ENETRESET - ENETUNREACH - ENFILE - ENOANO - ENOBUFS - ENOCSI - ENODATA - ENODEV - ENOEXEC - ENOKEY - ENOLCK - ENOLINK - ENOMEDIUM - ENOMEM - ENOMSG - ENONET - ENOPKG - ENOPROTOOPT - ENOSPC - ENOSR - ENOSTR - ENOSYS - ENOTBLK - ENOTCONN - ENOTEMPTY - ENOTNAM - ENOTRECOVERABLE - ENOTSOCK - ENOTSUP - ENOTTY - ENOTUNIQ - ENXIO - EOPNOTSUPP - EOVERFLOW - EOWNERDEAD - EPERM - EPFNOSUPPORT - EPIPE - EPROTO - EPROTONOSUPPORT - EPROTOTYPE - ERANGE - EREMCHG - EREMOTE - EREMOTEIO - ERESTART - EROFS - ESHUTDOWN - ESOCKTNOSUPPORT - ESPIPE - ESRCH - ESRMNT - ESTALE - ESTRPIPE - ETIME - ETIMEDOUT - ETOOMANYREFS - ETXTBSY - EUCLEAN - EUNATCH - EUSERS - EWOULDBLOCK - EXDEV - EXFULL - EWINDOWS -) - -// Error strings for invented errors -var errors = [...]string{ - E2BIG - APPLICATION_ERROR: "argument list too long", - EACCES - APPLICATION_ERROR: "permission denied", - EADDRINUSE - APPLICATION_ERROR: "address already in use", - EADDRNOTAVAIL - APPLICATION_ERROR: "cannot assign requested address", - EADV - APPLICATION_ERROR: "advertise error", - EAFNOSUPPORT - APPLICATION_ERROR: "address family not supported by protocol", - EAGAIN - APPLICATION_ERROR: "resource temporarily unavailable", - EALREADY - APPLICATION_ERROR: "operation already in progress", - EBADE - APPLICATION_ERROR: "invalid exchange", - EBADF - APPLICATION_ERROR: "bad file descriptor", - EBADFD - APPLICATION_ERROR: "file descriptor in bad state", - EBADMSG - APPLICATION_ERROR: "bad message", - EBADR - APPLICATION_ERROR: "invalid request descriptor", - EBADRQC - APPLICATION_ERROR: "invalid request code", - EBADSLT - APPLICATION_ERROR: "invalid slot", - EBFONT - APPLICATION_ERROR: "bad font file format", - EBUSY - APPLICATION_ERROR: "device or resource busy", - ECANCELED - APPLICATION_ERROR: "operation canceled", - ECHILD - APPLICATION_ERROR: "no child processes", - ECHRNG - APPLICATION_ERROR: "channel number out of range", - ECOMM - APPLICATION_ERROR: "communication error on send", - ECONNABORTED - APPLICATION_ERROR: "software caused connection abort", - ECONNREFUSED - APPLICATION_ERROR: "connection refused", - ECONNRESET - APPLICATION_ERROR: "connection reset by peer", - EDEADLK - APPLICATION_ERROR: "resource deadlock avoided", - EDEADLOCK - APPLICATION_ERROR: "resource deadlock avoided", - EDESTADDRREQ - APPLICATION_ERROR: "destination address required", - EDOM - APPLICATION_ERROR: "numerical argument out of domain", - EDOTDOT - APPLICATION_ERROR: "RFS specific error", - EDQUOT - APPLICATION_ERROR: "disk quota exceeded", - EEXIST - APPLICATION_ERROR: "file exists", - EFAULT - APPLICATION_ERROR: "bad address", - EFBIG - APPLICATION_ERROR: "file too large", - EHOSTDOWN - APPLICATION_ERROR: "host is down", - EHOSTUNREACH - APPLICATION_ERROR: "no route to host", - EIDRM - APPLICATION_ERROR: "identifier removed", - EILSEQ - APPLICATION_ERROR: "invalid or incomplete multibyte or wide character", - EINPROGRESS - APPLICATION_ERROR: "operation now in progress", - EINTR - APPLICATION_ERROR: "interrupted system call", - EINVAL - APPLICATION_ERROR: "invalid argument", - EIO - APPLICATION_ERROR: "input/output error", - EISCONN - APPLICATION_ERROR: "transport endpoint is already connected", - EISDIR - APPLICATION_ERROR: "is a directory", - EISNAM - APPLICATION_ERROR: "is a named type file", - EKEYEXPIRED - APPLICATION_ERROR: "key has expired", - EKEYREJECTED - APPLICATION_ERROR: "key was rejected by service", - EKEYREVOKED - APPLICATION_ERROR: "key has been revoked", - EL2HLT - APPLICATION_ERROR: "level 2 halted", - EL2NSYNC - APPLICATION_ERROR: "level 2 not synchronized", - EL3HLT - APPLICATION_ERROR: "level 3 halted", - EL3RST - APPLICATION_ERROR: "level 3 reset", - ELIBACC - APPLICATION_ERROR: "can not access a needed shared library", - ELIBBAD - APPLICATION_ERROR: "accessing a corrupted shared library", - ELIBEXEC - APPLICATION_ERROR: "cannot exec a shared library directly", - ELIBMAX - APPLICATION_ERROR: "attempting to link in too many shared libraries", - ELIBSCN - APPLICATION_ERROR: ".lib section in a.out corrupted", - ELNRNG - APPLICATION_ERROR: "link number out of range", - ELOOP - APPLICATION_ERROR: "too many levels of symbolic links", - EMEDIUMTYPE - APPLICATION_ERROR: "wrong medium type", - EMFILE - APPLICATION_ERROR: "too many open files", - EMLINK - APPLICATION_ERROR: "too many links", - EMSGSIZE - APPLICATION_ERROR: "message too long", - EMULTIHOP - APPLICATION_ERROR: "multihop attempted", - ENAMETOOLONG - APPLICATION_ERROR: "file name too long", - ENAVAIL - APPLICATION_ERROR: "no XENIX semaphores available", - ENETDOWN - APPLICATION_ERROR: "network is down", - ENETRESET - APPLICATION_ERROR: "network dropped connection on reset", - ENETUNREACH - APPLICATION_ERROR: "network is unreachable", - ENFILE - APPLICATION_ERROR: "too many open files in system", - ENOANO - APPLICATION_ERROR: "no anode", - ENOBUFS - APPLICATION_ERROR: "no buffer space available", - ENOCSI - APPLICATION_ERROR: "no CSI structure available", - ENODATA - APPLICATION_ERROR: "no data available", - ENODEV - APPLICATION_ERROR: "no such device", - ENOEXEC - APPLICATION_ERROR: "exec format error", - ENOKEY - APPLICATION_ERROR: "required key not available", - ENOLCK - APPLICATION_ERROR: "no locks available", - ENOLINK - APPLICATION_ERROR: "link has been severed", - ENOMEDIUM - APPLICATION_ERROR: "no medium found", - ENOMEM - APPLICATION_ERROR: "cannot allocate memory", - ENOMSG - APPLICATION_ERROR: "no message of desired type", - ENONET - APPLICATION_ERROR: "machine is not on the network", - ENOPKG - APPLICATION_ERROR: "package not installed", - ENOPROTOOPT - APPLICATION_ERROR: "protocol not available", - ENOSPC - APPLICATION_ERROR: "no space left on device", - ENOSR - APPLICATION_ERROR: "out of streams resources", - ENOSTR - APPLICATION_ERROR: "device not a stream", - ENOSYS - APPLICATION_ERROR: "function not implemented", - ENOTBLK - APPLICATION_ERROR: "block device required", - ENOTCONN - APPLICATION_ERROR: "transport endpoint is not connected", - ENOTEMPTY - APPLICATION_ERROR: "directory not empty", - ENOTNAM - APPLICATION_ERROR: "not a XENIX named type file", - ENOTRECOVERABLE - APPLICATION_ERROR: "state not recoverable", - ENOTSOCK - APPLICATION_ERROR: "socket operation on non-socket", - ENOTSUP - APPLICATION_ERROR: "operation not supported", - ENOTTY - APPLICATION_ERROR: "inappropriate ioctl for device", - ENOTUNIQ - APPLICATION_ERROR: "name not unique on network", - ENXIO - APPLICATION_ERROR: "no such device or address", - EOPNOTSUPP - APPLICATION_ERROR: "operation not supported", - EOVERFLOW - APPLICATION_ERROR: "value too large for defined data type", - EOWNERDEAD - APPLICATION_ERROR: "owner died", - EPERM - APPLICATION_ERROR: "operation not permitted", - EPFNOSUPPORT - APPLICATION_ERROR: "protocol family not supported", - EPIPE - APPLICATION_ERROR: "broken pipe", - EPROTO - APPLICATION_ERROR: "protocol error", - EPROTONOSUPPORT - APPLICATION_ERROR: "protocol not supported", - EPROTOTYPE - APPLICATION_ERROR: "protocol wrong type for socket", - ERANGE - APPLICATION_ERROR: "numerical result out of range", - EREMCHG - APPLICATION_ERROR: "remote address changed", - EREMOTE - APPLICATION_ERROR: "object is remote", - EREMOTEIO - APPLICATION_ERROR: "remote I/O error", - ERESTART - APPLICATION_ERROR: "interrupted system call should be restarted", - EROFS - APPLICATION_ERROR: "read-only file system", - ESHUTDOWN - APPLICATION_ERROR: "cannot send after transport endpoint shutdown", - ESOCKTNOSUPPORT - APPLICATION_ERROR: "socket type not supported", - ESPIPE - APPLICATION_ERROR: "illegal seek", - ESRCH - APPLICATION_ERROR: "no such process", - ESRMNT - APPLICATION_ERROR: "srmount error", - ESTALE - APPLICATION_ERROR: "stale NFS file handle", - ESTRPIPE - APPLICATION_ERROR: "streams pipe error", - ETIME - APPLICATION_ERROR: "timer expired", - ETIMEDOUT - APPLICATION_ERROR: "connection timed out", - ETOOMANYREFS - APPLICATION_ERROR: "too many references: cannot splice", - ETXTBSY - APPLICATION_ERROR: "text file busy", - EUCLEAN - APPLICATION_ERROR: "structure needs cleaning", - EUNATCH - APPLICATION_ERROR: "protocol driver not attached", - EUSERS - APPLICATION_ERROR: "too many users", - EWOULDBLOCK - APPLICATION_ERROR: "resource temporarily unavailable", - EXDEV - APPLICATION_ERROR: "invalid cross-device link", - EXFULL - APPLICATION_ERROR: "exchange full", - EWINDOWS - APPLICATION_ERROR: "not supported by windows", -} diff --git a/src/pkg/syscall/zerrors_windows_amd64.go b/src/pkg/syscall/zerrors_windows_amd64.go new file mode 100644 index 000000000..d1008bd03 --- /dev/null +++ b/src/pkg/syscall/zerrors_windows_amd64.go @@ -0,0 +1,5 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package syscall diff --git a/src/pkg/syscall/zsyscall_darwin_386.go b/src/pkg/syscall/zsyscall_darwin_386.go index 2f5b2703b..436953eca 100644 --- a/src/pkg/syscall/zsyscall_darwin_386.go +++ b/src/pkg/syscall/zsyscall_darwin_386.go @@ -85,7 +85,7 @@ func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errn // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) { +func setsockopt(s int, level int, name int, val uintptr, vallen uintptr) (errno int) { _, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0) errno = int(e1) return @@ -154,6 +154,23 @@ func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (errno i // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func recvmsg(s int, msg *Msghdr, flags int) (n int, errno int) { + r0, _, e1 := Syscall(SYS_RECVMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + n = int(r0) + errno = int(e1) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sendmsg(s int, msg *Msghdr, flags int) (errno int) { + _, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + errno = int(e1) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func kevent(kq int, change uintptr, nchange int, event uintptr, nevent int, timeout *Timespec) (n int, errno int) { r0, _, e1 := Syscall6(SYS_KEVENT, uintptr(kq), uintptr(change), uintptr(nchange), uintptr(event), uintptr(nevent), uintptr(unsafe.Pointer(timeout))) n = int(r0) @@ -219,6 +236,14 @@ func munmap(addr uintptr, length uintptr) (errno int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ptrace(request int, pid int, addr uintptr, data uintptr) (errno int) { + _, _, e1 := Syscall6(SYS_PTRACE, uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0) + errno = int(e1) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func kill(pid int, signum int, posix int) (errno int) { _, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), uintptr(posix)) errno = int(e1) diff --git a/src/pkg/syscall/zsyscall_darwin_amd64.go b/src/pkg/syscall/zsyscall_darwin_amd64.go index 995c710b4..1ba4c3cfe 100644 --- a/src/pkg/syscall/zsyscall_darwin_amd64.go +++ b/src/pkg/syscall/zsyscall_darwin_amd64.go @@ -85,7 +85,7 @@ func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errn // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) { +func setsockopt(s int, level int, name int, val uintptr, vallen uintptr) (errno int) { _, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0) errno = int(e1) return @@ -154,6 +154,23 @@ func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (errno i // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func recvmsg(s int, msg *Msghdr, flags int) (n int, errno int) { + r0, _, e1 := Syscall(SYS_RECVMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + n = int(r0) + errno = int(e1) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sendmsg(s int, msg *Msghdr, flags int) (errno int) { + _, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + errno = int(e1) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func kevent(kq int, change uintptr, nchange int, event uintptr, nevent int, timeout *Timespec) (n int, errno int) { r0, _, e1 := Syscall6(SYS_KEVENT, uintptr(kq), uintptr(change), uintptr(nchange), uintptr(event), uintptr(nevent), uintptr(unsafe.Pointer(timeout))) n = int(r0) @@ -219,6 +236,14 @@ func munmap(addr uintptr, length uintptr) (errno int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func ptrace(request int, pid int, addr uintptr, data uintptr) (errno int) { + _, _, e1 := Syscall6(SYS_PTRACE, uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0) + errno = int(e1) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func kill(pid int, signum int, posix int) (errno int) { _, _, e1 := Syscall(SYS_KILL, uintptr(pid), uintptr(signum), uintptr(posix)) errno = int(e1) diff --git a/src/pkg/syscall/zsyscall_freebsd_386.go b/src/pkg/syscall/zsyscall_freebsd_386.go index 0ffb9a4b9..d152e4380 100644 --- a/src/pkg/syscall/zsyscall_freebsd_386.go +++ b/src/pkg/syscall/zsyscall_freebsd_386.go @@ -85,7 +85,7 @@ func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errn // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) { +func setsockopt(s int, level int, name int, val uintptr, vallen uintptr) (errno int) { _, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0) errno = int(e1) return @@ -154,6 +154,23 @@ func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (errno i // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func recvmsg(s int, msg *Msghdr, flags int) (n int, errno int) { + r0, _, e1 := Syscall(SYS_RECVMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + n = int(r0) + errno = int(e1) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sendmsg(s int, msg *Msghdr, flags int) (errno int) { + _, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + errno = int(e1) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func kevent(kq int, change uintptr, nchange int, event uintptr, nevent int, timeout *Timespec) (n int, errno int) { r0, _, e1 := Syscall6(SYS_KEVENT, uintptr(kq), uintptr(change), uintptr(nchange), uintptr(event), uintptr(nevent), uintptr(unsafe.Pointer(timeout))) n = int(r0) diff --git a/src/pkg/syscall/zsyscall_freebsd_amd64.go b/src/pkg/syscall/zsyscall_freebsd_amd64.go index 38a06ae3b..156b087e3 100644 --- a/src/pkg/syscall/zsyscall_freebsd_amd64.go +++ b/src/pkg/syscall/zsyscall_freebsd_amd64.go @@ -85,7 +85,7 @@ func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errn // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) { +func setsockopt(s int, level int, name int, val uintptr, vallen uintptr) (errno int) { _, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0) errno = int(e1) return @@ -154,6 +154,23 @@ func sendto(s int, buf []byte, flags int, to uintptr, addrlen _Socklen) (errno i // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func recvmsg(s int, msg *Msghdr, flags int) (n int, errno int) { + r0, _, e1 := Syscall(SYS_RECVMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + n = int(r0) + errno = int(e1) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func sendmsg(s int, msg *Msghdr, flags int) (errno int) { + _, _, e1 := Syscall(SYS_SENDMSG, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)) + errno = int(e1) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func kevent(kq int, change uintptr, nchange int, event uintptr, nevent int, timeout *Timespec) (n int, errno int) { r0, _, e1 := Syscall6(SYS_KEVENT, uintptr(kq), uintptr(change), uintptr(nchange), uintptr(event), uintptr(nevent), uintptr(unsafe.Pointer(timeout))) n = int(r0) diff --git a/src/pkg/syscall/zsyscall_linux_amd64.go b/src/pkg/syscall/zsyscall_linux_amd64.go index d6e287967..fa20ff57a 100644 --- a/src/pkg/syscall/zsyscall_linux_amd64.go +++ b/src/pkg/syscall/zsyscall_linux_amd64.go @@ -1169,7 +1169,7 @@ func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errn // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) { +func setsockopt(s int, level int, name int, val uintptr, vallen uintptr) (errno int) { _, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0) errno = int(e1) return diff --git a/src/pkg/syscall/zsyscall_linux_arm.go b/src/pkg/syscall/zsyscall_linux_arm.go index af5f7c50c..560a65b12 100644 --- a/src/pkg/syscall/zsyscall_linux_arm.go +++ b/src/pkg/syscall/zsyscall_linux_arm.go @@ -895,7 +895,7 @@ func getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (errn // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func setsockopt(s int, level int, name int, val uintptr, vallen int) (errno int) { +func setsockopt(s int, level int, name int, val uintptr, vallen uintptr) (errno int) { _, _, e1 := Syscall6(SYS_SETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(vallen), 0) errno = int(e1) return diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows_386.go index 447b09043..350ad232a 100644 --- a/src/pkg/syscall/zsyscall_windows_386.go +++ b/src/pkg/syscall/zsyscall_windows_386.go @@ -12,6 +12,7 @@ var ( modwsock32 = loadDll("wsock32.dll") modws2_32 = loadDll("ws2_32.dll") moddnsapi = loadDll("dnsapi.dll") + modiphlpapi = loadDll("iphlpapi.dll") procGetLastError = getSysProcAddr(modkernel32, "GetLastError") procLoadLibraryW = getSysProcAddr(modkernel32, "LoadLibraryW") @@ -77,8 +78,10 @@ var ( procFlushViewOfFile = getSysProcAddr(modkernel32, "FlushViewOfFile") procVirtualLock = getSysProcAddr(modkernel32, "VirtualLock") procVirtualUnlock = getSysProcAddr(modkernel32, "VirtualUnlock") + procTransmitFile = getSysProcAddr(modwsock32, "TransmitFile") procWSAStartup = getSysProcAddr(modwsock32, "WSAStartup") procWSACleanup = getSysProcAddr(modwsock32, "WSACleanup") + procWSAIoctl = getSysProcAddr(modws2_32, "WSAIoctl") procsocket = getSysProcAddr(modwsock32, "socket") procsetsockopt = getSysProcAddr(modwsock32, "setsockopt") procbind = getSysProcAddr(modwsock32, "bind") @@ -99,6 +102,8 @@ var ( procntohs = getSysProcAddr(modws2_32, "ntohs") procDnsQuery_W = getSysProcAddr(moddnsapi, "DnsQuery_W") procDnsRecordListFree = getSysProcAddr(moddnsapi, "DnsRecordListFree") + procGetIfEntry = getSysProcAddr(modiphlpapi, "GetIfEntry") + procGetAdaptersInfo = getSysProcAddr(modiphlpapi, "GetAdaptersInfo") ) func GetLastError() (lasterrno int) { @@ -107,9 +112,9 @@ func GetLastError() (lasterrno int) { return } -func LoadLibrary(libname string) (handle uint32, errno int) { +func LoadLibrary(libname string) (handle Handle, errno int) { r0, _, e1 := Syscall(procLoadLibraryW, 1, uintptr(unsafe.Pointer(StringToUTF16Ptr(libname))), 0, 0) - handle = uint32(r0) + handle = Handle(r0) if handle == 0 { if e1 != 0 { errno = int(e1) @@ -122,7 +127,7 @@ func LoadLibrary(libname string) (handle uint32, errno int) { return } -func FreeLibrary(handle uint32) (errno int) { +func FreeLibrary(handle Handle) (errno int) { r1, _, e1 := Syscall(procFreeLibrary, 1, uintptr(handle), 0, 0) if int(r1) == 0 { if e1 != 0 { @@ -136,9 +141,9 @@ func FreeLibrary(handle uint32) (errno int) { return } -func GetProcAddress(module uint32, procname string) (proc uint32, errno int) { +func GetProcAddress(module Handle, procname string) (proc Handle, errno int) { r0, _, e1 := Syscall(procGetProcAddress, 2, uintptr(module), uintptr(unsafe.Pointer(StringBytePtr(procname))), 0) - proc = uint32(r0) + proc = Handle(r0) if proc == 0 { if e1 != 0 { errno = int(e1) @@ -190,10 +195,10 @@ func ExitProcess(exitcode uint32) { return } -func CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle int32, errno int) { +func CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle Handle, errno int) { r0, _, e1 := Syscall9(procCreateFileW, 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0) - handle = int32(r0) - if handle == -1 { + handle = Handle(r0) + if handle == InvalidHandle { if e1 != 0 { errno = int(e1) } else { @@ -205,7 +210,7 @@ func CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes return } -func ReadFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (errno int) { +func ReadFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (errno int) { var _p0 *byte if len(buf) > 0 { _p0 = &buf[0] @@ -223,7 +228,7 @@ func ReadFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (e return } -func WriteFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) (errno int) { +func WriteFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (errno int) { var _p0 *byte if len(buf) > 0 { _p0 = &buf[0] @@ -241,7 +246,7 @@ func WriteFile(handle int32, buf []byte, done *uint32, overlapped *Overlapped) ( return } -func SetFilePointer(handle int32, lowoffset int32, highoffsetptr *int32, whence uint32) (newlowoffset uint32, errno int) { +func SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence uint32) (newlowoffset uint32, errno int) { r0, _, e1 := Syscall6(procSetFilePointer, 4, uintptr(handle), uintptr(lowoffset), uintptr(unsafe.Pointer(highoffsetptr)), uintptr(whence), 0, 0) newlowoffset = uint32(r0) if newlowoffset == 0xffffffff { @@ -256,7 +261,7 @@ func SetFilePointer(handle int32, lowoffset int32, highoffsetptr *int32, whence return } -func CloseHandle(handle int32) (errno int) { +func CloseHandle(handle Handle) (errno int) { r1, _, e1 := Syscall(procCloseHandle, 1, uintptr(handle), 0, 0) if int(r1) == 0 { if e1 != 0 { @@ -270,10 +275,10 @@ func CloseHandle(handle int32) (errno int) { return } -func GetStdHandle(stdhandle int32) (handle int32, errno int) { +func GetStdHandle(stdhandle int) (handle Handle, errno int) { r0, _, e1 := Syscall(procGetStdHandle, 1, uintptr(stdhandle), 0, 0) - handle = int32(r0) - if handle == -1 { + handle = Handle(r0) + if handle == InvalidHandle { if e1 != 0 { errno = int(e1) } else { @@ -285,10 +290,10 @@ func GetStdHandle(stdhandle int32) (handle int32, errno int) { return } -func FindFirstFile(name *uint16, data *Win32finddata) (handle int32, errno int) { +func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, errno int) { r0, _, e1 := Syscall(procFindFirstFileW, 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(data)), 0) - handle = int32(r0) - if handle == -1 { + handle = Handle(r0) + if handle == InvalidHandle { if e1 != 0 { errno = int(e1) } else { @@ -300,7 +305,7 @@ func FindFirstFile(name *uint16, data *Win32finddata) (handle int32, errno int) return } -func FindNextFile(handle int32, data *Win32finddata) (errno int) { +func FindNextFile(handle Handle, data *Win32finddata) (errno int) { r1, _, e1 := Syscall(procFindNextFileW, 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0) if int(r1) == 0 { if e1 != 0 { @@ -314,7 +319,7 @@ func FindNextFile(handle int32, data *Win32finddata) (errno int) { return } -func FindClose(handle int32) (errno int) { +func FindClose(handle Handle) (errno int) { r1, _, e1 := Syscall(procFindClose, 1, uintptr(handle), 0, 0) if int(r1) == 0 { if e1 != 0 { @@ -328,7 +333,7 @@ func FindClose(handle int32) (errno int) { return } -func GetFileInformationByHandle(handle int32, data *ByHandleFileInformation) (errno int) { +func GetFileInformationByHandle(handle Handle, data *ByHandleFileInformation) (errno int) { r1, _, e1 := Syscall(procGetFileInformationByHandle, 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0) if int(r1) == 0 { if e1 != 0 { @@ -441,7 +446,7 @@ func GetComputerName(buf *uint16, n *uint32) (errno int) { return } -func SetEndOfFile(handle int32) (errno int) { +func SetEndOfFile(handle Handle) (errno int) { r1, _, e1 := Syscall(procSetEndOfFile, 1, uintptr(handle), 0, 0) if int(r1) == 0 { if e1 != 0 { @@ -480,9 +485,9 @@ func GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, errno int) { return } -func CreateIoCompletionPort(filehandle int32, cphandle int32, key uint32, threadcnt uint32) (handle int32, errno int) { +func CreateIoCompletionPort(filehandle Handle, cphandle Handle, key uint32, threadcnt uint32) (handle Handle, errno int) { r0, _, e1 := Syscall6(procCreateIoCompletionPort, 4, uintptr(filehandle), uintptr(cphandle), uintptr(key), uintptr(threadcnt), 0, 0) - handle = int32(r0) + handle = Handle(r0) if handle == 0 { if e1 != 0 { errno = int(e1) @@ -495,7 +500,7 @@ func CreateIoCompletionPort(filehandle int32, cphandle int32, key uint32, thread return } -func GetQueuedCompletionStatus(cphandle int32, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (errno int) { +func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (errno int) { r1, _, e1 := Syscall6(procGetQueuedCompletionStatus, 5, uintptr(cphandle), uintptr(unsafe.Pointer(qty)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(overlapped)), uintptr(timeout), 0) if int(r1) == 0 { if e1 != 0 { @@ -509,7 +514,7 @@ func GetQueuedCompletionStatus(cphandle int32, qty *uint32, key *uint32, overlap return } -func CancelIo(s uint32) (errno int) { +func CancelIo(s Handle) (errno int) { r1, _, e1 := Syscall(procCancelIo, 1, uintptr(s), 0, 0) if int(r1) == 0 { if e1 != 0 { @@ -543,7 +548,7 @@ func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityA return } -func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle int32, errno int) { +func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, errno int) { var _p0 uint32 if inheritHandle { _p0 = 1 @@ -551,7 +556,7 @@ func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle int32, errno _p0 = 0 } r0, _, e1 := Syscall(procOpenProcess, 3, uintptr(da), uintptr(_p0), uintptr(pid)) - handle = int32(r0) + handle = Handle(r0) if handle == 0 { if e1 != 0 { errno = int(e1) @@ -564,7 +569,7 @@ func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle int32, errno return } -func TerminateProcess(handle int32, exitcode uint32) (errno int) { +func TerminateProcess(handle Handle, exitcode uint32) (errno int) { r1, _, e1 := Syscall(procTerminateProcess, 2, uintptr(handle), uintptr(exitcode), 0) if int(r1) == 0 { if e1 != 0 { @@ -578,7 +583,7 @@ func TerminateProcess(handle int32, exitcode uint32) (errno int) { return } -func GetExitCodeProcess(handle int32, exitcode *uint32) (errno int) { +func GetExitCodeProcess(handle Handle, exitcode *uint32) (errno int) { r1, _, e1 := Syscall(procGetExitCodeProcess, 2, uintptr(handle), uintptr(unsafe.Pointer(exitcode)), 0) if int(r1) == 0 { if e1 != 0 { @@ -606,9 +611,9 @@ func GetStartupInfo(startupInfo *StartupInfo) (errno int) { return } -func GetCurrentProcess() (pseudoHandle int32, errno int) { +func GetCurrentProcess() (pseudoHandle Handle, errno int) { r0, _, e1 := Syscall(procGetCurrentProcess, 0, 0, 0, 0) - pseudoHandle = int32(r0) + pseudoHandle = Handle(r0) if pseudoHandle == 0 { if e1 != 0 { errno = int(e1) @@ -621,7 +626,7 @@ func GetCurrentProcess() (pseudoHandle int32, errno int) { return } -func DuplicateHandle(hSourceProcessHandle int32, hSourceHandle int32, hTargetProcessHandle int32, lpTargetHandle *int32, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (errno int) { +func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetProcessHandle Handle, lpTargetHandle *Handle, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (errno int) { var _p0 uint32 if bInheritHandle { _p0 = 1 @@ -641,7 +646,7 @@ func DuplicateHandle(hSourceProcessHandle int32, hSourceHandle int32, hTargetPro return } -func WaitForSingleObject(handle int32, waitMilliseconds uint32) (event uint32, errno int) { +func WaitForSingleObject(handle Handle, waitMilliseconds uint32) (event uint32, errno int) { r0, _, e1 := Syscall(procWaitForSingleObject, 2, uintptr(handle), uintptr(waitMilliseconds), 0) event = uint32(r0) if event == 0xffffffff { @@ -671,7 +676,7 @@ func GetTempPath(buflen uint32, buf *uint16) (n uint32, errno int) { return } -func CreatePipe(readhandle *uint32, writehandle *uint32, sa *SecurityAttributes, size uint32) (errno int) { +func CreatePipe(readhandle *Handle, writehandle *Handle, sa *SecurityAttributes, size uint32) (errno int) { r1, _, e1 := Syscall6(procCreatePipe, 4, uintptr(unsafe.Pointer(readhandle)), uintptr(unsafe.Pointer(writehandle)), uintptr(unsafe.Pointer(sa)), uintptr(size), 0, 0) if int(r1) == 0 { if e1 != 0 { @@ -685,7 +690,7 @@ func CreatePipe(readhandle *uint32, writehandle *uint32, sa *SecurityAttributes, return } -func GetFileType(filehandle uint32) (n uint32, errno int) { +func GetFileType(filehandle Handle) (n uint32, errno int) { r0, _, e1 := Syscall(procGetFileType, 1, uintptr(filehandle), 0, 0) n = uint32(r0) if n == 0 { @@ -700,7 +705,7 @@ func GetFileType(filehandle uint32) (n uint32, errno int) { return } -func CryptAcquireContext(provhandle *uint32, container *uint16, provider *uint16, provtype uint32, flags uint32) (errno int) { +func CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16, provtype uint32, flags uint32) (errno int) { r1, _, e1 := Syscall6(procCryptAcquireContextW, 5, uintptr(unsafe.Pointer(provhandle)), uintptr(unsafe.Pointer(container)), uintptr(unsafe.Pointer(provider)), uintptr(provtype), uintptr(flags), 0) if int(r1) == 0 { if e1 != 0 { @@ -714,7 +719,7 @@ func CryptAcquireContext(provhandle *uint32, container *uint16, provider *uint16 return } -func CryptReleaseContext(provhandle uint32, flags uint32) (errno int) { +func CryptReleaseContext(provhandle Handle, flags uint32) (errno int) { r1, _, e1 := Syscall(procCryptReleaseContext, 2, uintptr(provhandle), uintptr(flags), 0) if int(r1) == 0 { if e1 != 0 { @@ -728,7 +733,7 @@ func CryptReleaseContext(provhandle uint32, flags uint32) (errno int) { return } -func CryptGenRandom(provhandle uint32, buflen uint32, buf *byte) (errno int) { +func CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (errno int) { r1, _, e1 := Syscall(procCryptGenRandom, 3, uintptr(provhandle), uintptr(buflen), uintptr(unsafe.Pointer(buf))) if int(r1) == 0 { if e1 != 0 { @@ -800,7 +805,7 @@ func SetEnvironmentVariable(name *uint16, value *uint16) (errno int) { return } -func SetFileTime(handle int32, ctime *Filetime, atime *Filetime, wtime *Filetime) (errno int) { +func SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (errno int) { r1, _, e1 := Syscall6(procSetFileTime, 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0) if int(r1) == 0 { if e1 != 0 { @@ -864,9 +869,9 @@ func CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, err return } -func LocalFree(hmem uint32) (handle uint32, errno int) { +func LocalFree(hmem Handle) (handle Handle, errno int) { r0, _, e1 := Syscall(procLocalFree, 1, uintptr(hmem), 0, 0) - handle = uint32(r0) + handle = Handle(r0) if handle != 0 { if e1 != 0 { errno = int(e1) @@ -879,7 +884,7 @@ func LocalFree(hmem uint32) (handle uint32, errno int) { return } -func SetHandleInformation(handle int32, mask uint32, flags uint32) (errno int) { +func SetHandleInformation(handle Handle, mask uint32, flags uint32) (errno int) { r1, _, e1 := Syscall(procSetHandleInformation, 3, uintptr(handle), uintptr(mask), uintptr(flags)) if int(r1) == 0 { if e1 != 0 { @@ -893,7 +898,7 @@ func SetHandleInformation(handle int32, mask uint32, flags uint32) (errno int) { return } -func FlushFileBuffers(handle int32) (errno int) { +func FlushFileBuffers(handle Handle) (errno int) { r1, _, e1 := Syscall(procFlushFileBuffers, 1, uintptr(handle), 0, 0) if int(r1) == 0 { if e1 != 0 { @@ -922,9 +927,9 @@ func GetFullPathName(path *uint16, buflen uint32, buf *uint16, fname **uint16) ( return } -func CreateFileMapping(fhandle int32, sa *SecurityAttributes, prot uint32, maxSizeHigh uint32, maxSizeLow uint32, name *uint16) (handle int32, errno int) { +func CreateFileMapping(fhandle Handle, sa *SecurityAttributes, prot uint32, maxSizeHigh uint32, maxSizeLow uint32, name *uint16) (handle Handle, errno int) { r0, _, e1 := Syscall6(procCreateFileMappingW, 6, uintptr(fhandle), uintptr(unsafe.Pointer(sa)), uintptr(prot), uintptr(maxSizeHigh), uintptr(maxSizeLow), uintptr(unsafe.Pointer(name))) - handle = int32(r0) + handle = Handle(r0) if handle == 0 { if e1 != 0 { errno = int(e1) @@ -937,7 +942,7 @@ func CreateFileMapping(fhandle int32, sa *SecurityAttributes, prot uint32, maxSi return } -func MapViewOfFile(handle int32, access uint32, offsetHigh uint32, offsetLow uint32, length uintptr) (addr uintptr, errno int) { +func MapViewOfFile(handle Handle, access uint32, offsetHigh uint32, offsetLow uint32, length uintptr) (addr uintptr, errno int) { r0, _, e1 := Syscall6(procMapViewOfFile, 5, uintptr(handle), uintptr(access), uintptr(offsetHigh), uintptr(offsetLow), uintptr(length), 0) addr = uintptr(r0) if addr == 0 { @@ -1008,6 +1013,20 @@ func VirtualUnlock(addr uintptr, length uintptr) (errno int) { return } +func TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint32, overlapped *Overlapped, transmitFileBuf *TransmitFileBuffers, flags uint32) (errno int) { + r1, _, e1 := Syscall9(procTransmitFile, 7, uintptr(s), uintptr(handle), uintptr(bytesToWrite), uintptr(bytsPerSend), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transmitFileBuf)), uintptr(flags), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + func WSAStartup(verreq uint32, data *WSAData) (sockerrno int) { r0, _, _ := Syscall(procWSAStartup, 2, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0) sockerrno = int(r0) @@ -1028,10 +1047,24 @@ func WSACleanup() (errno int) { return } -func socket(af int32, typ int32, protocol int32) (handle int32, errno int) { +func WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (errno int) { + r1, _, e1 := Syscall9(procWSAIoctl, 9, uintptr(s), uintptr(iocc), uintptr(unsafe.Pointer(inbuf)), uintptr(cbif), uintptr(unsafe.Pointer(outbuf)), uintptr(cbob), uintptr(unsafe.Pointer(cbbr)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine)) + if int(r1) == -1 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func socket(af int32, typ int32, protocol int32) (handle Handle, errno int) { r0, _, e1 := Syscall(procsocket, 3, uintptr(af), uintptr(typ), uintptr(protocol)) - handle = int32(r0) - if handle == -1 { + handle = Handle(r0) + if handle == InvalidHandle { if e1 != 0 { errno = int(e1) } else { @@ -1043,7 +1076,7 @@ func socket(af int32, typ int32, protocol int32) (handle int32, errno int) { return } -func setsockopt(s int32, level int32, optname int32, optval *byte, optlen int32) (errno int) { +func setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (errno int) { r1, _, e1 := Syscall6(procsetsockopt, 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(optlen), 0) if int(r1) == -1 { if e1 != 0 { @@ -1057,7 +1090,7 @@ func setsockopt(s int32, level int32, optname int32, optval *byte, optlen int32) return } -func bind(s int32, name uintptr, namelen int32) (errno int) { +func bind(s Handle, name uintptr, namelen int32) (errno int) { r1, _, e1 := Syscall(procbind, 3, uintptr(s), uintptr(name), uintptr(namelen)) if int(r1) == -1 { if e1 != 0 { @@ -1071,7 +1104,7 @@ func bind(s int32, name uintptr, namelen int32) (errno int) { return } -func connect(s int32, name uintptr, namelen int32) (errno int) { +func connect(s Handle, name uintptr, namelen int32) (errno int) { r1, _, e1 := Syscall(procconnect, 3, uintptr(s), uintptr(name), uintptr(namelen)) if int(r1) == -1 { if e1 != 0 { @@ -1085,7 +1118,7 @@ func connect(s int32, name uintptr, namelen int32) (errno int) { return } -func getsockname(s int32, rsa *RawSockaddrAny, addrlen *int32) (errno int) { +func getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (errno int) { r1, _, e1 := Syscall(procgetsockname, 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) if int(r1) == -1 { if e1 != 0 { @@ -1099,7 +1132,7 @@ func getsockname(s int32, rsa *RawSockaddrAny, addrlen *int32) (errno int) { return } -func getpeername(s int32, rsa *RawSockaddrAny, addrlen *int32) (errno int) { +func getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (errno int) { r1, _, e1 := Syscall(procgetpeername, 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) if int(r1) == -1 { if e1 != 0 { @@ -1113,7 +1146,7 @@ func getpeername(s int32, rsa *RawSockaddrAny, addrlen *int32) (errno int) { return } -func listen(s int32, backlog int32) (errno int) { +func listen(s Handle, backlog int32) (errno int) { r1, _, e1 := Syscall(proclisten, 2, uintptr(s), uintptr(backlog), 0) if int(r1) == -1 { if e1 != 0 { @@ -1127,7 +1160,7 @@ func listen(s int32, backlog int32) (errno int) { return } -func shutdown(s int32, how int32) (errno int) { +func shutdown(s Handle, how int32) (errno int) { r1, _, e1 := Syscall(procshutdown, 2, uintptr(s), uintptr(how), 0) if int(r1) == -1 { if e1 != 0 { @@ -1141,7 +1174,7 @@ func shutdown(s int32, how int32) (errno int) { return } -func Closesocket(s int32) (errno int) { +func Closesocket(s Handle) (errno int) { r1, _, e1 := Syscall(procclosesocket, 1, uintptr(s), 0, 0) if int(r1) == -1 { if e1 != 0 { @@ -1155,7 +1188,7 @@ func Closesocket(s int32) (errno int) { return } -func AcceptEx(ls uint32, as uint32, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (errno int) { +func AcceptEx(ls Handle, as Handle, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (errno int) { r1, _, e1 := Syscall9(procAcceptEx, 8, uintptr(ls), uintptr(as), uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(overlapped)), 0) if int(r1) == 0 { if e1 != 0 { @@ -1174,7 +1207,7 @@ func GetAcceptExSockaddrs(buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen return } -func WSARecv(s uint32, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (errno int) { +func WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (errno int) { r1, _, e1 := Syscall9(procWSARecv, 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0) if int(r1) == -1 { if e1 != 0 { @@ -1188,7 +1221,7 @@ func WSARecv(s uint32, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32 return } -func WSASend(s uint32, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (errno int) { +func WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (errno int) { r1, _, e1 := Syscall9(procWSASend, 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0) if int(r1) == -1 { if e1 != 0 { @@ -1202,7 +1235,7 @@ func WSASend(s uint32, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, return } -func WSARecvFrom(s uint32, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (errno int) { +func WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (errno int) { r1, _, e1 := Syscall9(procWSARecvFrom, 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine))) if int(r1) == -1 { if e1 != 0 { @@ -1216,7 +1249,7 @@ func WSARecvFrom(s uint32, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *ui return } -func WSASendTo(s uint32, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (errno int) { +func WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (errno int) { r1, _, e1 := Syscall9(procWSASendTo, 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(to)), uintptr(tolen), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine))) if int(r1) == -1 { if e1 != 0 { @@ -1276,3 +1309,15 @@ func DnsRecordListFree(rl *DNSRecord, freetype uint32) { Syscall(procDnsRecordListFree, 2, uintptr(unsafe.Pointer(rl)), uintptr(freetype), 0) return } + +func GetIfEntry(pIfRow *MibIfRow) (errcode int) { + r0, _, _ := Syscall(procGetIfEntry, 1, uintptr(unsafe.Pointer(pIfRow)), 0, 0) + errcode = int(r0) + return +} + +func GetAdaptersInfo(ai *IpAdapterInfo, ol *uint32) (errcode int) { + r0, _, _ := Syscall(procGetAdaptersInfo, 2, uintptr(unsafe.Pointer(ai)), uintptr(unsafe.Pointer(ol)), 0) + errcode = int(r0) + return +} diff --git a/src/pkg/syscall/zsyscall_windows_amd64.go b/src/pkg/syscall/zsyscall_windows_amd64.go new file mode 100644 index 000000000..e7d09fbc2 --- /dev/null +++ b/src/pkg/syscall/zsyscall_windows_amd64.go @@ -0,0 +1,1323 @@ +// mksyscall_windows.pl syscall_windows.go syscall_windows_amd64.go +// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT + +package syscall + +import "unsafe" + +var ( + modkernel32 = loadDll("kernel32.dll") + modadvapi32 = loadDll("advapi32.dll") + modshell32 = loadDll("shell32.dll") + modwsock32 = loadDll("wsock32.dll") + modws2_32 = loadDll("ws2_32.dll") + moddnsapi = loadDll("dnsapi.dll") + modiphlpapi = loadDll("iphlpapi.dll") + + procGetLastError = getSysProcAddr(modkernel32, "GetLastError") + procLoadLibraryW = getSysProcAddr(modkernel32, "LoadLibraryW") + procFreeLibrary = getSysProcAddr(modkernel32, "FreeLibrary") + procGetProcAddress = getSysProcAddr(modkernel32, "GetProcAddress") + procGetVersion = getSysProcAddr(modkernel32, "GetVersion") + procFormatMessageW = getSysProcAddr(modkernel32, "FormatMessageW") + procExitProcess = getSysProcAddr(modkernel32, "ExitProcess") + procCreateFileW = getSysProcAddr(modkernel32, "CreateFileW") + procReadFile = getSysProcAddr(modkernel32, "ReadFile") + procWriteFile = getSysProcAddr(modkernel32, "WriteFile") + procSetFilePointer = getSysProcAddr(modkernel32, "SetFilePointer") + procCloseHandle = getSysProcAddr(modkernel32, "CloseHandle") + procGetStdHandle = getSysProcAddr(modkernel32, "GetStdHandle") + procFindFirstFileW = getSysProcAddr(modkernel32, "FindFirstFileW") + procFindNextFileW = getSysProcAddr(modkernel32, "FindNextFileW") + procFindClose = getSysProcAddr(modkernel32, "FindClose") + procGetFileInformationByHandle = getSysProcAddr(modkernel32, "GetFileInformationByHandle") + procGetCurrentDirectoryW = getSysProcAddr(modkernel32, "GetCurrentDirectoryW") + procSetCurrentDirectoryW = getSysProcAddr(modkernel32, "SetCurrentDirectoryW") + procCreateDirectoryW = getSysProcAddr(modkernel32, "CreateDirectoryW") + procRemoveDirectoryW = getSysProcAddr(modkernel32, "RemoveDirectoryW") + procDeleteFileW = getSysProcAddr(modkernel32, "DeleteFileW") + procMoveFileW = getSysProcAddr(modkernel32, "MoveFileW") + procGetComputerNameW = getSysProcAddr(modkernel32, "GetComputerNameW") + procSetEndOfFile = getSysProcAddr(modkernel32, "SetEndOfFile") + procGetSystemTimeAsFileTime = getSysProcAddr(modkernel32, "GetSystemTimeAsFileTime") + procSleep = getSysProcAddr(modkernel32, "Sleep") + procGetTimeZoneInformation = getSysProcAddr(modkernel32, "GetTimeZoneInformation") + procCreateIoCompletionPort = getSysProcAddr(modkernel32, "CreateIoCompletionPort") + procGetQueuedCompletionStatus = getSysProcAddr(modkernel32, "GetQueuedCompletionStatus") + procCancelIo = getSysProcAddr(modkernel32, "CancelIo") + procCreateProcessW = getSysProcAddr(modkernel32, "CreateProcessW") + procOpenProcess = getSysProcAddr(modkernel32, "OpenProcess") + procTerminateProcess = getSysProcAddr(modkernel32, "TerminateProcess") + procGetExitCodeProcess = getSysProcAddr(modkernel32, "GetExitCodeProcess") + procGetStartupInfoW = getSysProcAddr(modkernel32, "GetStartupInfoW") + procGetCurrentProcess = getSysProcAddr(modkernel32, "GetCurrentProcess") + procDuplicateHandle = getSysProcAddr(modkernel32, "DuplicateHandle") + procWaitForSingleObject = getSysProcAddr(modkernel32, "WaitForSingleObject") + procGetTempPathW = getSysProcAddr(modkernel32, "GetTempPathW") + procCreatePipe = getSysProcAddr(modkernel32, "CreatePipe") + procGetFileType = getSysProcAddr(modkernel32, "GetFileType") + procCryptAcquireContextW = getSysProcAddr(modadvapi32, "CryptAcquireContextW") + procCryptReleaseContext = getSysProcAddr(modadvapi32, "CryptReleaseContext") + procCryptGenRandom = getSysProcAddr(modadvapi32, "CryptGenRandom") + procGetEnvironmentStringsW = getSysProcAddr(modkernel32, "GetEnvironmentStringsW") + procFreeEnvironmentStringsW = getSysProcAddr(modkernel32, "FreeEnvironmentStringsW") + procGetEnvironmentVariableW = getSysProcAddr(modkernel32, "GetEnvironmentVariableW") + procSetEnvironmentVariableW = getSysProcAddr(modkernel32, "SetEnvironmentVariableW") + procSetFileTime = getSysProcAddr(modkernel32, "SetFileTime") + procGetFileAttributesW = getSysProcAddr(modkernel32, "GetFileAttributesW") + procSetFileAttributesW = getSysProcAddr(modkernel32, "SetFileAttributesW") + procGetCommandLineW = getSysProcAddr(modkernel32, "GetCommandLineW") + procCommandLineToArgvW = getSysProcAddr(modshell32, "CommandLineToArgvW") + procLocalFree = getSysProcAddr(modkernel32, "LocalFree") + procSetHandleInformation = getSysProcAddr(modkernel32, "SetHandleInformation") + procFlushFileBuffers = getSysProcAddr(modkernel32, "FlushFileBuffers") + procGetFullPathNameW = getSysProcAddr(modkernel32, "GetFullPathNameW") + procCreateFileMappingW = getSysProcAddr(modkernel32, "CreateFileMappingW") + procMapViewOfFile = getSysProcAddr(modkernel32, "MapViewOfFile") + procUnmapViewOfFile = getSysProcAddr(modkernel32, "UnmapViewOfFile") + procFlushViewOfFile = getSysProcAddr(modkernel32, "FlushViewOfFile") + procVirtualLock = getSysProcAddr(modkernel32, "VirtualLock") + procVirtualUnlock = getSysProcAddr(modkernel32, "VirtualUnlock") + procTransmitFile = getSysProcAddr(modwsock32, "TransmitFile") + procWSAStartup = getSysProcAddr(modwsock32, "WSAStartup") + procWSACleanup = getSysProcAddr(modwsock32, "WSACleanup") + procWSAIoctl = getSysProcAddr(modws2_32, "WSAIoctl") + procsocket = getSysProcAddr(modwsock32, "socket") + procsetsockopt = getSysProcAddr(modwsock32, "setsockopt") + procbind = getSysProcAddr(modwsock32, "bind") + procconnect = getSysProcAddr(modwsock32, "connect") + procgetsockname = getSysProcAddr(modwsock32, "getsockname") + procgetpeername = getSysProcAddr(modwsock32, "getpeername") + proclisten = getSysProcAddr(modwsock32, "listen") + procshutdown = getSysProcAddr(modwsock32, "shutdown") + procclosesocket = getSysProcAddr(modwsock32, "closesocket") + procAcceptEx = getSysProcAddr(modwsock32, "AcceptEx") + procGetAcceptExSockaddrs = getSysProcAddr(modwsock32, "GetAcceptExSockaddrs") + procWSARecv = getSysProcAddr(modws2_32, "WSARecv") + procWSASend = getSysProcAddr(modws2_32, "WSASend") + procWSARecvFrom = getSysProcAddr(modws2_32, "WSARecvFrom") + procWSASendTo = getSysProcAddr(modws2_32, "WSASendTo") + procgethostbyname = getSysProcAddr(modws2_32, "gethostbyname") + procgetservbyname = getSysProcAddr(modws2_32, "getservbyname") + procntohs = getSysProcAddr(modws2_32, "ntohs") + procDnsQuery_W = getSysProcAddr(moddnsapi, "DnsQuery_W") + procDnsRecordListFree = getSysProcAddr(moddnsapi, "DnsRecordListFree") + procGetIfEntry = getSysProcAddr(modiphlpapi, "GetIfEntry") + procGetAdaptersInfo = getSysProcAddr(modiphlpapi, "GetAdaptersInfo") +) + +func GetLastError() (lasterrno int) { + r0, _, _ := Syscall(procGetLastError, 0, 0, 0, 0) + lasterrno = int(r0) + return +} + +func LoadLibrary(libname string) (handle Handle, errno int) { + r0, _, e1 := Syscall(procLoadLibraryW, 1, uintptr(unsafe.Pointer(StringToUTF16Ptr(libname))), 0, 0) + handle = Handle(r0) + if handle == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func FreeLibrary(handle Handle) (errno int) { + r1, _, e1 := Syscall(procFreeLibrary, 1, uintptr(handle), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetProcAddress(module Handle, procname string) (proc Handle, errno int) { + r0, _, e1 := Syscall(procGetProcAddress, 2, uintptr(module), uintptr(unsafe.Pointer(StringBytePtr(procname))), 0) + proc = Handle(r0) + if proc == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetVersion() (ver uint32, errno int) { + r0, _, e1 := Syscall(procGetVersion, 0, 0, 0, 0) + ver = uint32(r0) + if ver == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func FormatMessage(flags uint32, msgsrc uint32, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, errno int) { + var _p0 *uint16 + if len(buf) > 0 { + _p0 = &buf[0] + } + r0, _, e1 := Syscall9(procFormatMessageW, 7, uintptr(flags), uintptr(msgsrc), uintptr(msgid), uintptr(langid), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(args)), 0, 0) + n = uint32(r0) + if n == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func ExitProcess(exitcode uint32) { + Syscall(procExitProcess, 1, uintptr(exitcode), 0, 0) + return +} + +func CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle Handle, errno int) { + r0, _, e1 := Syscall9(procCreateFileW, 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0) + handle = Handle(r0) + if handle == InvalidHandle { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func ReadFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (errno int) { + var _p0 *byte + if len(buf) > 0 { + _p0 = &buf[0] + } + r1, _, e1 := Syscall6(procReadFile, 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func WriteFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (errno int) { + var _p0 *byte + if len(buf) > 0 { + _p0 = &buf[0] + } + r1, _, e1 := Syscall6(procWriteFile, 5, uintptr(handle), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(unsafe.Pointer(done)), uintptr(unsafe.Pointer(overlapped)), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence uint32) (newlowoffset uint32, errno int) { + r0, _, e1 := Syscall6(procSetFilePointer, 4, uintptr(handle), uintptr(lowoffset), uintptr(unsafe.Pointer(highoffsetptr)), uintptr(whence), 0, 0) + newlowoffset = uint32(r0) + if newlowoffset == 0xffffffff { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func CloseHandle(handle Handle) (errno int) { + r1, _, e1 := Syscall(procCloseHandle, 1, uintptr(handle), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetStdHandle(stdhandle int) (handle Handle, errno int) { + r0, _, e1 := Syscall(procGetStdHandle, 1, uintptr(stdhandle), 0, 0) + handle = Handle(r0) + if handle == InvalidHandle { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, errno int) { + r0, _, e1 := Syscall(procFindFirstFileW, 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(data)), 0) + handle = Handle(r0) + if handle == InvalidHandle { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func FindNextFile(handle Handle, data *Win32finddata) (errno int) { + r1, _, e1 := Syscall(procFindNextFileW, 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func FindClose(handle Handle) (errno int) { + r1, _, e1 := Syscall(procFindClose, 1, uintptr(handle), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetFileInformationByHandle(handle Handle, data *ByHandleFileInformation) (errno int) { + r1, _, e1 := Syscall(procGetFileInformationByHandle, 2, uintptr(handle), uintptr(unsafe.Pointer(data)), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetCurrentDirectory(buflen uint32, buf *uint16) (n uint32, errno int) { + r0, _, e1 := Syscall(procGetCurrentDirectoryW, 2, uintptr(buflen), uintptr(unsafe.Pointer(buf)), 0) + n = uint32(r0) + if n == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func SetCurrentDirectory(path *uint16) (errno int) { + r1, _, e1 := Syscall(procSetCurrentDirectoryW, 1, uintptr(unsafe.Pointer(path)), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func CreateDirectory(path *uint16, sa *SecurityAttributes) (errno int) { + r1, _, e1 := Syscall(procCreateDirectoryW, 2, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(sa)), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func RemoveDirectory(path *uint16) (errno int) { + r1, _, e1 := Syscall(procRemoveDirectoryW, 1, uintptr(unsafe.Pointer(path)), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func DeleteFile(path *uint16) (errno int) { + r1, _, e1 := Syscall(procDeleteFileW, 1, uintptr(unsafe.Pointer(path)), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func MoveFile(from *uint16, to *uint16) (errno int) { + r1, _, e1 := Syscall(procMoveFileW, 2, uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(to)), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetComputerName(buf *uint16, n *uint32) (errno int) { + r1, _, e1 := Syscall(procGetComputerNameW, 2, uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n)), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func SetEndOfFile(handle Handle) (errno int) { + r1, _, e1 := Syscall(procSetEndOfFile, 1, uintptr(handle), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetSystemTimeAsFileTime(time *Filetime) { + Syscall(procGetSystemTimeAsFileTime, 1, uintptr(unsafe.Pointer(time)), 0, 0) + return +} + +func sleep(msec uint32) { + Syscall(procSleep, 1, uintptr(msec), 0, 0) + return +} + +func GetTimeZoneInformation(tzi *Timezoneinformation) (rc uint32, errno int) { + r0, _, e1 := Syscall(procGetTimeZoneInformation, 1, uintptr(unsafe.Pointer(tzi)), 0, 0) + rc = uint32(r0) + if rc == 0xffffffff { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func CreateIoCompletionPort(filehandle Handle, cphandle Handle, key uint32, threadcnt uint32) (handle Handle, errno int) { + r0, _, e1 := Syscall6(procCreateIoCompletionPort, 4, uintptr(filehandle), uintptr(cphandle), uintptr(key), uintptr(threadcnt), 0, 0) + handle = Handle(r0) + if handle == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (errno int) { + r1, _, e1 := Syscall6(procGetQueuedCompletionStatus, 5, uintptr(cphandle), uintptr(unsafe.Pointer(qty)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(overlapped)), uintptr(timeout), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func CancelIo(s Handle) (errno int) { + r1, _, e1 := Syscall(procCancelIo, 1, uintptr(s), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (errno int) { + var _p0 uint32 + if inheritHandles { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := Syscall12(procCreateProcessW, 10, uintptr(unsafe.Pointer(appName)), uintptr(unsafe.Pointer(commandLine)), uintptr(unsafe.Pointer(procSecurity)), uintptr(unsafe.Pointer(threadSecurity)), uintptr(_p0), uintptr(creationFlags), uintptr(unsafe.Pointer(env)), uintptr(unsafe.Pointer(currentDir)), uintptr(unsafe.Pointer(startupInfo)), uintptr(unsafe.Pointer(outProcInfo)), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, errno int) { + var _p0 uint32 + if inheritHandle { + _p0 = 1 + } else { + _p0 = 0 + } + r0, _, e1 := Syscall(procOpenProcess, 3, uintptr(da), uintptr(_p0), uintptr(pid)) + handle = Handle(r0) + if handle == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func TerminateProcess(handle Handle, exitcode uint32) (errno int) { + r1, _, e1 := Syscall(procTerminateProcess, 2, uintptr(handle), uintptr(exitcode), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetExitCodeProcess(handle Handle, exitcode *uint32) (errno int) { + r1, _, e1 := Syscall(procGetExitCodeProcess, 2, uintptr(handle), uintptr(unsafe.Pointer(exitcode)), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetStartupInfo(startupInfo *StartupInfo) (errno int) { + r1, _, e1 := Syscall(procGetStartupInfoW, 1, uintptr(unsafe.Pointer(startupInfo)), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetCurrentProcess() (pseudoHandle Handle, errno int) { + r0, _, e1 := Syscall(procGetCurrentProcess, 0, 0, 0, 0) + pseudoHandle = Handle(r0) + if pseudoHandle == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetProcessHandle Handle, lpTargetHandle *Handle, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (errno int) { + var _p0 uint32 + if bInheritHandle { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := Syscall9(procDuplicateHandle, 7, uintptr(hSourceProcessHandle), uintptr(hSourceHandle), uintptr(hTargetProcessHandle), uintptr(unsafe.Pointer(lpTargetHandle)), uintptr(dwDesiredAccess), uintptr(_p0), uintptr(dwOptions), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func WaitForSingleObject(handle Handle, waitMilliseconds uint32) (event uint32, errno int) { + r0, _, e1 := Syscall(procWaitForSingleObject, 2, uintptr(handle), uintptr(waitMilliseconds), 0) + event = uint32(r0) + if event == 0xffffffff { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetTempPath(buflen uint32, buf *uint16) (n uint32, errno int) { + r0, _, e1 := Syscall(procGetTempPathW, 2, uintptr(buflen), uintptr(unsafe.Pointer(buf)), 0) + n = uint32(r0) + if n == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func CreatePipe(readhandle *Handle, writehandle *Handle, sa *SecurityAttributes, size uint32) (errno int) { + r1, _, e1 := Syscall6(procCreatePipe, 4, uintptr(unsafe.Pointer(readhandle)), uintptr(unsafe.Pointer(writehandle)), uintptr(unsafe.Pointer(sa)), uintptr(size), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetFileType(filehandle Handle) (n uint32, errno int) { + r0, _, e1 := Syscall(procGetFileType, 1, uintptr(filehandle), 0, 0) + n = uint32(r0) + if n == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func CryptAcquireContext(provhandle *Handle, container *uint16, provider *uint16, provtype uint32, flags uint32) (errno int) { + r1, _, e1 := Syscall6(procCryptAcquireContextW, 5, uintptr(unsafe.Pointer(provhandle)), uintptr(unsafe.Pointer(container)), uintptr(unsafe.Pointer(provider)), uintptr(provtype), uintptr(flags), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func CryptReleaseContext(provhandle Handle, flags uint32) (errno int) { + r1, _, e1 := Syscall(procCryptReleaseContext, 2, uintptr(provhandle), uintptr(flags), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func CryptGenRandom(provhandle Handle, buflen uint32, buf *byte) (errno int) { + r1, _, e1 := Syscall(procCryptGenRandom, 3, uintptr(provhandle), uintptr(buflen), uintptr(unsafe.Pointer(buf))) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetEnvironmentStrings() (envs *uint16, errno int) { + r0, _, e1 := Syscall(procGetEnvironmentStringsW, 0, 0, 0, 0) + envs = (*uint16)(unsafe.Pointer(r0)) + if envs == nil { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func FreeEnvironmentStrings(envs *uint16) (errno int) { + r1, _, e1 := Syscall(procFreeEnvironmentStringsW, 1, uintptr(unsafe.Pointer(envs)), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetEnvironmentVariable(name *uint16, buffer *uint16, size uint32) (n uint32, errno int) { + r0, _, e1 := Syscall(procGetEnvironmentVariableW, 3, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(size)) + n = uint32(r0) + if n == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func SetEnvironmentVariable(name *uint16, value *uint16) (errno int) { + r1, _, e1 := Syscall(procSetEnvironmentVariableW, 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (errno int) { + r1, _, e1 := Syscall6(procSetFileTime, 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetFileAttributes(name *uint16) (attrs uint32, errno int) { + r0, _, e1 := Syscall(procGetFileAttributesW, 1, uintptr(unsafe.Pointer(name)), 0, 0) + attrs = uint32(r0) + if attrs == INVALID_FILE_ATTRIBUTES { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func SetFileAttributes(name *uint16, attrs uint32) (errno int) { + r1, _, e1 := Syscall(procSetFileAttributesW, 2, uintptr(unsafe.Pointer(name)), uintptr(attrs), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetCommandLine() (cmd *uint16) { + r0, _, _ := Syscall(procGetCommandLineW, 0, 0, 0, 0) + cmd = (*uint16)(unsafe.Pointer(r0)) + return +} + +func CommandLineToArgv(cmd *uint16, argc *int32) (argv *[8192]*[8192]uint16, errno int) { + r0, _, e1 := Syscall(procCommandLineToArgvW, 2, uintptr(unsafe.Pointer(cmd)), uintptr(unsafe.Pointer(argc)), 0) + argv = (*[8192]*[8192]uint16)(unsafe.Pointer(r0)) + if argv == nil { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func LocalFree(hmem Handle) (handle Handle, errno int) { + r0, _, e1 := Syscall(procLocalFree, 1, uintptr(hmem), 0, 0) + handle = Handle(r0) + if handle != 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func SetHandleInformation(handle Handle, mask uint32, flags uint32) (errno int) { + r1, _, e1 := Syscall(procSetHandleInformation, 3, uintptr(handle), uintptr(mask), uintptr(flags)) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func FlushFileBuffers(handle Handle) (errno int) { + r1, _, e1 := Syscall(procFlushFileBuffers, 1, uintptr(handle), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetFullPathName(path *uint16, buflen uint32, buf *uint16, fname **uint16) (n uint32, errno int) { + r0, _, e1 := Syscall6(procGetFullPathNameW, 4, uintptr(unsafe.Pointer(path)), uintptr(buflen), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(fname)), 0, 0) + n = uint32(r0) + if n == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func CreateFileMapping(fhandle Handle, sa *SecurityAttributes, prot uint32, maxSizeHigh uint32, maxSizeLow uint32, name *uint16) (handle Handle, errno int) { + r0, _, e1 := Syscall6(procCreateFileMappingW, 6, uintptr(fhandle), uintptr(unsafe.Pointer(sa)), uintptr(prot), uintptr(maxSizeHigh), uintptr(maxSizeLow), uintptr(unsafe.Pointer(name))) + handle = Handle(r0) + if handle == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func MapViewOfFile(handle Handle, access uint32, offsetHigh uint32, offsetLow uint32, length uintptr) (addr uintptr, errno int) { + r0, _, e1 := Syscall6(procMapViewOfFile, 5, uintptr(handle), uintptr(access), uintptr(offsetHigh), uintptr(offsetLow), uintptr(length), 0) + addr = uintptr(r0) + if addr == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func UnmapViewOfFile(addr uintptr) (errno int) { + r1, _, e1 := Syscall(procUnmapViewOfFile, 1, uintptr(addr), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func FlushViewOfFile(addr uintptr, length uintptr) (errno int) { + r1, _, e1 := Syscall(procFlushViewOfFile, 2, uintptr(addr), uintptr(length), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func VirtualLock(addr uintptr, length uintptr) (errno int) { + r1, _, e1 := Syscall(procVirtualLock, 2, uintptr(addr), uintptr(length), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func VirtualUnlock(addr uintptr, length uintptr) (errno int) { + r1, _, e1 := Syscall(procVirtualUnlock, 2, uintptr(addr), uintptr(length), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func TransmitFile(s Handle, handle Handle, bytesToWrite uint32, bytsPerSend uint32, overlapped *Overlapped, transmitFileBuf *TransmitFileBuffers, flags uint32) (errno int) { + r1, _, e1 := Syscall9(procTransmitFile, 7, uintptr(s), uintptr(handle), uintptr(bytesToWrite), uintptr(bytsPerSend), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(transmitFileBuf)), uintptr(flags), 0, 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func WSAStartup(verreq uint32, data *WSAData) (sockerrno int) { + r0, _, _ := Syscall(procWSAStartup, 2, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0) + sockerrno = int(r0) + return +} + +func WSACleanup() (errno int) { + r1, _, e1 := Syscall(procWSACleanup, 0, 0, 0, 0) + if int(r1) == -1 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (errno int) { + r1, _, e1 := Syscall9(procWSAIoctl, 9, uintptr(s), uintptr(iocc), uintptr(unsafe.Pointer(inbuf)), uintptr(cbif), uintptr(unsafe.Pointer(outbuf)), uintptr(cbob), uintptr(unsafe.Pointer(cbbr)), uintptr(unsafe.Pointer(overlapped)), uintptr(completionRoutine)) + if int(r1) == -1 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func socket(af int32, typ int32, protocol int32) (handle Handle, errno int) { + r0, _, e1 := Syscall(procsocket, 3, uintptr(af), uintptr(typ), uintptr(protocol)) + handle = Handle(r0) + if handle == InvalidHandle { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (errno int) { + r1, _, e1 := Syscall6(procsetsockopt, 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(optlen), 0) + if int(r1) == -1 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func bind(s Handle, name uintptr, namelen int32) (errno int) { + r1, _, e1 := Syscall(procbind, 3, uintptr(s), uintptr(name), uintptr(namelen)) + if int(r1) == -1 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func connect(s Handle, name uintptr, namelen int32) (errno int) { + r1, _, e1 := Syscall(procconnect, 3, uintptr(s), uintptr(name), uintptr(namelen)) + if int(r1) == -1 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func getsockname(s Handle, rsa *RawSockaddrAny, addrlen *int32) (errno int) { + r1, _, e1 := Syscall(procgetsockname, 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + if int(r1) == -1 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func getpeername(s Handle, rsa *RawSockaddrAny, addrlen *int32) (errno int) { + r1, _, e1 := Syscall(procgetpeername, 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))) + if int(r1) == -1 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func listen(s Handle, backlog int32) (errno int) { + r1, _, e1 := Syscall(proclisten, 2, uintptr(s), uintptr(backlog), 0) + if int(r1) == -1 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func shutdown(s Handle, how int32) (errno int) { + r1, _, e1 := Syscall(procshutdown, 2, uintptr(s), uintptr(how), 0) + if int(r1) == -1 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func Closesocket(s Handle) (errno int) { + r1, _, e1 := Syscall(procclosesocket, 1, uintptr(s), 0, 0) + if int(r1) == -1 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func AcceptEx(ls Handle, as Handle, buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, recvd *uint32, overlapped *Overlapped) (errno int) { + r1, _, e1 := Syscall9(procAcceptEx, 8, uintptr(ls), uintptr(as), uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(overlapped)), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetAcceptExSockaddrs(buf *byte, rxdatalen uint32, laddrlen uint32, raddrlen uint32, lrsa **RawSockaddrAny, lrsalen *int32, rrsa **RawSockaddrAny, rrsalen *int32) { + Syscall9(procGetAcceptExSockaddrs, 8, uintptr(unsafe.Pointer(buf)), uintptr(rxdatalen), uintptr(laddrlen), uintptr(raddrlen), uintptr(unsafe.Pointer(lrsa)), uintptr(unsafe.Pointer(lrsalen)), uintptr(unsafe.Pointer(rrsa)), uintptr(unsafe.Pointer(rrsalen)), 0) + return +} + +func WSARecv(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, overlapped *Overlapped, croutine *byte) (errno int) { + r1, _, e1 := Syscall9(procWSARecv, 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0) + if int(r1) == -1 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func WSASend(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, overlapped *Overlapped, croutine *byte) (errno int) { + r1, _, e1 := Syscall9(procWSASend, 7, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0, 0) + if int(r1) == -1 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func WSARecvFrom(s Handle, bufs *WSABuf, bufcnt uint32, recvd *uint32, flags *uint32, from *RawSockaddrAny, fromlen *int32, overlapped *Overlapped, croutine *byte) (errno int) { + r1, _, e1 := Syscall9(procWSARecvFrom, 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(recvd)), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine))) + if int(r1) == -1 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func WSASendTo(s Handle, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to *RawSockaddrAny, tolen int32, overlapped *Overlapped, croutine *byte) (errno int) { + r1, _, e1 := Syscall9(procWSASendTo, 9, uintptr(s), uintptr(unsafe.Pointer(bufs)), uintptr(bufcnt), uintptr(unsafe.Pointer(sent)), uintptr(flags), uintptr(unsafe.Pointer(to)), uintptr(tolen), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine))) + if int(r1) == -1 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetHostByName(name string) (h *Hostent, errno int) { + r0, _, e1 := Syscall(procgethostbyname, 1, uintptr(unsafe.Pointer(StringBytePtr(name))), 0, 0) + h = (*Hostent)(unsafe.Pointer(r0)) + if h == nil { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetServByName(name string, proto string) (s *Servent, errno int) { + r0, _, e1 := Syscall(procgetservbyname, 2, uintptr(unsafe.Pointer(StringBytePtr(name))), uintptr(unsafe.Pointer(StringBytePtr(proto))), 0) + s = (*Servent)(unsafe.Pointer(r0)) + if s == nil { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func Ntohs(netshort uint16) (u uint16) { + r0, _, _ := Syscall(procntohs, 1, uintptr(netshort), 0, 0) + u = uint16(r0) + return +} + +func DnsQuery(name string, qtype uint16, options uint32, extra *byte, qrs **DNSRecord, pr *byte) (status uint32) { + r0, _, _ := Syscall6(procDnsQuery_W, 6, uintptr(unsafe.Pointer(StringToUTF16Ptr(name))), uintptr(qtype), uintptr(options), uintptr(unsafe.Pointer(extra)), uintptr(unsafe.Pointer(qrs)), uintptr(unsafe.Pointer(pr))) + status = uint32(r0) + return +} + +func DnsRecordListFree(rl *DNSRecord, freetype uint32) { + Syscall(procDnsRecordListFree, 2, uintptr(unsafe.Pointer(rl)), uintptr(freetype), 0) + return +} + +func GetIfEntry(pIfRow *MibIfRow) (errcode int) { + r0, _, _ := Syscall(procGetIfEntry, 1, uintptr(unsafe.Pointer(pIfRow)), 0, 0) + errcode = int(r0) + return +} + +func GetAdaptersInfo(ai *IpAdapterInfo, ol *uint32) (errcode int) { + r0, _, _ := Syscall(procGetAdaptersInfo, 2, uintptr(unsafe.Pointer(ai)), uintptr(unsafe.Pointer(ol)), 0) + errcode = int(r0) + return +} diff --git a/src/pkg/syscall/zsysnum_darwin_386.go b/src/pkg/syscall/zsysnum_darwin_386.go index 8d5c93478..50aec39f1 100644 --- a/src/pkg/syscall/zsysnum_darwin_386.go +++ b/src/pkg/syscall/zsysnum_darwin_386.go @@ -1,485 +1,355 @@ -// mksysnum_darwin.sh /home/rsc/pub/xnu-1228/bsd/kern/syscalls.master +// mksysnum_darwin.pl /usr/include/sys/syscall.h // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT package syscall const ( - // SYS_NOSYS = 0; // { int nosys(void); } { indirect syscall } - SYS_EXIT = 1 // { void exit(int rval); } - SYS_FORK = 2 // { int fork(void); } - SYS_READ = 3 // { user_ssize_t read(int fd, user_addr_t cbuf, user_size_t nbyte); } - SYS_WRITE = 4 // { user_ssize_t write(int fd, user_addr_t cbuf, user_size_t nbyte); } - SYS_OPEN = 5 // { int open(user_addr_t path, int flags, int mode); } - SYS_CLOSE = 6 // { int close(int fd); } - SYS_WAIT4 = 7 // { int wait4(int pid, user_addr_t status, int options, user_addr_t rusage); } - // SYS_NOSYS = 8; // { int nosys(void); } { old creat } - SYS_LINK = 9 // { int link(user_addr_t path, user_addr_t link); } - SYS_UNLINK = 10 // { int unlink(user_addr_t path); } - // SYS_NOSYS = 11; // { int nosys(void); } { old execv } - SYS_CHDIR = 12 // { int chdir(user_addr_t path); } - SYS_FCHDIR = 13 // { int fchdir(int fd); } - SYS_MKNOD = 14 // { int mknod(user_addr_t path, int mode, int dev); } - SYS_CHMOD = 15 // { int chmod(user_addr_t path, int mode); } - SYS_CHOWN = 16 // { int chown(user_addr_t path, int uid, int gid); } - SYS_OGETFSSTAT = 18 // { int ogetfsstat(user_addr_t buf, int bufsize, int flags); } - SYS_GETFSSTAT = 18 // { int getfsstat(user_addr_t buf, int bufsize, int flags); } - // SYS_NOSYS = 19; // { int nosys(void); } { old lseek } - SYS_GETPID = 20 // { int getpid(void); } - // SYS_NOSYS = 21; // { int nosys(void); } { old mount } - // SYS_NOSYS = 22; // { int nosys(void); } { old umount } - SYS_SETUID = 23 // { int setuid(uid_t uid); } - SYS_GETUID = 24 // { int getuid(void); } - SYS_GETEUID = 25 // { int geteuid(void); } - SYS_PTRACE = 26 // { int ptrace(int req, pid_t pid, caddr_t addr, int data); } - SYS_RECVMSG = 27 // { int recvmsg(int s, struct msghdr *msg, int flags); } - SYS_SENDMSG = 28 // { int sendmsg(int s, caddr_t msg, int flags); } - SYS_RECVFROM = 29 // { int recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, int *fromlenaddr); } - SYS_ACCEPT = 30 // { int accept(int s, caddr_t name, socklen_t *anamelen); } - SYS_GETPEERNAME = 31 // { int getpeername(int fdes, caddr_t asa, socklen_t *alen); } - SYS_GETSOCKNAME = 32 // { int getsockname(int fdes, caddr_t asa, socklen_t *alen); } - // SYS_NOSYS = 27; // { int nosys(void); } - // SYS_NOSYS = 28; // { int nosys(void); } - // SYS_NOSYS = 29; // { int nosys(void); } - // SYS_NOSYS = 30; // { int nosys(void); } - // SYS_NOSYS = 31; // { int nosys(void); } - // SYS_NOSYS = 32; // { int nosys(void); } - SYS_ACCESS = 33 // { int access(user_addr_t path, int flags); } - SYS_CHFLAGS = 34 // { int chflags(char *path, int flags); } - SYS_FCHFLAGS = 35 // { int fchflags(int fd, int flags); } - SYS_SYNC = 36 // { int sync(void); } - SYS_KILL = 37 // { int kill(int pid, int signum, int posix); } - // SYS_NOSYS = 38; // { int nosys(void); } { old stat } - SYS_GETPPID = 39 // { int getppid(void); } - // SYS_NOSYS = 40; // { int nosys(void); } { old lstat } - SYS_DUP = 41 // { int dup(u_int fd); } - SYS_PIPE = 42 // { int pipe(void); } - SYS_GETEGID = 43 // { int getegid(void); } - SYS_PROFIL = 44 // { int profil(short *bufbase, size_t bufsize, u_long pcoffset, u_int pcscale); } - // SYS_NOSYS = 45; // { int nosys(void); } { old ktrace } - SYS_SIGACTION = 46 // { int sigaction(int signum, struct __sigaction *nsa, struct sigaction *osa); } - SYS_GETGID = 47 // { int getgid(void); } - SYS_SIGPROCMASK = 48 // { int sigprocmask(int how, user_addr_t mask, user_addr_t omask); } - SYS_GETLOGIN = 49 // { int getlogin(char *namebuf, u_int namelen); } - SYS_SETLOGIN = 50 // { int setlogin(char *namebuf); } - SYS_ACCT = 51 // { int acct(char *path); } - SYS_SIGPENDING = 52 // { int sigpending(struct sigvec *osv); } - SYS_SIGALTSTACK = 53 // { int sigaltstack(struct sigaltstack *nss, struct sigaltstack *oss); } - SYS_IOCTL = 54 // { int ioctl(int fd, u_long com, caddr_t data); } - SYS_REBOOT = 55 // { int reboot(int opt, char *command); } - SYS_REVOKE = 56 // { int revoke(char *path); } - SYS_SYMLINK = 57 // { int symlink(char *path, char *link); } - SYS_READLINK = 58 // { int readlink(char *path, char *buf, int count); } - SYS_EXECVE = 59 // { int execve(char *fname, char **argp, char **envp); } - SYS_UMASK = 60 // { int umask(int newmask); } - SYS_CHROOT = 61 // { int chroot(user_addr_t path); } - // SYS_NOSYS = 62; // { int nosys(void); } { old fstat } - // SYS_NOSYS = 63; // { int nosys(void); } { used internally, reserved } - // SYS_NOSYS = 64; // { int nosys(void); } { old getpagesize } - SYS_MSYNC = 65 // { int msync(caddr_t addr, size_t len, int flags); } - SYS_VFORK = 66 // { int vfork(void); } - // SYS_NOSYS = 67; // { int nosys(void); } { old vread } - // SYS_NOSYS = 68; // { int nosys(void); } { old vwrite } - SYS_SBRK = 69 // { int sbrk(int incr) NO_SYSCALL_STUB; } - SYS_SSTK = 70 // { int sstk(int incr) NO_SYSCALL_STUB; } - // SYS_NOSYS = 71; // { int nosys(void); } { old mmap } - SYS_OVADVISE = 72 // { int ovadvise(void) NO_SYSCALL_STUB; } { old vadvise } - SYS_MUNMAP = 73 // { int munmap(caddr_t addr, size_t len); } - SYS_MPROTECT = 74 // { int mprotect(caddr_t addr, size_t len, int prot); } - SYS_MADVISE = 75 // { int madvise(caddr_t addr, size_t len, int behav); } - // SYS_NOSYS = 76; // { int nosys(void); } { old vhangup } - // SYS_NOSYS = 77; // { int nosys(void); } { old vlimit } - SYS_MINCORE = 78 // { int mincore(user_addr_t addr, user_size_t len, user_addr_t vec); } - SYS_GETGROUPS = 79 // { int getgroups(u_int gidsetsize, gid_t *gidset); } - SYS_SETGROUPS = 80 // { int setgroups(u_int gidsetsize, gid_t *gidset); } - SYS_GETPGRP = 81 // { int getpgrp(void); } - SYS_SETPGID = 82 // { int setpgid(int pid, int pgid); } - SYS_SETITIMER = 83 // { int setitimer(u_int which, struct itimerval *itv, struct itimerval *oitv); } - // SYS_NOSYS = 84; // { int nosys(void); } { old wait } - SYS_SWAPON = 85 // { int swapon(void); } - SYS_GETITIMER = 86 // { int getitimer(u_int which, struct itimerval *itv); } - // SYS_NOSYS = 87; // { int nosys(void); } { old gethostname } - // SYS_NOSYS = 88; // { int nosys(void); } { old sethostname } - SYS_GETDTABLESIZE = 89 // { int getdtablesize(void); } - SYS_DUP2 = 90 // { int dup2(u_int from, u_int to); } - // SYS_NOSYS = 91; // { int nosys(void); } { old getdopt } - SYS_FCNTL = 92 // { int fcntl(int fd, int cmd, long arg); } - SYS_SELECT = 93 // { int select(int nd, u_int32_t *in, u_int32_t *ou, u_int32_t *ex, struct timeval *tv); } - // SYS_NOSYS = 94; // { int nosys(void); } { old setdopt } - SYS_FSYNC = 95 // { int fsync(int fd); } - SYS_SETPRIORITY = 96 // { int setpriority(int which, id_t who, int prio); } - SYS_SOCKET = 97 // { int socket(int domain, int type, int protocol); } - SYS_CONNECT = 98 // { int connect(int s, caddr_t name, socklen_t namelen); } - // SYS_NOSYS = 97; // { int nosys(void); } - // SYS_NOSYS = 98; // { int nosys(void); } - // SYS_NOSYS = 99; // { int nosys(void); } { old accept } - SYS_GETPRIORITY = 100 // { int getpriority(int which, id_t who); } - // SYS_NOSYS = 101; // { int nosys(void); } { old send } - // SYS_NOSYS = 102; // { int nosys(void); } { old recv } - // SYS_NOSYS = 103; // { int nosys(void); } { old sigreturn } - SYS_BIND = 104 // { int bind(int s, caddr_t name, socklen_t namelen); } - SYS_SETSOCKOPT = 105 // { int setsockopt(int s, int level, int name, caddr_t val, socklen_t valsize); } - SYS_LISTEN = 106 // { int listen(int s, int backlog); } - // SYS_NOSYS = 104; // { int nosys(void); } - // SYS_NOSYS = 105; // { int nosys(void); } - // SYS_NOSYS = 106; // { int nosys(void); } - // SYS_NOSYS = 107; // { int nosys(void); } { old vtimes } - // SYS_NOSYS = 108; // { int nosys(void); } { old sigvec } - // SYS_NOSYS = 109; // { int nosys(void); } { old sigblock } - // SYS_NOSYS = 110; // { int nosys(void); } { old sigsetmask } - SYS_SIGSUSPEND = 111 // { int sigsuspend(sigset_t mask); } - // SYS_NOSYS = 112; // { int nosys(void); } { old sigstack } - // SYS_NOSYS = 113; // { int nosys(void); } { old recvmsg } - // SYS_NOSYS = 114; // { int nosys(void); } { old sendmsg } - // SYS_NOSYS = 113; // { int nosys(void); } - // SYS_NOSYS = 114; // { int nosys(void); } - // SYS_NOSYS = 115; // { int nosys(void); } { old vtrace } - SYS_GETTIMEOFDAY = 116 // { int gettimeofday(struct timeval *tp, struct timezone *tzp); } - SYS_GETRUSAGE = 117 // { int getrusage(int who, struct rusage *rusage); } - SYS_GETSOCKOPT = 118 // { int getsockopt(int s, int level, int name, caddr_t val, socklen_t *avalsize); } - // SYS_NOSYS = 118; // { int nosys(void); } - // SYS_NOSYS = 119; // { int nosys(void); } { old resuba } - SYS_READV = 120 // { user_ssize_t readv(int fd, struct iovec *iovp, u_int iovcnt); } - SYS_WRITEV = 121 // { user_ssize_t writev(int fd, struct iovec *iovp, u_int iovcnt); } - SYS_SETTIMEOFDAY = 122 // { int settimeofday(struct timeval *tv, struct timezone *tzp); } - SYS_FCHOWN = 123 // { int fchown(int fd, int uid, int gid); } - SYS_FCHMOD = 124 // { int fchmod(int fd, int mode); } - // SYS_NOSYS = 125; // { int nosys(void); } { old recvfrom } - SYS_SETREUID = 126 // { int setreuid(uid_t ruid, uid_t euid); } - SYS_SETREGID = 127 // { int setregid(gid_t rgid, gid_t egid); } - SYS_RENAME = 128 // { int rename(char *from, char *to); } - // SYS_NOSYS = 129; // { int nosys(void); } { old truncate } - // SYS_NOSYS = 130; // { int nosys(void); } { old ftruncate } - SYS_FLOCK = 131 // { int flock(int fd, int how); } - SYS_MKFIFO = 132 // { int mkfifo(user_addr_t path, int mode); } - SYS_SENDTO = 133 // { int sendto(int s, caddr_t buf, size_t len, int flags, caddr_t to, socklen_t tolen); } - SYS_SHUTDOWN = 134 // { int shutdown(int s, int how); } - SYS_SOCKETPAIR = 135 // { int socketpair(int domain, int type, int protocol, int *rsv); } - // SYS_NOSYS = 133; // { int nosys(void); } - // SYS_NOSYS = 134; // { int nosys(void); } - // SYS_NOSYS = 135; // { int nosys(void); } - SYS_MKDIR = 136 // { int mkdir(user_addr_t path, int mode); } - SYS_RMDIR = 137 // { int rmdir(char *path); } - SYS_UTIMES = 138 // { int utimes(char *path, struct timeval *tptr); } - SYS_FUTIMES = 139 // { int futimes(int fd, struct timeval *tptr); } - SYS_ADJTIME = 140 // { int adjtime(struct timeval *delta, struct timeval *olddelta); } - // SYS_NOSYS = 141; // { int nosys(void); } { old getpeername } - SYS_GETHOSTUUID = 142 // { int gethostuuid(unsigned char *uuid_buf, const struct timespec *timeoutp); } - // SYS_NOSYS = 143; // { int nosys(void); } { old sethostid } - // SYS_NOSYS = 144; // { int nosys(void); } { old getrlimit } - // SYS_NOSYS = 145; // { int nosys(void); } { old setrlimit } - // SYS_NOSYS = 146; // { int nosys(void); } { old killpg } - SYS_SETSID = 147 // { int setsid(void); } - // SYS_NOSYS = 148; // { int nosys(void); } { old setquota } - // SYS_NOSYS = 149; // { int nosys(void); } { old qquota } - // SYS_NOSYS = 150; // { int nosys(void); } { old getsockname } - SYS_GETPGID = 151 // { int getpgid(pid_t pid); } - SYS_SETPRIVEXEC = 152 // { int setprivexec(int flag); } - SYS_PREAD = 153 // { user_ssize_t pread(int fd, user_addr_t buf, user_size_t nbyte, off_t offset); } - SYS_PWRITE = 154 // { user_ssize_t pwrite(int fd, user_addr_t buf, user_size_t nbyte, off_t offset); } - SYS_NFSSVC = 155 // { int nfssvc(int flag, caddr_t argp); } - // SYS_NOSYS = 155; // { int nosys(void); } - // SYS_NOSYS = 156; // { int nosys(void); } { old getdirentries } - SYS_STATFS = 157 // { int statfs(char *path, struct statfs *buf); } - SYS_FSTATFS = 158 // { int fstatfs(int fd, struct statfs *buf); } - SYS_UNMOUNT = 159 // { int unmount(user_addr_t path, int flags); } - // SYS_NOSYS = 160; // { int nosys(void); } { old async_daemon } - SYS_GETFH = 161 // { int getfh(char *fname, fhandle_t *fhp); } - // SYS_NOSYS = 161; // { int nosys(void); } - // SYS_NOSYS = 162; // { int nosys(void); } { old getdomainname } - // SYS_NOSYS = 163; // { int nosys(void); } { old setdomainname } - // SYS_NOSYS = 164; // { int nosys(void); } - SYS_QUOTACTL = 165 // { int quotactl(const char *path, int cmd, int uid, caddr_t arg); } - // SYS_NOSYS = 166; // { int nosys(void); } { old exportfs } - SYS_MOUNT = 167 // { int mount(char *type, char *path, int flags, caddr_t data); } - // SYS_NOSYS = 168; // { int nosys(void); } { old ustat } - SYS_CSOPS = 169 // { int csops(pid_t pid, uint32_t ops, user_addr_t useraddr, user_size_t usersize); } - // SYS_NOSYS = 171; // { int nosys(void); } { old wait3 } - // SYS_NOSYS = 172; // { int nosys(void); } { old rpause } - SYS_WAITID = 173 // { int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); } - // SYS_NOSYS = 174; // { int nosys(void); } { old getdents } - // SYS_NOSYS = 175; // { int nosys(void); } { old gc_control } - SYS_ADD_PROFIL = 176 // { int add_profil(short *bufbase, size_t bufsize, u_long pcoffset, u_int pcscale); } - // SYS_NOSYS = 177; // { int nosys(void); } - // SYS_NOSYS = 178; // { int nosys(void); } - // SYS_NOSYS = 179; // { int nosys(void); } - SYS_KDEBUG_TRACE = 180 // { int kdebug_trace(int code, int arg1, int arg2, int arg3, int arg4, int arg5) NO_SYSCALL_STUB; } - SYS_SETGID = 181 // { int setgid(gid_t gid); } - SYS_SETEGID = 182 // { int setegid(gid_t egid); } - SYS_SETEUID = 183 // { int seteuid(uid_t euid); } - SYS_SIGRETURN = 184 // { int sigreturn(struct ucontext *uctx, int infostyle); } - // SYS_NOSYS = 186; // { int nosys(void); } - // SYS_NOSYS = 187; // { int nosys(void); } - SYS_STAT = 188 // { int stat(user_addr_t path, user_addr_t ub); } - SYS_FSTAT = 189 // { int fstat(int fd, user_addr_t ub); } - SYS_LSTAT = 190 // { int lstat(user_addr_t path, user_addr_t ub); } - SYS_PATHCONF = 191 // { int pathconf(char *path, int name); } - SYS_FPATHCONF = 192 // { int fpathconf(int fd, int name); } - // SYS_NOSYS = 193; // { int nosys(void); } - SYS_GETRLIMIT = 194 // { int getrlimit(u_int which, struct rlimit *rlp); } - SYS_SETRLIMIT = 195 // { int setrlimit(u_int which, struct rlimit *rlp); } - SYS_GETDIRENTRIES = 196 // { int getdirentries(int fd, char *buf, u_int count, long *basep); } - SYS_MMAP = 197 // { user_addr_t mmap(caddr_t addr, size_t len, int prot, int flags, int fd, off_t pos); } - // SYS_NOSYS = 198; // { int nosys(void); } { __syscall } - SYS_LSEEK = 199 // { off_t lseek(int fd, off_t offset, int whence); } - SYS_TRUNCATE = 200 // { int truncate(char *path, off_t length); } - SYS_FTRUNCATE = 201 // { int ftruncate(int fd, off_t length); } - SYS___SYSCTL = 202 // { int __sysctl(int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen); } - SYS_MLOCK = 203 // { int mlock(caddr_t addr, size_t len); } - SYS_MUNLOCK = 204 // { int munlock(caddr_t addr, size_t len); } - SYS_UNDELETE = 205 // { int undelete(user_addr_t path); } - SYS_ATSOCKET = 206 // { int ATsocket(int proto); } - // SYS_NOSYS = 213; // { int nosys(void); } { Reserved for AppleTalk } - // SYS_NOSYS = 206; // { int nosys(void); } - // SYS_NOSYS = 207; // { int nosys(void); } - // SYS_NOSYS = 208; // { int nosys(void); } - // SYS_NOSYS = 209; // { int nosys(void); } - // SYS_NOSYS = 210; // { int nosys(void); } - // SYS_NOSYS = 211; // { int nosys(void); } - // SYS_NOSYS = 212; // { int nosys(void); } - // SYS_NOSYS = 213; // { int nosys(void); } { Reserved for AppleTalk } - SYS_KQUEUE_FROM_PORTSET_NP = 214 // { int kqueue_from_portset_np(int portset); } - SYS_KQUEUE_PORTSET_NP = 215 // { int kqueue_portset_np(int fd); } - SYS_GETATTRLIST = 220 // { int getattrlist(const char *path, struct attrlist *alist, void *attributeBuffer, size_t bufferSize, u_long options); } - SYS_SETATTRLIST = 221 // { int setattrlist(const char *path, struct attrlist *alist, void *attributeBuffer, size_t bufferSize, u_long options); } - SYS_GETDIRENTRIESATTR = 222 // { int getdirentriesattr(int fd, struct attrlist *alist, void *buffer, size_t buffersize, u_long *count, u_long *basep, u_long *newstate, u_long options); } - SYS_EXCHANGEDATA = 223 // { int exchangedata(const char *path1, const char *path2, u_long options); } - // SYS_NOSYS = 224; // { int nosys(void); } { was checkuseraccess } - SYS_SEARCHFS = 225 // { int searchfs(const char *path, struct fssearchblock *searchblock, u_long *nummatches, u_long scriptcode, u_long options, struct searchstate *state); } - SYS_DELETE = 226 // { int delete(user_addr_t path) NO_SYSCALL_STUB; } { private delete (Carbon semantics) } - SYS_COPYFILE = 227 // { int copyfile(char *from, char *to, int mode, int flags) NO_SYSCALL_STUB; } - // SYS_NOSYS = 228; // { int nosys(void); } - // SYS_NOSYS = 229; // { int nosys(void); } - SYS_POLL = 230 // { int poll(struct pollfd *fds, u_int nfds, int timeout); } - SYS_WATCHEVENT = 231 // { int watchevent(struct eventreq *u_req, int u_eventmask); } - SYS_WAITEVENT = 232 // { int waitevent(struct eventreq *u_req, struct timeval *tv); } - SYS_MODWATCH = 233 // { int modwatch(struct eventreq *u_req, int u_eventmask); } - SYS_GETXATTR = 234 // { user_ssize_t getxattr(user_addr_t path, user_addr_t attrname, user_addr_t value, size_t size, uint32_t position, int options); } - SYS_FGETXATTR = 235 // { user_ssize_t fgetxattr(int fd, user_addr_t attrname, user_addr_t value, size_t size, uint32_t position, int options); } - SYS_SETXATTR = 236 // { int setxattr(user_addr_t path, user_addr_t attrname, user_addr_t value, size_t size, uint32_t position, int options); } - SYS_FSETXATTR = 237 // { int fsetxattr(int fd, user_addr_t attrname, user_addr_t value, size_t size, uint32_t position, int options); } - SYS_REMOVEXATTR = 238 // { int removexattr(user_addr_t path, user_addr_t attrname, int options); } - SYS_FREMOVEXATTR = 239 // { int fremovexattr(int fd, user_addr_t attrname, int options); } - SYS_LISTXATTR = 240 // { user_ssize_t listxattr(user_addr_t path, user_addr_t namebuf, size_t bufsize, int options); } - SYS_FLISTXATTR = 241 // { user_ssize_t flistxattr(int fd, user_addr_t namebuf, size_t bufsize, int options); } - SYS_FSCTL = 242 // { int fsctl(const char *path, u_long cmd, caddr_t data, u_long options); } - SYS_INITGROUPS = 243 // { int initgroups(u_int gidsetsize, gid_t *gidset, int gmuid); } - SYS_POSIX_SPAWN = 244 // { int posix_spawn(pid_t *pid, const char *path, const struct _posix_spawn_args_desc *adesc, char **argv, char **envp); } - // SYS_NOSYS = 245; // { int nosys(void); } - // SYS_NOSYS = 246; // { int nosys(void); } - SYS_NFSCLNT = 247 // { int nfsclnt(int flag, caddr_t argp); } - // SYS_NOSYS = 247; // { int nosys(void); } - SYS_FHOPEN = 248 // { int fhopen(const struct fhandle *u_fhp, int flags); } - // SYS_NOSYS = 248; // { int nosys(void); } - // SYS_NOSYS = 249; // { int nosys(void); } - SYS_MINHERIT = 250 // { int minherit(void *addr, size_t len, int inherit); } - SYS_SEMSYS = 251 // { int semsys(u_int which, int a2, int a3, int a4, int a5); } - // SYS_NOSYS = 251; // { int nosys(void); } - SYS_MSGSYS = 252 // { int msgsys(u_int which, int a2, int a3, int a4, int a5); } - // SYS_NOSYS = 252; // { int nosys(void); } - SYS_SHMSYS = 253 // { int shmsys(u_int which, int a2, int a3, int a4); } - // SYS_NOSYS = 253; // { int nosys(void); } - SYS_SEMCTL = 254 // { int semctl(int semid, int semnum, int cmd, semun_t arg); } - SYS_SEMGET = 255 // { int semget(key_t key, int nsems, int semflg); } - SYS_SEMOP = 256 // { int semop(int semid, struct sembuf *sops, int nsops); } - // SYS_NOSYS = 257; // { int nosys(void); } - // SYS_NOSYS = 254; // { int nosys(void); } - // SYS_NOSYS = 255; // { int nosys(void); } - // SYS_NOSYS = 256; // { int nosys(void); } - // SYS_NOSYS = 257; // { int nosys(void); } - SYS_MSGCTL = 258 // { int msgctl(int msqid, int cmd, struct msqid_ds *buf); } - SYS_MSGGET = 259 // { int msgget(key_t key, int msgflg); } - SYS_MSGSND = 260 // { int msgsnd(int msqid, void *msgp, size_t msgsz, int msgflg); } - SYS_MSGRCV = 261 // { user_ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); } - // SYS_NOSYS = 258; // { int nosys(void); } - // SYS_NOSYS = 259; // { int nosys(void); } - // SYS_NOSYS = 260; // { int nosys(void); } - // SYS_NOSYS = 261; // { int nosys(void); } - SYS_SHMAT = 262 // { user_addr_t shmat(int shmid, void *shmaddr, int shmflg); } - SYS_SHMCTL = 263 // { int shmctl(int shmid, int cmd, struct shmid_ds *buf); } - SYS_SHMDT = 264 // { int shmdt(void *shmaddr); } - SYS_SHMGET = 265 // { int shmget(key_t key, size_t size, int shmflg); } - // SYS_NOSYS = 262; // { int nosys(void); } - // SYS_NOSYS = 263; // { int nosys(void); } - // SYS_NOSYS = 264; // { int nosys(void); } - // SYS_NOSYS = 265; // { int nosys(void); } - SYS_SHM_OPEN = 266 // { int shm_open(const char *name, int oflag, int mode); } - SYS_SHM_UNLINK = 267 // { int shm_unlink(const char *name); } - SYS_SEM_OPEN = 268 // { user_addr_t sem_open(const char *name, int oflag, int mode, int value); } - SYS_SEM_CLOSE = 269 // { int sem_close(sem_t *sem); } - SYS_SEM_UNLINK = 270 // { int sem_unlink(const char *name); } - SYS_SEM_WAIT = 271 // { int sem_wait(sem_t *sem); } - SYS_SEM_TRYWAIT = 272 // { int sem_trywait(sem_t *sem); } - SYS_SEM_POST = 273 // { int sem_post(sem_t *sem); } - SYS_SEM_GETVALUE = 274 // { int sem_getvalue(sem_t *sem, int *sval); } - SYS_SEM_INIT = 275 // { int sem_init(sem_t *sem, int phsared, u_int value); } - SYS_SEM_DESTROY = 276 // { int sem_destroy(sem_t *sem); } - SYS_OPEN_EXTENDED = 277 // { int open_extended(user_addr_t path, int flags, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } - SYS_UMASK_EXTENDED = 278 // { int umask_extended(int newmask, user_addr_t xsecurity) NO_SYSCALL_STUB; } - SYS_STAT_EXTENDED = 279 // { int stat_extended(user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } - SYS_LSTAT_EXTENDED = 280 // { int lstat_extended(user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } - SYS_FSTAT_EXTENDED = 281 // { int fstat_extended(int fd, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } - SYS_CHMOD_EXTENDED = 282 // { int chmod_extended(user_addr_t path, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } - SYS_FCHMOD_EXTENDED = 283 // { int fchmod_extended(int fd, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } - SYS_ACCESS_EXTENDED = 284 // { int access_extended(user_addr_t entries, size_t size, user_addr_t results, uid_t uid) NO_SYSCALL_STUB; } - SYS_SETTID = 285 // { int settid(uid_t uid, gid_t gid) NO_SYSCALL_STUB; } - SYS_GETTID = 286 // { int gettid(uid_t *uidp, gid_t *gidp) NO_SYSCALL_STUB; } - SYS_SETSGROUPS = 287 // { int setsgroups(int setlen, user_addr_t guidset) NO_SYSCALL_STUB; } - SYS_GETSGROUPS = 288 // { int getsgroups(user_addr_t setlen, user_addr_t guidset) NO_SYSCALL_STUB; } - SYS_SETWGROUPS = 289 // { int setwgroups(int setlen, user_addr_t guidset) NO_SYSCALL_STUB; } - SYS_GETWGROUPS = 290 // { int getwgroups(user_addr_t setlen, user_addr_t guidset) NO_SYSCALL_STUB; } - SYS_MKFIFO_EXTENDED = 291 // { int mkfifo_extended(user_addr_t path, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } - SYS_MKDIR_EXTENDED = 292 // { int mkdir_extended(user_addr_t path, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } - SYS_IDENTITYSVC = 293 // { int identitysvc(int opcode, user_addr_t message) NO_SYSCALL_STUB; } - SYS_SHARED_REGION_CHECK_NP = 294 // { int shared_region_check_np(uint64_t *start_address) NO_SYSCALL_STUB; } - SYS_SHARED_REGION_MAP_NP = 295 // { int shared_region_map_np(int fd, uint32_t count, const struct shared_file_mapping_np *mappings) NO_SYSCALL_STUB; } - // SYS_NOSYS = 296; // { int nosys(void); } { old load_shared_file } - // SYS_NOSYS = 297; // { int nosys(void); } { old reset_shared_file } - // SYS_NOSYS = 298; // { int nosys(void); } { old new_system_shared_regions } - // SYS_ENOSYS = 299; // { int enosys(void); } { old shared_region_map_file_np } - // SYS_ENOSYS = 300; // { int enosys(void); } { old shared_region_make_private_np } - SYS___PTHREAD_MUTEX_DESTROY = 301 // { int __pthread_mutex_destroy(int mutexid); } - SYS___PTHREAD_MUTEX_INIT = 302 // { int __pthread_mutex_init(user_addr_t mutex, user_addr_t attr); } - SYS___PTHREAD_MUTEX_LOCK = 303 // { int __pthread_mutex_lock(int mutexid); } - SYS___PTHREAD_MUTEX_TRYLOCK = 304 // { int __pthread_mutex_trylock(int mutexid); } - SYS___PTHREAD_MUTEX_UNLOCK = 305 // { int __pthread_mutex_unlock(int mutexid); } - SYS___PTHREAD_COND_INIT = 306 // { int __pthread_cond_init(user_addr_t cond, user_addr_t attr); } - SYS___PTHREAD_COND_DESTROY = 307 // { int __pthread_cond_destroy(int condid); } - SYS___PTHREAD_COND_BROADCAST = 308 // { int __pthread_cond_broadcast(int condid); } - SYS___PTHREAD_COND_SIGNAL = 309 // { int __pthread_cond_signal(int condid); } - SYS_GETSID = 310 // { int getsid(pid_t pid); } - SYS_SETTID_WITH_PID = 311 // { int settid_with_pid(pid_t pid, int assume) NO_SYSCALL_STUB; } - SYS___PTHREAD_COND_TIMEDWAIT = 312 // { int __pthread_cond_timedwait(int condid, int mutexid, user_addr_t abstime); } - SYS_AIO_FSYNC = 313 // { int aio_fsync(int op, user_addr_t aiocbp); } - SYS_AIO_RETURN = 314 // { user_ssize_t aio_return(user_addr_t aiocbp); } - SYS_AIO_SUSPEND = 315 // { int aio_suspend(user_addr_t aiocblist, int nent, user_addr_t timeoutp); } - SYS_AIO_CANCEL = 316 // { int aio_cancel(int fd, user_addr_t aiocbp); } - SYS_AIO_ERROR = 317 // { int aio_error(user_addr_t aiocbp); } - SYS_AIO_READ = 318 // { int aio_read(user_addr_t aiocbp); } - SYS_AIO_WRITE = 319 // { int aio_write(user_addr_t aiocbp); } - SYS_LIO_LISTIO = 320 // { int lio_listio(int mode, user_addr_t aiocblist, int nent, user_addr_t sigp); } - SYS___PTHREAD_COND_WAIT = 321 // { int __pthread_cond_wait(int condid, int mutexid); } - SYS_IOPOLICYSYS = 322 // { int iopolicysys(int cmd, void *arg) NO_SYSCALL_STUB; } - // SYS_NOSYS = 323; // { int nosys(void); } - SYS_MLOCKALL = 324 // { int mlockall(int how); } - SYS_MUNLOCKALL = 325 // { int munlockall(int how); } - // SYS_NOSYS = 326; // { int nosys(void); } - SYS_ISSETUGID = 327 // { int issetugid(void); } - SYS___PTHREAD_KILL = 328 // { int __pthread_kill(int thread_port, int sig); } - SYS___PTHREAD_SIGMASK = 329 // { int __pthread_sigmask(int how, user_addr_t set, user_addr_t oset); } - SYS___SIGWAIT = 330 // { int __sigwait(user_addr_t set, user_addr_t sig); } - SYS___DISABLE_THREADSIGNAL = 331 // { int __disable_threadsignal(int value); } - SYS___PTHREAD_MARKCANCEL = 332 // { int __pthread_markcancel(int thread_port); } - SYS___PTHREAD_CANCELED = 333 // { int __pthread_canceled(int action); } - SYS___SEMWAIT_SIGNAL = 334 // { int __semwait_signal(int cond_sem, int mutex_sem, int timeout, int relative, time_t tv_sec, int32_t tv_nsec); } - // SYS_NOSYS = 335; // { int nosys(void); } { old utrace } - SYS_PROC_INFO = 336 // { int proc_info(int32_t callnum,int32_t pid,uint32_t flavor, uint64_t arg,user_addr_t buffer,int32_t buffersize) NO_SYSCALL_STUB; } - SYS_SENDFILE = 337 // { int sendfile(int fd, int s, off_t offset, off_t *nbytes, struct sf_hdtr *hdtr, int flags); } - // SYS_NOSYS = 337; // { int nosys(void); } - SYS_STAT64 = 338 // { int stat64(user_addr_t path, user_addr_t ub); } - SYS_FSTAT64 = 339 // { int fstat64(int fd, user_addr_t ub); } - SYS_LSTAT64 = 340 // { int lstat64(user_addr_t path, user_addr_t ub); } - SYS_STAT64_EXTENDED = 341 // { int stat64_extended(user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } - SYS_LSTAT64_EXTENDED = 342 // { int lstat64_extended(user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } - SYS_FSTAT64_EXTENDED = 343 // { int fstat64_extended(int fd, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } - SYS_GETDIRENTRIES64 = 344 // { user_ssize_t getdirentries64(int fd, void *buf, user_size_t bufsize, off_t *position) NO_SYSCALL_STUB; } - SYS_STATFS64 = 345 // { int statfs64(char *path, struct statfs64 *buf); } - SYS_FSTATFS64 = 346 // { int fstatfs64(int fd, struct statfs64 *buf); } - SYS_GETFSSTAT64 = 347 // { int getfsstat64(user_addr_t buf, int bufsize, int flags); } - SYS___PTHREAD_CHDIR = 348 // { int __pthread_chdir(user_addr_t path); } - SYS___PTHREAD_FCHDIR = 349 // { int __pthread_fchdir(int fd); } - SYS_AUDIT = 350 // { int audit(void *record, int length); } - SYS_AUDITON = 351 // { int auditon(int cmd, void *data, int length); } - // SYS_NOSYS = 352; // { int nosys(void); } - SYS_GETAUID = 353 // { int getauid(au_id_t *auid); } - SYS_SETAUID = 354 // { int setauid(au_id_t *auid); } - SYS_GETAUDIT = 355 // { int getaudit(struct auditinfo *auditinfo); } - SYS_SETAUDIT = 356 // { int setaudit(struct auditinfo *auditinfo); } - SYS_GETAUDIT_ADDR = 357 // { int getaudit_addr(struct auditinfo_addr *auditinfo_addr, int length); } - SYS_SETAUDIT_ADDR = 358 // { int setaudit_addr(struct auditinfo_addr *auditinfo_addr, int length); } - SYS_AUDITCTL = 359 // { int auditctl(char *path); } - // SYS_NOSYS = 350; // { int nosys(void); } - // SYS_NOSYS = 351; // { int nosys(void); } - // SYS_NOSYS = 352; // { int nosys(void); } - // SYS_NOSYS = 353; // { int nosys(void); } - // SYS_NOSYS = 354; // { int nosys(void); } - // SYS_NOSYS = 355; // { int nosys(void); } - // SYS_NOSYS = 356; // { int nosys(void); } - // SYS_NOSYS = 357; // { int nosys(void); } - // SYS_NOSYS = 358; // { int nosys(void); } - // SYS_NOSYS = 359; // { int nosys(void); } - SYS_BSDTHREAD_CREATE = 360 // { user_addr_t bsdthread_create(user_addr_t func, user_addr_t func_arg, user_addr_t stack, user_addr_t pthread, uint32_t flags) NO_SYSCALL_STUB; } - SYS_BSDTHREAD_TERMINATE = 361 // { int bsdthread_terminate(user_addr_t stackaddr, size_t freesize, uint32_t port, uint32_t sem) NO_SYSCALL_STUB; } - SYS_KQUEUE = 362 // { int kqueue(void); } - SYS_KEVENT = 363 // { int kevent(int fd, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout); } - SYS_LCHOWN = 364 // { int lchown(user_addr_t path, uid_t owner, gid_t group); } - SYS_STACK_SNAPSHOT = 365 // { int stack_snapshot(pid_t pid, user_addr_t tracebuf, uint32_t tracebuf_size, uint32_t options) NO_SYSCALL_STUB; } - SYS_BSDTHREAD_REGISTER = 366 // { int bsdthread_register(user_addr_t threadstart, user_addr_t wqthread, int pthsize) NO_SYSCALL_STUB; } - SYS_WORKQ_OPEN = 367 // { int workq_open(void) NO_SYSCALL_STUB; } - SYS_WORKQ_OPS = 368 // { int workq_ops(int options, user_addr_t item, int prio) NO_SYSCALL_STUB; } - // SYS_NOSYS = 369; // { int nosys(void); } - // SYS_NOSYS = 370; // { int nosys(void); } - // SYS_NOSYS = 371; // { int nosys(void); } - // SYS_NOSYS = 372; // { int nosys(void); } - // SYS_NOSYS = 373; // { int nosys(void); } - // SYS_NOSYS = 374; // { int nosys(void); } - // SYS_NOSYS = 375; // { int nosys(void); } - // SYS_NOSYS = 376; // { int nosys(void); } - // SYS_NOSYS = 377; // { int nosys(void); } - // SYS_NOSYS = 378; // { int nosys(void); } - // SYS_NOSYS = 379; // { int nosys(void); } - SYS___MAC_EXECVE = 380 // { int __mac_execve(char *fname, char **argp, char **envp, struct mac *mac_p); } - SYS___MAC_SYSCALL = 381 // { int __mac_syscall(char *policy, int call, user_addr_t arg); } - SYS___MAC_GET_FILE = 382 // { int __mac_get_file(char *path_p, struct mac *mac_p); } - SYS___MAC_SET_FILE = 383 // { int __mac_set_file(char *path_p, struct mac *mac_p); } - SYS___MAC_GET_LINK = 384 // { int __mac_get_link(char *path_p, struct mac *mac_p); } - SYS___MAC_SET_LINK = 385 // { int __mac_set_link(char *path_p, struct mac *mac_p); } - SYS___MAC_GET_PROC = 386 // { int __mac_get_proc(struct mac *mac_p); } - SYS___MAC_SET_PROC = 387 // { int __mac_set_proc(struct mac *mac_p); } - SYS___MAC_GET_FD = 388 // { int __mac_get_fd(int fd, struct mac *mac_p); } - SYS___MAC_SET_FD = 389 // { int __mac_set_fd(int fd, struct mac *mac_p); } - SYS___MAC_GET_PID = 390 // { int __mac_get_pid(pid_t pid, struct mac *mac_p); } - SYS___MAC_GET_LCID = 391 // { int __mac_get_lcid(pid_t lcid, struct mac *mac_p); } - SYS___MAC_GET_LCTX = 392 // { int __mac_get_lctx(struct mac *mac_p); } - SYS___MAC_SET_LCTX = 393 // { int __mac_set_lctx(struct mac *mac_p); } - SYS_SETLCID = 394 // { int setlcid(pid_t pid, pid_t lcid) NO_SYSCALL_STUB; } - SYS_GETLCID = 395 // { int getlcid(pid_t pid) NO_SYSCALL_STUB; } - SYS_READ_NOCANCEL = 396 // { user_ssize_t read_nocancel(int fd, user_addr_t cbuf, user_size_t nbyte) NO_SYSCALL_STUB; } - SYS_WRITE_NOCANCEL = 397 // { user_ssize_t write_nocancel(int fd, user_addr_t cbuf, user_size_t nbyte) NO_SYSCALL_STUB; } - SYS_OPEN_NOCANCEL = 398 // { int open_nocancel(user_addr_t path, int flags, int mode) NO_SYSCALL_STUB; } - SYS_CLOSE_NOCANCEL = 399 // { int close_nocancel(int fd) NO_SYSCALL_STUB; } - SYS_WAIT4_NOCANCEL = 400 // { int wait4_nocancel(int pid, user_addr_t status, int options, user_addr_t rusage) NO_SYSCALL_STUB; } - SYS_RECVMSG_NOCANCEL = 401 // { int recvmsg_nocancel(int s, struct msghdr *msg, int flags) NO_SYSCALL_STUB; } - SYS_SENDMSG_NOCANCEL = 402 // { int sendmsg_nocancel(int s, caddr_t msg, int flags) NO_SYSCALL_STUB; } - SYS_RECVFROM_NOCANCEL = 403 // { int recvfrom_nocancel(int s, void *buf, size_t len, int flags, struct sockaddr *from, int *fromlenaddr) NO_SYSCALL_STUB; } - SYS_ACCEPT_NOCANCEL = 404 // { int accept_nocancel(int s, caddr_t name, socklen_t *anamelen) NO_SYSCALL_STUB; } - // SYS_NOSYS = 401; // { int nosys(void); } - // SYS_NOSYS = 402; // { int nosys(void); } - // SYS_NOSYS = 403; // { int nosys(void); } - // SYS_NOSYS = 404; // { int nosys(void); } - SYS_MSYNC_NOCANCEL = 405 // { int msync_nocancel(caddr_t addr, size_t len, int flags) NO_SYSCALL_STUB; } - SYS_FCNTL_NOCANCEL = 406 // { int fcntl_nocancel(int fd, int cmd, long arg) NO_SYSCALL_STUB; } - SYS_SELECT_NOCANCEL = 407 // { int select_nocancel(int nd, u_int32_t *in, u_int32_t *ou, u_int32_t *ex, struct timeval *tv) NO_SYSCALL_STUB; } - SYS_FSYNC_NOCANCEL = 408 // { int fsync_nocancel(int fd) NO_SYSCALL_STUB; } - SYS_CONNECT_NOCANCEL = 409 // { int connect_nocancel(int s, caddr_t name, socklen_t namelen) NO_SYSCALL_STUB; } - // SYS_NOSYS = 409; // { int nosys(void); } - SYS_SIGSUSPEND_NOCANCEL = 410 // { int sigsuspend_nocancel(sigset_t mask) NO_SYSCALL_STUB; } - SYS_READV_NOCANCEL = 411 // { user_ssize_t readv_nocancel(int fd, struct iovec *iovp, u_int iovcnt) NO_SYSCALL_STUB; } - SYS_WRITEV_NOCANCEL = 412 // { user_ssize_t writev_nocancel(int fd, struct iovec *iovp, u_int iovcnt) NO_SYSCALL_STUB; } - SYS_SENDTO_NOCANCEL = 413 // { int sendto_nocancel(int s, caddr_t buf, size_t len, int flags, caddr_t to, socklen_t tolen) NO_SYSCALL_STUB; } - // SYS_NOSYS = 413; // { int nosys(void); } - SYS_PREAD_NOCANCEL = 414 // { user_ssize_t pread_nocancel(int fd, user_addr_t buf, user_size_t nbyte, off_t offset) NO_SYSCALL_STUB; } - SYS_PWRITE_NOCANCEL = 415 // { user_ssize_t pwrite_nocancel(int fd, user_addr_t buf, user_size_t nbyte, off_t offset) NO_SYSCALL_STUB; } - SYS_WAITID_NOCANCEL = 416 // { int waitid_nocancel(idtype_t idtype, id_t id, siginfo_t *infop, int options) NO_SYSCALL_STUB; } - SYS_POLL_NOCANCEL = 417 // { int poll_nocancel(struct pollfd *fds, u_int nfds, int timeout) NO_SYSCALL_STUB; } - SYS_MSGSND_NOCANCEL = 418 // { int msgsnd_nocancel(int msqid, void *msgp, size_t msgsz, int msgflg) NO_SYSCALL_STUB; } - SYS_MSGRCV_NOCANCEL = 419 // { user_ssize_t msgrcv_nocancel(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) NO_SYSCALL_STUB; } - // SYS_NOSYS = 418; // { int nosys(void); } - // SYS_NOSYS = 419; // { int nosys(void); } - SYS_SEM_WAIT_NOCANCEL = 420 // { int sem_wait_nocancel(sem_t *sem) NO_SYSCALL_STUB; } - SYS_AIO_SUSPEND_NOCANCEL = 421 // { int aio_suspend_nocancel(user_addr_t aiocblist, int nent, user_addr_t timeoutp) NO_SYSCALL_STUB; } - SYS___SIGWAIT_NOCANCEL = 422 // { int __sigwait_nocancel(user_addr_t set, user_addr_t sig) NO_SYSCALL_STUB; } - SYS___SEMWAIT_SIGNAL_NOCANCEL = 423 // { int __semwait_signal_nocancel(int cond_sem, int mutex_sem, int timeout, int relative, time_t tv_sec, int32_t tv_nsec) NO_SYSCALL_STUB; } - SYS___MAC_MOUNT = 424 // { int __mac_mount(char *type, char *path, int flags, caddr_t data, struct mac *mac_p); } - SYS___MAC_GET_MOUNT = 425 // { int __mac_get_mount(char *path, struct mac *mac_p); } - SYS___MAC_GETFSSTAT = 426 // { int __mac_getfsstat(user_addr_t buf, int bufsize, user_addr_t mac, int macsize, int flags); } + SYS_SYSCALL = 0 + SYS_EXIT = 1 + SYS_FORK = 2 + SYS_READ = 3 + SYS_WRITE = 4 + SYS_OPEN = 5 + SYS_CLOSE = 6 + SYS_WAIT4 = 7 + SYS_LINK = 9 + SYS_UNLINK = 10 + SYS_CHDIR = 12 + SYS_FCHDIR = 13 + SYS_MKNOD = 14 + SYS_CHMOD = 15 + SYS_CHOWN = 16 + SYS_GETFSSTAT = 18 + SYS_GETPID = 20 + SYS_SETUID = 23 + SYS_GETUID = 24 + SYS_GETEUID = 25 + SYS_PTRACE = 26 + SYS_RECVMSG = 27 + SYS_SENDMSG = 28 + SYS_RECVFROM = 29 + SYS_ACCEPT = 30 + SYS_GETPEERNAME = 31 + SYS_GETSOCKNAME = 32 + SYS_ACCESS = 33 + SYS_CHFLAGS = 34 + SYS_FCHFLAGS = 35 + SYS_SYNC = 36 + SYS_KILL = 37 + SYS_GETPPID = 39 + SYS_DUP = 41 + SYS_PIPE = 42 + SYS_GETEGID = 43 + SYS_PROFIL = 44 + SYS_SIGACTION = 46 + SYS_GETGID = 47 + SYS_SIGPROCMASK = 48 + SYS_GETLOGIN = 49 + SYS_SETLOGIN = 50 + SYS_ACCT = 51 + SYS_SIGPENDING = 52 + SYS_SIGALTSTACK = 53 + SYS_IOCTL = 54 + SYS_REBOOT = 55 + SYS_REVOKE = 56 + SYS_SYMLINK = 57 + SYS_READLINK = 58 + SYS_EXECVE = 59 + SYS_UMASK = 60 + SYS_CHROOT = 61 + SYS_MSYNC = 65 + SYS_VFORK = 66 + SYS_MUNMAP = 73 + SYS_MPROTECT = 74 + SYS_MADVISE = 75 + SYS_MINCORE = 78 + SYS_GETGROUPS = 79 + SYS_SETGROUPS = 80 + SYS_GETPGRP = 81 + SYS_SETPGID = 82 + SYS_SETITIMER = 83 + SYS_SWAPON = 85 + SYS_GETITIMER = 86 + SYS_GETDTABLESIZE = 89 + SYS_DUP2 = 90 + SYS_FCNTL = 92 + SYS_SELECT = 93 + SYS_FSYNC = 95 + SYS_SETPRIORITY = 96 + SYS_SOCKET = 97 + SYS_CONNECT = 98 + SYS_GETPRIORITY = 100 + SYS_BIND = 104 + SYS_SETSOCKOPT = 105 + SYS_LISTEN = 106 + SYS_SIGSUSPEND = 111 + SYS_GETTIMEOFDAY = 116 + SYS_GETRUSAGE = 117 + SYS_GETSOCKOPT = 118 + SYS_READV = 120 + SYS_WRITEV = 121 + SYS_SETTIMEOFDAY = 122 + SYS_FCHOWN = 123 + SYS_FCHMOD = 124 + SYS_SETREUID = 126 + SYS_SETREGID = 127 + SYS_RENAME = 128 + SYS_FLOCK = 131 + SYS_MKFIFO = 132 + SYS_SENDTO = 133 + SYS_SHUTDOWN = 134 + SYS_SOCKETPAIR = 135 + SYS_MKDIR = 136 + SYS_RMDIR = 137 + SYS_UTIMES = 138 + SYS_FUTIMES = 139 + SYS_ADJTIME = 140 + SYS_GETHOSTUUID = 142 + SYS_SETSID = 147 + SYS_GETPGID = 151 + SYS_SETPRIVEXEC = 152 + SYS_PREAD = 153 + SYS_PWRITE = 154 + SYS_NFSSVC = 155 + SYS_STATFS = 157 + SYS_FSTATFS = 158 + SYS_UNMOUNT = 159 + SYS_GETFH = 161 + SYS_QUOTACTL = 165 + SYS_MOUNT = 167 + SYS_CSOPS = 169 + SYS_WAITID = 173 + SYS_ADD_PROFIL = 176 + SYS_KDEBUG_TRACE = 180 + SYS_SETGID = 181 + SYS_SETEGID = 182 + SYS_SETEUID = 183 + SYS_SIGRETURN = 184 + SYS_CHUD = 185 + SYS_FDATASYNC = 187 + SYS_STAT = 188 + SYS_FSTAT = 189 + SYS_LSTAT = 190 + SYS_PATHCONF = 191 + SYS_FPATHCONF = 192 + SYS_GETRLIMIT = 194 + SYS_SETRLIMIT = 195 + SYS_GETDIRENTRIES = 196 + SYS_MMAP = 197 + SYS_LSEEK = 199 + SYS_TRUNCATE = 200 + SYS_FTRUNCATE = 201 + SYS___SYSCTL = 202 + SYS_MLOCK = 203 + SYS_MUNLOCK = 204 + SYS_UNDELETE = 205 + SYS_ATSOCKET = 206 + SYS_ATGETMSG = 207 + SYS_ATPUTMSG = 208 + SYS_ATPSNDREQ = 209 + SYS_ATPSNDRSP = 210 + SYS_ATPGETREQ = 211 + SYS_ATPGETRSP = 212 + SYS_MKCOMPLEX = 216 + SYS_STATV = 217 + SYS_LSTATV = 218 + SYS_FSTATV = 219 + SYS_GETATTRLIST = 220 + SYS_SETATTRLIST = 221 + SYS_GETDIRENTRIESATTR = 222 + SYS_EXCHANGEDATA = 223 + SYS_SEARCHFS = 225 + SYS_DELETE = 226 + SYS_COPYFILE = 227 + SYS_FGETATTRLIST = 228 + SYS_FSETATTRLIST = 229 + SYS_POLL = 230 + SYS_WATCHEVENT = 231 + SYS_WAITEVENT = 232 + SYS_MODWATCH = 233 + SYS_GETXATTR = 234 + SYS_FGETXATTR = 235 + SYS_SETXATTR = 236 + SYS_FSETXATTR = 237 + SYS_REMOVEXATTR = 238 + SYS_FREMOVEXATTR = 239 + SYS_LISTXATTR = 240 + SYS_FLISTXATTR = 241 + SYS_FSCTL = 242 + SYS_INITGROUPS = 243 + SYS_POSIX_SPAWN = 244 + SYS_FFSCTL = 245 + SYS_NFSCLNT = 247 + SYS_FHOPEN = 248 + SYS_MINHERIT = 250 + SYS_SEMSYS = 251 + SYS_MSGSYS = 252 + SYS_SHMSYS = 253 + SYS_SEMCTL = 254 + SYS_SEMGET = 255 + SYS_SEMOP = 256 + SYS_MSGCTL = 258 + SYS_MSGGET = 259 + SYS_MSGSND = 260 + SYS_MSGRCV = 261 + SYS_SHMAT = 262 + SYS_SHMCTL = 263 + SYS_SHMDT = 264 + SYS_SHMGET = 265 + SYS_SHM_OPEN = 266 + SYS_SHM_UNLINK = 267 + SYS_SEM_OPEN = 268 + SYS_SEM_CLOSE = 269 + SYS_SEM_UNLINK = 270 + SYS_SEM_WAIT = 271 + SYS_SEM_TRYWAIT = 272 + SYS_SEM_POST = 273 + SYS_SEM_GETVALUE = 274 + SYS_SEM_INIT = 275 + SYS_SEM_DESTROY = 276 + SYS_OPEN_EXTENDED = 277 + SYS_UMASK_EXTENDED = 278 + SYS_STAT_EXTENDED = 279 + SYS_LSTAT_EXTENDED = 280 + SYS_FSTAT_EXTENDED = 281 + SYS_CHMOD_EXTENDED = 282 + SYS_FCHMOD_EXTENDED = 283 + SYS_ACCESS_EXTENDED = 284 + SYS_SETTID = 285 + SYS_GETTID = 286 + SYS_SETSGROUPS = 287 + SYS_GETSGROUPS = 288 + SYS_SETWGROUPS = 289 + SYS_GETWGROUPS = 290 + SYS_MKFIFO_EXTENDED = 291 + SYS_MKDIR_EXTENDED = 292 + SYS_IDENTITYSVC = 293 + SYS_SHARED_REGION_CHECK_NP = 294 + SYS_SHARED_REGION_MAP_NP = 295 + SYS_VM_PRESSURE_MONITOR = 296 + SYS_PSYNCH_RW_LONGRDLOCK = 297 + SYS_PSYNCH_RW_YIELDWRLOCK = 298 + SYS_PSYNCH_RW_DOWNGRADE = 299 + SYS_PSYNCH_RW_UPGRADE = 300 + SYS_PSYNCH_MUTEXWAIT = 301 + SYS_PSYNCH_MUTEXDROP = 302 + SYS_PSYNCH_CVBROAD = 303 + SYS_PSYNCH_CVSIGNAL = 304 + SYS_PSYNCH_CVWAIT = 305 + SYS_PSYNCH_RW_RDLOCK = 306 + SYS_PSYNCH_RW_WRLOCK = 307 + SYS_PSYNCH_RW_UNLOCK = 308 + SYS_PSYNCH_RW_UNLOCK2 = 309 + SYS_GETSID = 310 + SYS_SETTID_WITH_PID = 311 + SYS_AIO_FSYNC = 313 + SYS_AIO_RETURN = 314 + SYS_AIO_SUSPEND = 315 + SYS_AIO_CANCEL = 316 + SYS_AIO_ERROR = 317 + SYS_AIO_READ = 318 + SYS_AIO_WRITE = 319 + SYS_LIO_LISTIO = 320 + SYS_IOPOLICYSYS = 322 + SYS_MLOCKALL = 324 + SYS_MUNLOCKALL = 325 + SYS_ISSETUGID = 327 + SYS___PTHREAD_KILL = 328 + SYS___PTHREAD_SIGMASK = 329 + SYS___SIGWAIT = 330 + SYS___DISABLE_THREADSIGNAL = 331 + SYS___PTHREAD_MARKCANCEL = 332 + SYS___PTHREAD_CANCELED = 333 + SYS___SEMWAIT_SIGNAL = 334 + SYS_PROC_INFO = 336 + SYS_SENDFILE = 337 + SYS_STAT64 = 338 + SYS_FSTAT64 = 339 + SYS_LSTAT64 = 340 + SYS_STAT64_EXTENDED = 341 + SYS_LSTAT64_EXTENDED = 342 + SYS_FSTAT64_EXTENDED = 343 + SYS_GETDIRENTRIES64 = 344 + SYS_STATFS64 = 345 + SYS_FSTATFS64 = 346 + SYS_GETFSSTAT64 = 347 + SYS___PTHREAD_CHDIR = 348 + SYS___PTHREAD_FCHDIR = 349 + SYS_AUDIT = 350 + SYS_AUDITON = 351 + SYS_GETAUID = 353 + SYS_SETAUID = 354 + SYS_GETAUDIT = 355 + SYS_SETAUDIT = 356 + SYS_GETAUDIT_ADDR = 357 + SYS_SETAUDIT_ADDR = 358 + SYS_AUDITCTL = 359 + SYS_BSDTHREAD_CREATE = 360 + SYS_BSDTHREAD_TERMINATE = 361 + SYS_KQUEUE = 362 + SYS_KEVENT = 363 + SYS_LCHOWN = 364 + SYS_STACK_SNAPSHOT = 365 + SYS_BSDTHREAD_REGISTER = 366 + SYS_WORKQ_OPEN = 367 + SYS_WORKQ_KERNRETURN = 368 + SYS_KEVENT64 = 369 + SYS___OLD_SEMWAIT_SIGNAL = 370 + SYS___OLD_SEMWAIT_SIGNAL_NOCANCEL = 371 + SYS_THREAD_SELFID = 372 + SYS___MAC_EXECVE = 380 + SYS___MAC_SYSCALL = 381 + SYS___MAC_GET_FILE = 382 + SYS___MAC_SET_FILE = 383 + SYS___MAC_GET_LINK = 384 + SYS___MAC_SET_LINK = 385 + SYS___MAC_GET_PROC = 386 + SYS___MAC_SET_PROC = 387 + SYS___MAC_GET_FD = 388 + SYS___MAC_SET_FD = 389 + SYS___MAC_GET_PID = 390 + SYS___MAC_GET_LCID = 391 + SYS___MAC_GET_LCTX = 392 + SYS___MAC_SET_LCTX = 393 + SYS_SETLCID = 394 + SYS_GETLCID = 395 + SYS_READ_NOCANCEL = 396 + SYS_WRITE_NOCANCEL = 397 + SYS_OPEN_NOCANCEL = 398 + SYS_CLOSE_NOCANCEL = 399 + SYS_WAIT4_NOCANCEL = 400 + SYS_RECVMSG_NOCANCEL = 401 + SYS_SENDMSG_NOCANCEL = 402 + SYS_RECVFROM_NOCANCEL = 403 + SYS_ACCEPT_NOCANCEL = 404 + SYS_MSYNC_NOCANCEL = 405 + SYS_FCNTL_NOCANCEL = 406 + SYS_SELECT_NOCANCEL = 407 + SYS_FSYNC_NOCANCEL = 408 + SYS_CONNECT_NOCANCEL = 409 + SYS_SIGSUSPEND_NOCANCEL = 410 + SYS_READV_NOCANCEL = 411 + SYS_WRITEV_NOCANCEL = 412 + SYS_SENDTO_NOCANCEL = 413 + SYS_PREAD_NOCANCEL = 414 + SYS_PWRITE_NOCANCEL = 415 + SYS_WAITID_NOCANCEL = 416 + SYS_POLL_NOCANCEL = 417 + SYS_MSGSND_NOCANCEL = 418 + SYS_MSGRCV_NOCANCEL = 419 + SYS_SEM_WAIT_NOCANCEL = 420 + SYS_AIO_SUSPEND_NOCANCEL = 421 + SYS___SIGWAIT_NOCANCEL = 422 + SYS___SEMWAIT_SIGNAL_NOCANCEL = 423 + SYS___MAC_MOUNT = 424 + SYS___MAC_GET_MOUNT = 425 + SYS___MAC_GETFSSTAT = 426 + SYS_FSGETPATH = 427 + SYS_AUDIT_SESSION_SELF = 428 + SYS_AUDIT_SESSION_JOIN = 429 + SYS_PID_SUSPEND = 430 + SYS_PID_RESUME = 431 + SYS_FILEPORT_MAKEPORT = 432 + SYS_FILEPORT_MAKEFD = 433 + SYS_MAXSYSCALL = 434 ) diff --git a/src/pkg/syscall/zsysnum_darwin_amd64.go b/src/pkg/syscall/zsysnum_darwin_amd64.go index f9c6e077d..50aec39f1 100644 --- a/src/pkg/syscall/zsysnum_darwin_amd64.go +++ b/src/pkg/syscall/zsysnum_darwin_amd64.go @@ -1,485 +1,355 @@ -// mksysnum_darwin.pl /home/rsc/pub/xnu-1228/bsd/kern/syscalls.master +// mksysnum_darwin.pl /usr/include/sys/syscall.h // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT package syscall const ( - // SYS_NOSYS = 0; // { int nosys(void); } { indirect syscall } - SYS_EXIT = 1 // { void exit(int rval); } - SYS_FORK = 2 // { int fork(void); } - SYS_READ = 3 // { user_ssize_t read(int fd, user_addr_t cbuf, user_size_t nbyte); } - SYS_WRITE = 4 // { user_ssize_t write(int fd, user_addr_t cbuf, user_size_t nbyte); } - SYS_OPEN = 5 // { int open(user_addr_t path, int flags, int mode); } - SYS_CLOSE = 6 // { int close(int fd); } - SYS_WAIT4 = 7 // { int wait4(int pid, user_addr_t status, int options, user_addr_t rusage); } - // SYS_NOSYS = 8; // { int nosys(void); } { old creat } - SYS_LINK = 9 // { int link(user_addr_t path, user_addr_t link); } - SYS_UNLINK = 10 // { int unlink(user_addr_t path); } - // SYS_NOSYS = 11; // { int nosys(void); } { old execv } - SYS_CHDIR = 12 // { int chdir(user_addr_t path); } - SYS_FCHDIR = 13 // { int fchdir(int fd); } - SYS_MKNOD = 14 // { int mknod(user_addr_t path, int mode, int dev); } - SYS_CHMOD = 15 // { int chmod(user_addr_t path, int mode); } - SYS_CHOWN = 16 // { int chown(user_addr_t path, int uid, int gid); } - SYS_OGETFSSTAT = 18 // { int ogetfsstat(user_addr_t buf, int bufsize, int flags); } - SYS_GETFSSTAT = 18 // { int getfsstat(user_addr_t buf, int bufsize, int flags); } - // SYS_NOSYS = 19; // { int nosys(void); } { old lseek } - SYS_GETPID = 20 // { int getpid(void); } - // SYS_NOSYS = 21; // { int nosys(void); } { old mount } - // SYS_NOSYS = 22; // { int nosys(void); } { old umount } - SYS_SETUID = 23 // { int setuid(uid_t uid); } - SYS_GETUID = 24 // { int getuid(void); } - SYS_GETEUID = 25 // { int geteuid(void); } - SYS_PTRACE = 26 // { int ptrace(int req, pid_t pid, caddr_t addr, int data); } - SYS_RECVMSG = 27 // { int recvmsg(int s, struct msghdr *msg, int flags); } - SYS_SENDMSG = 28 // { int sendmsg(int s, caddr_t msg, int flags); } - SYS_RECVFROM = 29 // { int recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, int *fromlenaddr); } - SYS_ACCEPT = 30 // { int accept(int s, caddr_t name, socklen_t *anamelen); } - SYS_GETPEERNAME = 31 // { int getpeername(int fdes, caddr_t asa, socklen_t *alen); } - SYS_GETSOCKNAME = 32 // { int getsockname(int fdes, caddr_t asa, socklen_t *alen); } - // SYS_NOSYS = 27; // { int nosys(void); } - // SYS_NOSYS = 28; // { int nosys(void); } - // SYS_NOSYS = 29; // { int nosys(void); } - // SYS_NOSYS = 30; // { int nosys(void); } - // SYS_NOSYS = 31; // { int nosys(void); } - // SYS_NOSYS = 32; // { int nosys(void); } - SYS_ACCESS = 33 // { int access(user_addr_t path, int flags); } - SYS_CHFLAGS = 34 // { int chflags(char *path, int flags); } - SYS_FCHFLAGS = 35 // { int fchflags(int fd, int flags); } - SYS_SYNC = 36 // { int sync(void); } - SYS_KILL = 37 // { int kill(int pid, int signum, int posix); } - // SYS_NOSYS = 38; // { int nosys(void); } { old stat } - SYS_GETPPID = 39 // { int getppid(void); } - // SYS_NOSYS = 40; // { int nosys(void); } { old lstat } - SYS_DUP = 41 // { int dup(u_int fd); } - SYS_PIPE = 42 // { int pipe(void); } - SYS_GETEGID = 43 // { int getegid(void); } - SYS_PROFIL = 44 // { int profil(short *bufbase, size_t bufsize, u_long pcoffset, u_int pcscale); } - // SYS_NOSYS = 45; // { int nosys(void); } { old ktrace } - SYS_SIGACTION = 46 // { int sigaction(int signum, struct __sigaction *nsa, struct sigaction *osa); } - SYS_GETGID = 47 // { int getgid(void); } - SYS_SIGPROCMASK = 48 // { int sigprocmask(int how, user_addr_t mask, user_addr_t omask); } - SYS_GETLOGIN = 49 // { int getlogin(char *namebuf, u_int namelen); } - SYS_SETLOGIN = 50 // { int setlogin(char *namebuf); } - SYS_ACCT = 51 // { int acct(char *path); } - SYS_SIGPENDING = 52 // { int sigpending(struct sigvec *osv); } - SYS_SIGALTSTACK = 53 // { int sigaltstack(struct sigaltstack *nss, struct sigaltstack *oss); } - SYS_IOCTL = 54 // { int ioctl(int fd, u_long com, caddr_t data); } - SYS_REBOOT = 55 // { int reboot(int opt, char *command); } - SYS_REVOKE = 56 // { int revoke(char *path); } - SYS_SYMLINK = 57 // { int symlink(char *path, char *link); } - SYS_READLINK = 58 // { int readlink(char *path, char *buf, int count); } - SYS_EXECVE = 59 // { int execve(char *fname, char **argp, char **envp); } - SYS_UMASK = 60 // { int umask(int newmask); } - SYS_CHROOT = 61 // { int chroot(user_addr_t path); } - // SYS_NOSYS = 62; // { int nosys(void); } { old fstat } - // SYS_NOSYS = 63; // { int nosys(void); } { used internally, reserved } - // SYS_NOSYS = 64; // { int nosys(void); } { old getpagesize } - SYS_MSYNC = 65 // { int msync(caddr_t addr, size_t len, int flags); } - SYS_VFORK = 66 // { int vfork(void); } - // SYS_NOSYS = 67; // { int nosys(void); } { old vread } - // SYS_NOSYS = 68; // { int nosys(void); } { old vwrite } - SYS_SBRK = 69 // { int sbrk(int incr) NO_SYSCALL_STUB; } - SYS_SSTK = 70 // { int sstk(int incr) NO_SYSCALL_STUB; } - // SYS_NOSYS = 71; // { int nosys(void); } { old mmap } - SYS_OVADVISE = 72 // { int ovadvise(void) NO_SYSCALL_STUB; } { old vadvise } - SYS_MUNMAP = 73 // { int munmap(caddr_t addr, size_t len); } - SYS_MPROTECT = 74 // { int mprotect(caddr_t addr, size_t len, int prot); } - SYS_MADVISE = 75 // { int madvise(caddr_t addr, size_t len, int behav); } - // SYS_NOSYS = 76; // { int nosys(void); } { old vhangup } - // SYS_NOSYS = 77; // { int nosys(void); } { old vlimit } - SYS_MINCORE = 78 // { int mincore(user_addr_t addr, user_size_t len, user_addr_t vec); } - SYS_GETGROUPS = 79 // { int getgroups(u_int gidsetsize, gid_t *gidset); } - SYS_SETGROUPS = 80 // { int setgroups(u_int gidsetsize, gid_t *gidset); } - SYS_GETPGRP = 81 // { int getpgrp(void); } - SYS_SETPGID = 82 // { int setpgid(int pid, int pgid); } - SYS_SETITIMER = 83 // { int setitimer(u_int which, struct itimerval *itv, struct itimerval *oitv); } - // SYS_NOSYS = 84; // { int nosys(void); } { old wait } - SYS_SWAPON = 85 // { int swapon(void); } - SYS_GETITIMER = 86 // { int getitimer(u_int which, struct itimerval *itv); } - // SYS_NOSYS = 87; // { int nosys(void); } { old gethostname } - // SYS_NOSYS = 88; // { int nosys(void); } { old sethostname } - SYS_GETDTABLESIZE = 89 // { int getdtablesize(void); } - SYS_DUP2 = 90 // { int dup2(u_int from, u_int to); } - // SYS_NOSYS = 91; // { int nosys(void); } { old getdopt } - SYS_FCNTL = 92 // { int fcntl(int fd, int cmd, long arg); } - SYS_SELECT = 93 // { int select(int nd, u_int32_t *in, u_int32_t *ou, u_int32_t *ex, struct timeval *tv); } - // SYS_NOSYS = 94; // { int nosys(void); } { old setdopt } - SYS_FSYNC = 95 // { int fsync(int fd); } - SYS_SETPRIORITY = 96 // { int setpriority(int which, id_t who, int prio); } - SYS_SOCKET = 97 // { int socket(int domain, int type, int protocol); } - SYS_CONNECT = 98 // { int connect(int s, caddr_t name, socklen_t namelen); } - // SYS_NOSYS = 97; // { int nosys(void); } - // SYS_NOSYS = 98; // { int nosys(void); } - // SYS_NOSYS = 99; // { int nosys(void); } { old accept } - SYS_GETPRIORITY = 100 // { int getpriority(int which, id_t who); } - // SYS_NOSYS = 101; // { int nosys(void); } { old send } - // SYS_NOSYS = 102; // { int nosys(void); } { old recv } - // SYS_NOSYS = 103; // { int nosys(void); } { old sigreturn } - SYS_BIND = 104 // { int bind(int s, caddr_t name, socklen_t namelen); } - SYS_SETSOCKOPT = 105 // { int setsockopt(int s, int level, int name, caddr_t val, socklen_t valsize); } - SYS_LISTEN = 106 // { int listen(int s, int backlog); } - // SYS_NOSYS = 104; // { int nosys(void); } - // SYS_NOSYS = 105; // { int nosys(void); } - // SYS_NOSYS = 106; // { int nosys(void); } - // SYS_NOSYS = 107; // { int nosys(void); } { old vtimes } - // SYS_NOSYS = 108; // { int nosys(void); } { old sigvec } - // SYS_NOSYS = 109; // { int nosys(void); } { old sigblock } - // SYS_NOSYS = 110; // { int nosys(void); } { old sigsetmask } - SYS_SIGSUSPEND = 111 // { int sigsuspend(sigset_t mask); } - // SYS_NOSYS = 112; // { int nosys(void); } { old sigstack } - // SYS_NOSYS = 113; // { int nosys(void); } { old recvmsg } - // SYS_NOSYS = 114; // { int nosys(void); } { old sendmsg } - // SYS_NOSYS = 113; // { int nosys(void); } - // SYS_NOSYS = 114; // { int nosys(void); } - // SYS_NOSYS = 115; // { int nosys(void); } { old vtrace } - SYS_GETTIMEOFDAY = 116 // { int gettimeofday(struct timeval *tp, struct timezone *tzp); } - SYS_GETRUSAGE = 117 // { int getrusage(int who, struct rusage *rusage); } - SYS_GETSOCKOPT = 118 // { int getsockopt(int s, int level, int name, caddr_t val, socklen_t *avalsize); } - // SYS_NOSYS = 118; // { int nosys(void); } - // SYS_NOSYS = 119; // { int nosys(void); } { old resuba } - SYS_READV = 120 // { user_ssize_t readv(int fd, struct iovec *iovp, u_int iovcnt); } - SYS_WRITEV = 121 // { user_ssize_t writev(int fd, struct iovec *iovp, u_int iovcnt); } - SYS_SETTIMEOFDAY = 122 // { int settimeofday(struct timeval *tv, struct timezone *tzp); } - SYS_FCHOWN = 123 // { int fchown(int fd, int uid, int gid); } - SYS_FCHMOD = 124 // { int fchmod(int fd, int mode); } - // SYS_NOSYS = 125; // { int nosys(void); } { old recvfrom } - SYS_SETREUID = 126 // { int setreuid(uid_t ruid, uid_t euid); } - SYS_SETREGID = 127 // { int setregid(gid_t rgid, gid_t egid); } - SYS_RENAME = 128 // { int rename(char *from, char *to); } - // SYS_NOSYS = 129; // { int nosys(void); } { old truncate } - // SYS_NOSYS = 130; // { int nosys(void); } { old ftruncate } - SYS_FLOCK = 131 // { int flock(int fd, int how); } - SYS_MKFIFO = 132 // { int mkfifo(user_addr_t path, int mode); } - SYS_SENDTO = 133 // { int sendto(int s, caddr_t buf, size_t len, int flags, caddr_t to, socklen_t tolen); } - SYS_SHUTDOWN = 134 // { int shutdown(int s, int how); } - SYS_SOCKETPAIR = 135 // { int socketpair(int domain, int type, int protocol, int *rsv); } - // SYS_NOSYS = 133; // { int nosys(void); } - // SYS_NOSYS = 134; // { int nosys(void); } - // SYS_NOSYS = 135; // { int nosys(void); } - SYS_MKDIR = 136 // { int mkdir(user_addr_t path, int mode); } - SYS_RMDIR = 137 // { int rmdir(char *path); } - SYS_UTIMES = 138 // { int utimes(char *path, struct timeval *tptr); } - SYS_FUTIMES = 139 // { int futimes(int fd, struct timeval *tptr); } - SYS_ADJTIME = 140 // { int adjtime(struct timeval *delta, struct timeval *olddelta); } - // SYS_NOSYS = 141; // { int nosys(void); } { old getpeername } - SYS_GETHOSTUUID = 142 // { int gethostuuid(unsigned char *uuid_buf, const struct timespec *timeoutp); } - // SYS_NOSYS = 143; // { int nosys(void); } { old sethostid } - // SYS_NOSYS = 144; // { int nosys(void); } { old getrlimit } - // SYS_NOSYS = 145; // { int nosys(void); } { old setrlimit } - // SYS_NOSYS = 146; // { int nosys(void); } { old killpg } - SYS_SETSID = 147 // { int setsid(void); } - // SYS_NOSYS = 148; // { int nosys(void); } { old setquota } - // SYS_NOSYS = 149; // { int nosys(void); } { old qquota } - // SYS_NOSYS = 150; // { int nosys(void); } { old getsockname } - SYS_GETPGID = 151 // { int getpgid(pid_t pid); } - SYS_SETPRIVEXEC = 152 // { int setprivexec(int flag); } - SYS_PREAD = 153 // { user_ssize_t pread(int fd, user_addr_t buf, user_size_t nbyte, off_t offset); } - SYS_PWRITE = 154 // { user_ssize_t pwrite(int fd, user_addr_t buf, user_size_t nbyte, off_t offset); } - SYS_NFSSVC = 155 // { int nfssvc(int flag, caddr_t argp); } - // SYS_NOSYS = 155; // { int nosys(void); } - // SYS_NOSYS = 156; // { int nosys(void); } { old getdirentries } - SYS_STATFS = 157 // { int statfs(char *path, struct statfs *buf); } - SYS_FSTATFS = 158 // { int fstatfs(int fd, struct statfs *buf); } - SYS_UNMOUNT = 159 // { int unmount(user_addr_t path, int flags); } - // SYS_NOSYS = 160; // { int nosys(void); } { old async_daemon } - SYS_GETFH = 161 // { int getfh(char *fname, fhandle_t *fhp); } - // SYS_NOSYS = 161; // { int nosys(void); } - // SYS_NOSYS = 162; // { int nosys(void); } { old getdomainname } - // SYS_NOSYS = 163; // { int nosys(void); } { old setdomainname } - // SYS_NOSYS = 164; // { int nosys(void); } - SYS_QUOTACTL = 165 // { int quotactl(const char *path, int cmd, int uid, caddr_t arg); } - // SYS_NOSYS = 166; // { int nosys(void); } { old exportfs } - SYS_MOUNT = 167 // { int mount(char *type, char *path, int flags, caddr_t data); } - // SYS_NOSYS = 168; // { int nosys(void); } { old ustat } - SYS_CSOPS = 169 // { int csops(pid_t pid, uint32_t ops, user_addr_t useraddr, user_size_t usersize); } - // SYS_NOSYS = 171; // { int nosys(void); } { old wait3 } - // SYS_NOSYS = 172; // { int nosys(void); } { old rpause } - SYS_WAITID = 173 // { int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options); } - // SYS_NOSYS = 174; // { int nosys(void); } { old getdents } - // SYS_NOSYS = 175; // { int nosys(void); } { old gc_control } - SYS_ADD_PROFIL = 176 // { int add_profil(short *bufbase, size_t bufsize, u_long pcoffset, u_int pcscale); } - // SYS_NOSYS = 177; // { int nosys(void); } - // SYS_NOSYS = 178; // { int nosys(void); } - // SYS_NOSYS = 179; // { int nosys(void); } - SYS_KDEBUG_TRACE = 180 // { int kdebug_trace(int code, int arg1, int arg2, int arg3, int arg4, int arg5) NO_SYSCALL_STUB; } - SYS_SETGID = 181 // { int setgid(gid_t gid); } - SYS_SETEGID = 182 // { int setegid(gid_t egid); } - SYS_SETEUID = 183 // { int seteuid(uid_t euid); } - SYS_SIGRETURN = 184 // { int sigreturn(struct ucontext *uctx, int infostyle); } - // SYS_NOSYS = 186; // { int nosys(void); } - // SYS_NOSYS = 187; // { int nosys(void); } - SYS_STAT = 188 // { int stat(user_addr_t path, user_addr_t ub); } - SYS_FSTAT = 189 // { int fstat(int fd, user_addr_t ub); } - SYS_LSTAT = 190 // { int lstat(user_addr_t path, user_addr_t ub); } - SYS_PATHCONF = 191 // { int pathconf(char *path, int name); } - SYS_FPATHCONF = 192 // { int fpathconf(int fd, int name); } - // SYS_NOSYS = 193; // { int nosys(void); } - SYS_GETRLIMIT = 194 // { int getrlimit(u_int which, struct rlimit *rlp); } - SYS_SETRLIMIT = 195 // { int setrlimit(u_int which, struct rlimit *rlp); } - SYS_GETDIRENTRIES = 196 // { int getdirentries(int fd, char *buf, u_int count, long *basep); } - SYS_MMAP = 197 // { user_addr_t mmap(caddr_t addr, size_t len, int prot, int flags, int fd, off_t pos); } - // SYS_NOSYS = 198; // { int nosys(void); } { __syscall } - SYS_LSEEK = 199 // { off_t lseek(int fd, off_t offset, int whence); } - SYS_TRUNCATE = 200 // { int truncate(char *path, off_t length); } - SYS_FTRUNCATE = 201 // { int ftruncate(int fd, off_t length); } - SYS___SYSCTL = 202 // { int __sysctl(int *name, u_int namelen, void *old, size_t *oldlenp, void *new, size_t newlen); } - SYS_MLOCK = 203 // { int mlock(caddr_t addr, size_t len); } - SYS_MUNLOCK = 204 // { int munlock(caddr_t addr, size_t len); } - SYS_UNDELETE = 205 // { int undelete(user_addr_t path); } - SYS_ATSOCKET = 206 // { int ATsocket(int proto); } - // SYS_NOSYS = 213; // { int nosys(void); } { Reserved for AppleTalk } - // SYS_NOSYS = 206; // { int nosys(void); } - // SYS_NOSYS = 207; // { int nosys(void); } - // SYS_NOSYS = 208; // { int nosys(void); } - // SYS_NOSYS = 209; // { int nosys(void); } - // SYS_NOSYS = 210; // { int nosys(void); } - // SYS_NOSYS = 211; // { int nosys(void); } - // SYS_NOSYS = 212; // { int nosys(void); } - // SYS_NOSYS = 213; // { int nosys(void); } { Reserved for AppleTalk } - SYS_KQUEUE_FROM_PORTSET_NP = 214 // { int kqueue_from_portset_np(int portset); } - SYS_KQUEUE_PORTSET_NP = 215 // { int kqueue_portset_np(int fd); } - SYS_GETATTRLIST = 220 // { int getattrlist(const char *path, struct attrlist *alist, void *attributeBuffer, size_t bufferSize, u_long options); } - SYS_SETATTRLIST = 221 // { int setattrlist(const char *path, struct attrlist *alist, void *attributeBuffer, size_t bufferSize, u_long options); } - SYS_GETDIRENTRIESATTR = 222 // { int getdirentriesattr(int fd, struct attrlist *alist, void *buffer, size_t buffersize, u_long *count, u_long *basep, u_long *newstate, u_long options); } - SYS_EXCHANGEDATA = 223 // { int exchangedata(const char *path1, const char *path2, u_long options); } - // SYS_NOSYS = 224; // { int nosys(void); } { was checkuseraccess } - SYS_SEARCHFS = 225 // { int searchfs(const char *path, struct fssearchblock *searchblock, u_long *nummatches, u_long scriptcode, u_long options, struct searchstate *state); } - SYS_DELETE = 226 // { int delete(user_addr_t path) NO_SYSCALL_STUB; } { private delete (Carbon semantics) } - SYS_COPYFILE = 227 // { int copyfile(char *from, char *to, int mode, int flags) NO_SYSCALL_STUB; } - // SYS_NOSYS = 228; // { int nosys(void); } - // SYS_NOSYS = 229; // { int nosys(void); } - SYS_POLL = 230 // { int poll(struct pollfd *fds, u_int nfds, int timeout); } - SYS_WATCHEVENT = 231 // { int watchevent(struct eventreq *u_req, int u_eventmask); } - SYS_WAITEVENT = 232 // { int waitevent(struct eventreq *u_req, struct timeval *tv); } - SYS_MODWATCH = 233 // { int modwatch(struct eventreq *u_req, int u_eventmask); } - SYS_GETXATTR = 234 // { user_ssize_t getxattr(user_addr_t path, user_addr_t attrname, user_addr_t value, size_t size, uint32_t position, int options); } - SYS_FGETXATTR = 235 // { user_ssize_t fgetxattr(int fd, user_addr_t attrname, user_addr_t value, size_t size, uint32_t position, int options); } - SYS_SETXATTR = 236 // { int setxattr(user_addr_t path, user_addr_t attrname, user_addr_t value, size_t size, uint32_t position, int options); } - SYS_FSETXATTR = 237 // { int fsetxattr(int fd, user_addr_t attrname, user_addr_t value, size_t size, uint32_t position, int options); } - SYS_REMOVEXATTR = 238 // { int removexattr(user_addr_t path, user_addr_t attrname, int options); } - SYS_FREMOVEXATTR = 239 // { int fremovexattr(int fd, user_addr_t attrname, int options); } - SYS_LISTXATTR = 240 // { user_ssize_t listxattr(user_addr_t path, user_addr_t namebuf, size_t bufsize, int options); } - SYS_FLISTXATTR = 241 // { user_ssize_t flistxattr(int fd, user_addr_t namebuf, size_t bufsize, int options); } - SYS_FSCTL = 242 // { int fsctl(const char *path, u_long cmd, caddr_t data, u_long options); } - SYS_INITGROUPS = 243 // { int initgroups(u_int gidsetsize, gid_t *gidset, int gmuid); } - SYS_POSIX_SPAWN = 244 // { int posix_spawn(pid_t *pid, const char *path, const struct _posix_spawn_args_desc *adesc, char **argv, char **envp); } - // SYS_NOSYS = 245; // { int nosys(void); } - // SYS_NOSYS = 246; // { int nosys(void); } - SYS_NFSCLNT = 247 // { int nfsclnt(int flag, caddr_t argp); } - // SYS_NOSYS = 247; // { int nosys(void); } - SYS_FHOPEN = 248 // { int fhopen(const struct fhandle *u_fhp, int flags); } - // SYS_NOSYS = 248; // { int nosys(void); } - // SYS_NOSYS = 249; // { int nosys(void); } - SYS_MINHERIT = 250 // { int minherit(void *addr, size_t len, int inherit); } - SYS_SEMSYS = 251 // { int semsys(u_int which, int a2, int a3, int a4, int a5); } - // SYS_NOSYS = 251; // { int nosys(void); } - SYS_MSGSYS = 252 // { int msgsys(u_int which, int a2, int a3, int a4, int a5); } - // SYS_NOSYS = 252; // { int nosys(void); } - SYS_SHMSYS = 253 // { int shmsys(u_int which, int a2, int a3, int a4); } - // SYS_NOSYS = 253; // { int nosys(void); } - SYS_SEMCTL = 254 // { int semctl(int semid, int semnum, int cmd, semun_t arg); } - SYS_SEMGET = 255 // { int semget(key_t key, int nsems, int semflg); } - SYS_SEMOP = 256 // { int semop(int semid, struct sembuf *sops, int nsops); } - // SYS_NOSYS = 257; // { int nosys(void); } - // SYS_NOSYS = 254; // { int nosys(void); } - // SYS_NOSYS = 255; // { int nosys(void); } - // SYS_NOSYS = 256; // { int nosys(void); } - // SYS_NOSYS = 257; // { int nosys(void); } - SYS_MSGCTL = 258 // { int msgctl(int msqid, int cmd, struct msqid_ds *buf); } - SYS_MSGGET = 259 // { int msgget(key_t key, int msgflg); } - SYS_MSGSND = 260 // { int msgsnd(int msqid, void *msgp, size_t msgsz, int msgflg); } - SYS_MSGRCV = 261 // { user_ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); } - // SYS_NOSYS = 258; // { int nosys(void); } - // SYS_NOSYS = 259; // { int nosys(void); } - // SYS_NOSYS = 260; // { int nosys(void); } - // SYS_NOSYS = 261; // { int nosys(void); } - SYS_SHMAT = 262 // { user_addr_t shmat(int shmid, void *shmaddr, int shmflg); } - SYS_SHMCTL = 263 // { int shmctl(int shmid, int cmd, struct shmid_ds *buf); } - SYS_SHMDT = 264 // { int shmdt(void *shmaddr); } - SYS_SHMGET = 265 // { int shmget(key_t key, size_t size, int shmflg); } - // SYS_NOSYS = 262; // { int nosys(void); } - // SYS_NOSYS = 263; // { int nosys(void); } - // SYS_NOSYS = 264; // { int nosys(void); } - // SYS_NOSYS = 265; // { int nosys(void); } - SYS_SHM_OPEN = 266 // { int shm_open(const char *name, int oflag, int mode); } - SYS_SHM_UNLINK = 267 // { int shm_unlink(const char *name); } - SYS_SEM_OPEN = 268 // { user_addr_t sem_open(const char *name, int oflag, int mode, int value); } - SYS_SEM_CLOSE = 269 // { int sem_close(sem_t *sem); } - SYS_SEM_UNLINK = 270 // { int sem_unlink(const char *name); } - SYS_SEM_WAIT = 271 // { int sem_wait(sem_t *sem); } - SYS_SEM_TRYWAIT = 272 // { int sem_trywait(sem_t *sem); } - SYS_SEM_POST = 273 // { int sem_post(sem_t *sem); } - SYS_SEM_GETVALUE = 274 // { int sem_getvalue(sem_t *sem, int *sval); } - SYS_SEM_INIT = 275 // { int sem_init(sem_t *sem, int phsared, u_int value); } - SYS_SEM_DESTROY = 276 // { int sem_destroy(sem_t *sem); } - SYS_OPEN_EXTENDED = 277 // { int open_extended(user_addr_t path, int flags, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } - SYS_UMASK_EXTENDED = 278 // { int umask_extended(int newmask, user_addr_t xsecurity) NO_SYSCALL_STUB; } - SYS_STAT_EXTENDED = 279 // { int stat_extended(user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } - SYS_LSTAT_EXTENDED = 280 // { int lstat_extended(user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } - SYS_FSTAT_EXTENDED = 281 // { int fstat_extended(int fd, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } - SYS_CHMOD_EXTENDED = 282 // { int chmod_extended(user_addr_t path, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } - SYS_FCHMOD_EXTENDED = 283 // { int fchmod_extended(int fd, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } - SYS_ACCESS_EXTENDED = 284 // { int access_extended(user_addr_t entries, size_t size, user_addr_t results, uid_t uid) NO_SYSCALL_STUB; } - SYS_SETTID = 285 // { int settid(uid_t uid, gid_t gid) NO_SYSCALL_STUB; } - SYS_GETTID = 286 // { int gettid(uid_t *uidp, gid_t *gidp) NO_SYSCALL_STUB; } - SYS_SETSGROUPS = 287 // { int setsgroups(int setlen, user_addr_t guidset) NO_SYSCALL_STUB; } - SYS_GETSGROUPS = 288 // { int getsgroups(user_addr_t setlen, user_addr_t guidset) NO_SYSCALL_STUB; } - SYS_SETWGROUPS = 289 // { int setwgroups(int setlen, user_addr_t guidset) NO_SYSCALL_STUB; } - SYS_GETWGROUPS = 290 // { int getwgroups(user_addr_t setlen, user_addr_t guidset) NO_SYSCALL_STUB; } - SYS_MKFIFO_EXTENDED = 291 // { int mkfifo_extended(user_addr_t path, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } - SYS_MKDIR_EXTENDED = 292 // { int mkdir_extended(user_addr_t path, uid_t uid, gid_t gid, int mode, user_addr_t xsecurity) NO_SYSCALL_STUB; } - SYS_IDENTITYSVC = 293 // { int identitysvc(int opcode, user_addr_t message) NO_SYSCALL_STUB; } - SYS_SHARED_REGION_CHECK_NP = 294 // { int shared_region_check_np(uint64_t *start_address) NO_SYSCALL_STUB; } - SYS_SHARED_REGION_MAP_NP = 295 // { int shared_region_map_np(int fd, uint32_t count, const struct shared_file_mapping_np *mappings) NO_SYSCALL_STUB; } - // SYS_NOSYS = 296; // { int nosys(void); } { old load_shared_file } - // SYS_NOSYS = 297; // { int nosys(void); } { old reset_shared_file } - // SYS_NOSYS = 298; // { int nosys(void); } { old new_system_shared_regions } - // SYS_ENOSYS = 299; // { int enosys(void); } { old shared_region_map_file_np } - // SYS_ENOSYS = 300; // { int enosys(void); } { old shared_region_make_private_np } - SYS___PTHREAD_MUTEX_DESTROY = 301 // { int __pthread_mutex_destroy(int mutexid); } - SYS___PTHREAD_MUTEX_INIT = 302 // { int __pthread_mutex_init(user_addr_t mutex, user_addr_t attr); } - SYS___PTHREAD_MUTEX_LOCK = 303 // { int __pthread_mutex_lock(int mutexid); } - SYS___PTHREAD_MUTEX_TRYLOCK = 304 // { int __pthread_mutex_trylock(int mutexid); } - SYS___PTHREAD_MUTEX_UNLOCK = 305 // { int __pthread_mutex_unlock(int mutexid); } - SYS___PTHREAD_COND_INIT = 306 // { int __pthread_cond_init(user_addr_t cond, user_addr_t attr); } - SYS___PTHREAD_COND_DESTROY = 307 // { int __pthread_cond_destroy(int condid); } - SYS___PTHREAD_COND_BROADCAST = 308 // { int __pthread_cond_broadcast(int condid); } - SYS___PTHREAD_COND_SIGNAL = 309 // { int __pthread_cond_signal(int condid); } - SYS_GETSID = 310 // { int getsid(pid_t pid); } - SYS_SETTID_WITH_PID = 311 // { int settid_with_pid(pid_t pid, int assume) NO_SYSCALL_STUB; } - SYS___PTHREAD_COND_TIMEDWAIT = 312 // { int __pthread_cond_timedwait(int condid, int mutexid, user_addr_t abstime); } - SYS_AIO_FSYNC = 313 // { int aio_fsync(int op, user_addr_t aiocbp); } - SYS_AIO_RETURN = 314 // { user_ssize_t aio_return(user_addr_t aiocbp); } - SYS_AIO_SUSPEND = 315 // { int aio_suspend(user_addr_t aiocblist, int nent, user_addr_t timeoutp); } - SYS_AIO_CANCEL = 316 // { int aio_cancel(int fd, user_addr_t aiocbp); } - SYS_AIO_ERROR = 317 // { int aio_error(user_addr_t aiocbp); } - SYS_AIO_READ = 318 // { int aio_read(user_addr_t aiocbp); } - SYS_AIO_WRITE = 319 // { int aio_write(user_addr_t aiocbp); } - SYS_LIO_LISTIO = 320 // { int lio_listio(int mode, user_addr_t aiocblist, int nent, user_addr_t sigp); } - SYS___PTHREAD_COND_WAIT = 321 // { int __pthread_cond_wait(int condid, int mutexid); } - SYS_IOPOLICYSYS = 322 // { int iopolicysys(int cmd, void *arg) NO_SYSCALL_STUB; } - // SYS_NOSYS = 323; // { int nosys(void); } - SYS_MLOCKALL = 324 // { int mlockall(int how); } - SYS_MUNLOCKALL = 325 // { int munlockall(int how); } - // SYS_NOSYS = 326; // { int nosys(void); } - SYS_ISSETUGID = 327 // { int issetugid(void); } - SYS___PTHREAD_KILL = 328 // { int __pthread_kill(int thread_port, int sig); } - SYS___PTHREAD_SIGMASK = 329 // { int __pthread_sigmask(int how, user_addr_t set, user_addr_t oset); } - SYS___SIGWAIT = 330 // { int __sigwait(user_addr_t set, user_addr_t sig); } - SYS___DISABLE_THREADSIGNAL = 331 // { int __disable_threadsignal(int value); } - SYS___PTHREAD_MARKCANCEL = 332 // { int __pthread_markcancel(int thread_port); } - SYS___PTHREAD_CANCELED = 333 // { int __pthread_canceled(int action); } - SYS___SEMWAIT_SIGNAL = 334 // { int __semwait_signal(int cond_sem, int mutex_sem, int timeout, int relative, time_t tv_sec, int32_t tv_nsec); } - // SYS_NOSYS = 335; // { int nosys(void); } { old utrace } - SYS_PROC_INFO = 336 // { int proc_info(int32_t callnum,int32_t pid,uint32_t flavor, uint64_t arg,user_addr_t buffer,int32_t buffersize) NO_SYSCALL_STUB; } - SYS_SENDFILE = 337 // { int sendfile(int fd, int s, off_t offset, off_t *nbytes, struct sf_hdtr *hdtr, int flags); } - // SYS_NOSYS = 337; // { int nosys(void); } - SYS_STAT64 = 338 // { int stat64(user_addr_t path, user_addr_t ub); } - SYS_FSTAT64 = 339 // { int fstat64(int fd, user_addr_t ub); } - SYS_LSTAT64 = 340 // { int lstat64(user_addr_t path, user_addr_t ub); } - SYS_STAT64_EXTENDED = 341 // { int stat64_extended(user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } - SYS_LSTAT64_EXTENDED = 342 // { int lstat64_extended(user_addr_t path, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } - SYS_FSTAT64_EXTENDED = 343 // { int fstat64_extended(int fd, user_addr_t ub, user_addr_t xsecurity, user_addr_t xsecurity_size) NO_SYSCALL_STUB; } - SYS_GETDIRENTRIES64 = 344 // { user_ssize_t getdirentries64(int fd, void *buf, user_size_t bufsize, off_t *position) NO_SYSCALL_STUB; } - SYS_STATFS64 = 345 // { int statfs64(char *path, struct statfs64 *buf); } - SYS_FSTATFS64 = 346 // { int fstatfs64(int fd, struct statfs64 *buf); } - SYS_GETFSSTAT64 = 347 // { int getfsstat64(user_addr_t buf, int bufsize, int flags); } - SYS___PTHREAD_CHDIR = 348 // { int __pthread_chdir(user_addr_t path); } - SYS___PTHREAD_FCHDIR = 349 // { int __pthread_fchdir(int fd); } - SYS_AUDIT = 350 // { int audit(void *record, int length); } - SYS_AUDITON = 351 // { int auditon(int cmd, void *data, int length); } - // SYS_NOSYS = 352; // { int nosys(void); } - SYS_GETAUID = 353 // { int getauid(au_id_t *auid); } - SYS_SETAUID = 354 // { int setauid(au_id_t *auid); } - SYS_GETAUDIT = 355 // { int getaudit(struct auditinfo *auditinfo); } - SYS_SETAUDIT = 356 // { int setaudit(struct auditinfo *auditinfo); } - SYS_GETAUDIT_ADDR = 357 // { int getaudit_addr(struct auditinfo_addr *auditinfo_addr, int length); } - SYS_SETAUDIT_ADDR = 358 // { int setaudit_addr(struct auditinfo_addr *auditinfo_addr, int length); } - SYS_AUDITCTL = 359 // { int auditctl(char *path); } - // SYS_NOSYS = 350; // { int nosys(void); } - // SYS_NOSYS = 351; // { int nosys(void); } - // SYS_NOSYS = 352; // { int nosys(void); } - // SYS_NOSYS = 353; // { int nosys(void); } - // SYS_NOSYS = 354; // { int nosys(void); } - // SYS_NOSYS = 355; // { int nosys(void); } - // SYS_NOSYS = 356; // { int nosys(void); } - // SYS_NOSYS = 357; // { int nosys(void); } - // SYS_NOSYS = 358; // { int nosys(void); } - // SYS_NOSYS = 359; // { int nosys(void); } - SYS_BSDTHREAD_CREATE = 360 // { user_addr_t bsdthread_create(user_addr_t func, user_addr_t func_arg, user_addr_t stack, user_addr_t pthread, uint32_t flags) NO_SYSCALL_STUB; } - SYS_BSDTHREAD_TERMINATE = 361 // { int bsdthread_terminate(user_addr_t stackaddr, size_t freesize, uint32_t port, uint32_t sem) NO_SYSCALL_STUB; } - SYS_KQUEUE = 362 // { int kqueue(void); } - SYS_KEVENT = 363 // { int kevent(int fd, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout); } - SYS_LCHOWN = 364 // { int lchown(user_addr_t path, uid_t owner, gid_t group); } - SYS_STACK_SNAPSHOT = 365 // { int stack_snapshot(pid_t pid, user_addr_t tracebuf, uint32_t tracebuf_size, uint32_t options) NO_SYSCALL_STUB; } - SYS_BSDTHREAD_REGISTER = 366 // { int bsdthread_register(user_addr_t threadstart, user_addr_t wqthread, int pthsize) NO_SYSCALL_STUB; } - SYS_WORKQ_OPEN = 367 // { int workq_open(void) NO_SYSCALL_STUB; } - SYS_WORKQ_OPS = 368 // { int workq_ops(int options, user_addr_t item, int prio) NO_SYSCALL_STUB; } - // SYS_NOSYS = 369; // { int nosys(void); } - // SYS_NOSYS = 370; // { int nosys(void); } - // SYS_NOSYS = 371; // { int nosys(void); } - // SYS_NOSYS = 372; // { int nosys(void); } - // SYS_NOSYS = 373; // { int nosys(void); } - // SYS_NOSYS = 374; // { int nosys(void); } - // SYS_NOSYS = 375; // { int nosys(void); } - // SYS_NOSYS = 376; // { int nosys(void); } - // SYS_NOSYS = 377; // { int nosys(void); } - // SYS_NOSYS = 378; // { int nosys(void); } - // SYS_NOSYS = 379; // { int nosys(void); } - SYS___MAC_EXECVE = 380 // { int __mac_execve(char *fname, char **argp, char **envp, struct mac *mac_p); } - SYS___MAC_SYSCALL = 381 // { int __mac_syscall(char *policy, int call, user_addr_t arg); } - SYS___MAC_GET_FILE = 382 // { int __mac_get_file(char *path_p, struct mac *mac_p); } - SYS___MAC_SET_FILE = 383 // { int __mac_set_file(char *path_p, struct mac *mac_p); } - SYS___MAC_GET_LINK = 384 // { int __mac_get_link(char *path_p, struct mac *mac_p); } - SYS___MAC_SET_LINK = 385 // { int __mac_set_link(char *path_p, struct mac *mac_p); } - SYS___MAC_GET_PROC = 386 // { int __mac_get_proc(struct mac *mac_p); } - SYS___MAC_SET_PROC = 387 // { int __mac_set_proc(struct mac *mac_p); } - SYS___MAC_GET_FD = 388 // { int __mac_get_fd(int fd, struct mac *mac_p); } - SYS___MAC_SET_FD = 389 // { int __mac_set_fd(int fd, struct mac *mac_p); } - SYS___MAC_GET_PID = 390 // { int __mac_get_pid(pid_t pid, struct mac *mac_p); } - SYS___MAC_GET_LCID = 391 // { int __mac_get_lcid(pid_t lcid, struct mac *mac_p); } - SYS___MAC_GET_LCTX = 392 // { int __mac_get_lctx(struct mac *mac_p); } - SYS___MAC_SET_LCTX = 393 // { int __mac_set_lctx(struct mac *mac_p); } - SYS_SETLCID = 394 // { int setlcid(pid_t pid, pid_t lcid) NO_SYSCALL_STUB; } - SYS_GETLCID = 395 // { int getlcid(pid_t pid) NO_SYSCALL_STUB; } - SYS_READ_NOCANCEL = 396 // { user_ssize_t read_nocancel(int fd, user_addr_t cbuf, user_size_t nbyte) NO_SYSCALL_STUB; } - SYS_WRITE_NOCANCEL = 397 // { user_ssize_t write_nocancel(int fd, user_addr_t cbuf, user_size_t nbyte) NO_SYSCALL_STUB; } - SYS_OPEN_NOCANCEL = 398 // { int open_nocancel(user_addr_t path, int flags, int mode) NO_SYSCALL_STUB; } - SYS_CLOSE_NOCANCEL = 399 // { int close_nocancel(int fd) NO_SYSCALL_STUB; } - SYS_WAIT4_NOCANCEL = 400 // { int wait4_nocancel(int pid, user_addr_t status, int options, user_addr_t rusage) NO_SYSCALL_STUB; } - SYS_RECVMSG_NOCANCEL = 401 // { int recvmsg_nocancel(int s, struct msghdr *msg, int flags) NO_SYSCALL_STUB; } - SYS_SENDMSG_NOCANCEL = 402 // { int sendmsg_nocancel(int s, caddr_t msg, int flags) NO_SYSCALL_STUB; } - SYS_RECVFROM_NOCANCEL = 403 // { int recvfrom_nocancel(int s, void *buf, size_t len, int flags, struct sockaddr *from, int *fromlenaddr) NO_SYSCALL_STUB; } - SYS_ACCEPT_NOCANCEL = 404 // { int accept_nocancel(int s, caddr_t name, socklen_t *anamelen) NO_SYSCALL_STUB; } - // SYS_NOSYS = 401; // { int nosys(void); } - // SYS_NOSYS = 402; // { int nosys(void); } - // SYS_NOSYS = 403; // { int nosys(void); } - // SYS_NOSYS = 404; // { int nosys(void); } - SYS_MSYNC_NOCANCEL = 405 // { int msync_nocancel(caddr_t addr, size_t len, int flags) NO_SYSCALL_STUB; } - SYS_FCNTL_NOCANCEL = 406 // { int fcntl_nocancel(int fd, int cmd, long arg) NO_SYSCALL_STUB; } - SYS_SELECT_NOCANCEL = 407 // { int select_nocancel(int nd, u_int32_t *in, u_int32_t *ou, u_int32_t *ex, struct timeval *tv) NO_SYSCALL_STUB; } - SYS_FSYNC_NOCANCEL = 408 // { int fsync_nocancel(int fd) NO_SYSCALL_STUB; } - SYS_CONNECT_NOCANCEL = 409 // { int connect_nocancel(int s, caddr_t name, socklen_t namelen) NO_SYSCALL_STUB; } - // SYS_NOSYS = 409; // { int nosys(void); } - SYS_SIGSUSPEND_NOCANCEL = 410 // { int sigsuspend_nocancel(sigset_t mask) NO_SYSCALL_STUB; } - SYS_READV_NOCANCEL = 411 // { user_ssize_t readv_nocancel(int fd, struct iovec *iovp, u_int iovcnt) NO_SYSCALL_STUB; } - SYS_WRITEV_NOCANCEL = 412 // { user_ssize_t writev_nocancel(int fd, struct iovec *iovp, u_int iovcnt) NO_SYSCALL_STUB; } - SYS_SENDTO_NOCANCEL = 413 // { int sendto_nocancel(int s, caddr_t buf, size_t len, int flags, caddr_t to, socklen_t tolen) NO_SYSCALL_STUB; } - // SYS_NOSYS = 413; // { int nosys(void); } - SYS_PREAD_NOCANCEL = 414 // { user_ssize_t pread_nocancel(int fd, user_addr_t buf, user_size_t nbyte, off_t offset) NO_SYSCALL_STUB; } - SYS_PWRITE_NOCANCEL = 415 // { user_ssize_t pwrite_nocancel(int fd, user_addr_t buf, user_size_t nbyte, off_t offset) NO_SYSCALL_STUB; } - SYS_WAITID_NOCANCEL = 416 // { int waitid_nocancel(idtype_t idtype, id_t id, siginfo_t *infop, int options) NO_SYSCALL_STUB; } - SYS_POLL_NOCANCEL = 417 // { int poll_nocancel(struct pollfd *fds, u_int nfds, int timeout) NO_SYSCALL_STUB; } - SYS_MSGSND_NOCANCEL = 418 // { int msgsnd_nocancel(int msqid, void *msgp, size_t msgsz, int msgflg) NO_SYSCALL_STUB; } - SYS_MSGRCV_NOCANCEL = 419 // { user_ssize_t msgrcv_nocancel(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) NO_SYSCALL_STUB; } - // SYS_NOSYS = 418; // { int nosys(void); } - // SYS_NOSYS = 419; // { int nosys(void); } - SYS_SEM_WAIT_NOCANCEL = 420 // { int sem_wait_nocancel(sem_t *sem) NO_SYSCALL_STUB; } - SYS_AIO_SUSPEND_NOCANCEL = 421 // { int aio_suspend_nocancel(user_addr_t aiocblist, int nent, user_addr_t timeoutp) NO_SYSCALL_STUB; } - SYS___SIGWAIT_NOCANCEL = 422 // { int __sigwait_nocancel(user_addr_t set, user_addr_t sig) NO_SYSCALL_STUB; } - SYS___SEMWAIT_SIGNAL_NOCANCEL = 423 // { int __semwait_signal_nocancel(int cond_sem, int mutex_sem, int timeout, int relative, time_t tv_sec, int32_t tv_nsec) NO_SYSCALL_STUB; } - SYS___MAC_MOUNT = 424 // { int __mac_mount(char *type, char *path, int flags, caddr_t data, struct mac *mac_p); } - SYS___MAC_GET_MOUNT = 425 // { int __mac_get_mount(char *path, struct mac *mac_p); } - SYS___MAC_GETFSSTAT = 426 // { int __mac_getfsstat(user_addr_t buf, int bufsize, user_addr_t mac, int macsize, int flags); } + SYS_SYSCALL = 0 + SYS_EXIT = 1 + SYS_FORK = 2 + SYS_READ = 3 + SYS_WRITE = 4 + SYS_OPEN = 5 + SYS_CLOSE = 6 + SYS_WAIT4 = 7 + SYS_LINK = 9 + SYS_UNLINK = 10 + SYS_CHDIR = 12 + SYS_FCHDIR = 13 + SYS_MKNOD = 14 + SYS_CHMOD = 15 + SYS_CHOWN = 16 + SYS_GETFSSTAT = 18 + SYS_GETPID = 20 + SYS_SETUID = 23 + SYS_GETUID = 24 + SYS_GETEUID = 25 + SYS_PTRACE = 26 + SYS_RECVMSG = 27 + SYS_SENDMSG = 28 + SYS_RECVFROM = 29 + SYS_ACCEPT = 30 + SYS_GETPEERNAME = 31 + SYS_GETSOCKNAME = 32 + SYS_ACCESS = 33 + SYS_CHFLAGS = 34 + SYS_FCHFLAGS = 35 + SYS_SYNC = 36 + SYS_KILL = 37 + SYS_GETPPID = 39 + SYS_DUP = 41 + SYS_PIPE = 42 + SYS_GETEGID = 43 + SYS_PROFIL = 44 + SYS_SIGACTION = 46 + SYS_GETGID = 47 + SYS_SIGPROCMASK = 48 + SYS_GETLOGIN = 49 + SYS_SETLOGIN = 50 + SYS_ACCT = 51 + SYS_SIGPENDING = 52 + SYS_SIGALTSTACK = 53 + SYS_IOCTL = 54 + SYS_REBOOT = 55 + SYS_REVOKE = 56 + SYS_SYMLINK = 57 + SYS_READLINK = 58 + SYS_EXECVE = 59 + SYS_UMASK = 60 + SYS_CHROOT = 61 + SYS_MSYNC = 65 + SYS_VFORK = 66 + SYS_MUNMAP = 73 + SYS_MPROTECT = 74 + SYS_MADVISE = 75 + SYS_MINCORE = 78 + SYS_GETGROUPS = 79 + SYS_SETGROUPS = 80 + SYS_GETPGRP = 81 + SYS_SETPGID = 82 + SYS_SETITIMER = 83 + SYS_SWAPON = 85 + SYS_GETITIMER = 86 + SYS_GETDTABLESIZE = 89 + SYS_DUP2 = 90 + SYS_FCNTL = 92 + SYS_SELECT = 93 + SYS_FSYNC = 95 + SYS_SETPRIORITY = 96 + SYS_SOCKET = 97 + SYS_CONNECT = 98 + SYS_GETPRIORITY = 100 + SYS_BIND = 104 + SYS_SETSOCKOPT = 105 + SYS_LISTEN = 106 + SYS_SIGSUSPEND = 111 + SYS_GETTIMEOFDAY = 116 + SYS_GETRUSAGE = 117 + SYS_GETSOCKOPT = 118 + SYS_READV = 120 + SYS_WRITEV = 121 + SYS_SETTIMEOFDAY = 122 + SYS_FCHOWN = 123 + SYS_FCHMOD = 124 + SYS_SETREUID = 126 + SYS_SETREGID = 127 + SYS_RENAME = 128 + SYS_FLOCK = 131 + SYS_MKFIFO = 132 + SYS_SENDTO = 133 + SYS_SHUTDOWN = 134 + SYS_SOCKETPAIR = 135 + SYS_MKDIR = 136 + SYS_RMDIR = 137 + SYS_UTIMES = 138 + SYS_FUTIMES = 139 + SYS_ADJTIME = 140 + SYS_GETHOSTUUID = 142 + SYS_SETSID = 147 + SYS_GETPGID = 151 + SYS_SETPRIVEXEC = 152 + SYS_PREAD = 153 + SYS_PWRITE = 154 + SYS_NFSSVC = 155 + SYS_STATFS = 157 + SYS_FSTATFS = 158 + SYS_UNMOUNT = 159 + SYS_GETFH = 161 + SYS_QUOTACTL = 165 + SYS_MOUNT = 167 + SYS_CSOPS = 169 + SYS_WAITID = 173 + SYS_ADD_PROFIL = 176 + SYS_KDEBUG_TRACE = 180 + SYS_SETGID = 181 + SYS_SETEGID = 182 + SYS_SETEUID = 183 + SYS_SIGRETURN = 184 + SYS_CHUD = 185 + SYS_FDATASYNC = 187 + SYS_STAT = 188 + SYS_FSTAT = 189 + SYS_LSTAT = 190 + SYS_PATHCONF = 191 + SYS_FPATHCONF = 192 + SYS_GETRLIMIT = 194 + SYS_SETRLIMIT = 195 + SYS_GETDIRENTRIES = 196 + SYS_MMAP = 197 + SYS_LSEEK = 199 + SYS_TRUNCATE = 200 + SYS_FTRUNCATE = 201 + SYS___SYSCTL = 202 + SYS_MLOCK = 203 + SYS_MUNLOCK = 204 + SYS_UNDELETE = 205 + SYS_ATSOCKET = 206 + SYS_ATGETMSG = 207 + SYS_ATPUTMSG = 208 + SYS_ATPSNDREQ = 209 + SYS_ATPSNDRSP = 210 + SYS_ATPGETREQ = 211 + SYS_ATPGETRSP = 212 + SYS_MKCOMPLEX = 216 + SYS_STATV = 217 + SYS_LSTATV = 218 + SYS_FSTATV = 219 + SYS_GETATTRLIST = 220 + SYS_SETATTRLIST = 221 + SYS_GETDIRENTRIESATTR = 222 + SYS_EXCHANGEDATA = 223 + SYS_SEARCHFS = 225 + SYS_DELETE = 226 + SYS_COPYFILE = 227 + SYS_FGETATTRLIST = 228 + SYS_FSETATTRLIST = 229 + SYS_POLL = 230 + SYS_WATCHEVENT = 231 + SYS_WAITEVENT = 232 + SYS_MODWATCH = 233 + SYS_GETXATTR = 234 + SYS_FGETXATTR = 235 + SYS_SETXATTR = 236 + SYS_FSETXATTR = 237 + SYS_REMOVEXATTR = 238 + SYS_FREMOVEXATTR = 239 + SYS_LISTXATTR = 240 + SYS_FLISTXATTR = 241 + SYS_FSCTL = 242 + SYS_INITGROUPS = 243 + SYS_POSIX_SPAWN = 244 + SYS_FFSCTL = 245 + SYS_NFSCLNT = 247 + SYS_FHOPEN = 248 + SYS_MINHERIT = 250 + SYS_SEMSYS = 251 + SYS_MSGSYS = 252 + SYS_SHMSYS = 253 + SYS_SEMCTL = 254 + SYS_SEMGET = 255 + SYS_SEMOP = 256 + SYS_MSGCTL = 258 + SYS_MSGGET = 259 + SYS_MSGSND = 260 + SYS_MSGRCV = 261 + SYS_SHMAT = 262 + SYS_SHMCTL = 263 + SYS_SHMDT = 264 + SYS_SHMGET = 265 + SYS_SHM_OPEN = 266 + SYS_SHM_UNLINK = 267 + SYS_SEM_OPEN = 268 + SYS_SEM_CLOSE = 269 + SYS_SEM_UNLINK = 270 + SYS_SEM_WAIT = 271 + SYS_SEM_TRYWAIT = 272 + SYS_SEM_POST = 273 + SYS_SEM_GETVALUE = 274 + SYS_SEM_INIT = 275 + SYS_SEM_DESTROY = 276 + SYS_OPEN_EXTENDED = 277 + SYS_UMASK_EXTENDED = 278 + SYS_STAT_EXTENDED = 279 + SYS_LSTAT_EXTENDED = 280 + SYS_FSTAT_EXTENDED = 281 + SYS_CHMOD_EXTENDED = 282 + SYS_FCHMOD_EXTENDED = 283 + SYS_ACCESS_EXTENDED = 284 + SYS_SETTID = 285 + SYS_GETTID = 286 + SYS_SETSGROUPS = 287 + SYS_GETSGROUPS = 288 + SYS_SETWGROUPS = 289 + SYS_GETWGROUPS = 290 + SYS_MKFIFO_EXTENDED = 291 + SYS_MKDIR_EXTENDED = 292 + SYS_IDENTITYSVC = 293 + SYS_SHARED_REGION_CHECK_NP = 294 + SYS_SHARED_REGION_MAP_NP = 295 + SYS_VM_PRESSURE_MONITOR = 296 + SYS_PSYNCH_RW_LONGRDLOCK = 297 + SYS_PSYNCH_RW_YIELDWRLOCK = 298 + SYS_PSYNCH_RW_DOWNGRADE = 299 + SYS_PSYNCH_RW_UPGRADE = 300 + SYS_PSYNCH_MUTEXWAIT = 301 + SYS_PSYNCH_MUTEXDROP = 302 + SYS_PSYNCH_CVBROAD = 303 + SYS_PSYNCH_CVSIGNAL = 304 + SYS_PSYNCH_CVWAIT = 305 + SYS_PSYNCH_RW_RDLOCK = 306 + SYS_PSYNCH_RW_WRLOCK = 307 + SYS_PSYNCH_RW_UNLOCK = 308 + SYS_PSYNCH_RW_UNLOCK2 = 309 + SYS_GETSID = 310 + SYS_SETTID_WITH_PID = 311 + SYS_AIO_FSYNC = 313 + SYS_AIO_RETURN = 314 + SYS_AIO_SUSPEND = 315 + SYS_AIO_CANCEL = 316 + SYS_AIO_ERROR = 317 + SYS_AIO_READ = 318 + SYS_AIO_WRITE = 319 + SYS_LIO_LISTIO = 320 + SYS_IOPOLICYSYS = 322 + SYS_MLOCKALL = 324 + SYS_MUNLOCKALL = 325 + SYS_ISSETUGID = 327 + SYS___PTHREAD_KILL = 328 + SYS___PTHREAD_SIGMASK = 329 + SYS___SIGWAIT = 330 + SYS___DISABLE_THREADSIGNAL = 331 + SYS___PTHREAD_MARKCANCEL = 332 + SYS___PTHREAD_CANCELED = 333 + SYS___SEMWAIT_SIGNAL = 334 + SYS_PROC_INFO = 336 + SYS_SENDFILE = 337 + SYS_STAT64 = 338 + SYS_FSTAT64 = 339 + SYS_LSTAT64 = 340 + SYS_STAT64_EXTENDED = 341 + SYS_LSTAT64_EXTENDED = 342 + SYS_FSTAT64_EXTENDED = 343 + SYS_GETDIRENTRIES64 = 344 + SYS_STATFS64 = 345 + SYS_FSTATFS64 = 346 + SYS_GETFSSTAT64 = 347 + SYS___PTHREAD_CHDIR = 348 + SYS___PTHREAD_FCHDIR = 349 + SYS_AUDIT = 350 + SYS_AUDITON = 351 + SYS_GETAUID = 353 + SYS_SETAUID = 354 + SYS_GETAUDIT = 355 + SYS_SETAUDIT = 356 + SYS_GETAUDIT_ADDR = 357 + SYS_SETAUDIT_ADDR = 358 + SYS_AUDITCTL = 359 + SYS_BSDTHREAD_CREATE = 360 + SYS_BSDTHREAD_TERMINATE = 361 + SYS_KQUEUE = 362 + SYS_KEVENT = 363 + SYS_LCHOWN = 364 + SYS_STACK_SNAPSHOT = 365 + SYS_BSDTHREAD_REGISTER = 366 + SYS_WORKQ_OPEN = 367 + SYS_WORKQ_KERNRETURN = 368 + SYS_KEVENT64 = 369 + SYS___OLD_SEMWAIT_SIGNAL = 370 + SYS___OLD_SEMWAIT_SIGNAL_NOCANCEL = 371 + SYS_THREAD_SELFID = 372 + SYS___MAC_EXECVE = 380 + SYS___MAC_SYSCALL = 381 + SYS___MAC_GET_FILE = 382 + SYS___MAC_SET_FILE = 383 + SYS___MAC_GET_LINK = 384 + SYS___MAC_SET_LINK = 385 + SYS___MAC_GET_PROC = 386 + SYS___MAC_SET_PROC = 387 + SYS___MAC_GET_FD = 388 + SYS___MAC_SET_FD = 389 + SYS___MAC_GET_PID = 390 + SYS___MAC_GET_LCID = 391 + SYS___MAC_GET_LCTX = 392 + SYS___MAC_SET_LCTX = 393 + SYS_SETLCID = 394 + SYS_GETLCID = 395 + SYS_READ_NOCANCEL = 396 + SYS_WRITE_NOCANCEL = 397 + SYS_OPEN_NOCANCEL = 398 + SYS_CLOSE_NOCANCEL = 399 + SYS_WAIT4_NOCANCEL = 400 + SYS_RECVMSG_NOCANCEL = 401 + SYS_SENDMSG_NOCANCEL = 402 + SYS_RECVFROM_NOCANCEL = 403 + SYS_ACCEPT_NOCANCEL = 404 + SYS_MSYNC_NOCANCEL = 405 + SYS_FCNTL_NOCANCEL = 406 + SYS_SELECT_NOCANCEL = 407 + SYS_FSYNC_NOCANCEL = 408 + SYS_CONNECT_NOCANCEL = 409 + SYS_SIGSUSPEND_NOCANCEL = 410 + SYS_READV_NOCANCEL = 411 + SYS_WRITEV_NOCANCEL = 412 + SYS_SENDTO_NOCANCEL = 413 + SYS_PREAD_NOCANCEL = 414 + SYS_PWRITE_NOCANCEL = 415 + SYS_WAITID_NOCANCEL = 416 + SYS_POLL_NOCANCEL = 417 + SYS_MSGSND_NOCANCEL = 418 + SYS_MSGRCV_NOCANCEL = 419 + SYS_SEM_WAIT_NOCANCEL = 420 + SYS_AIO_SUSPEND_NOCANCEL = 421 + SYS___SIGWAIT_NOCANCEL = 422 + SYS___SEMWAIT_SIGNAL_NOCANCEL = 423 + SYS___MAC_MOUNT = 424 + SYS___MAC_GET_MOUNT = 425 + SYS___MAC_GETFSSTAT = 426 + SYS_FSGETPATH = 427 + SYS_AUDIT_SESSION_SELF = 428 + SYS_AUDIT_SESSION_JOIN = 429 + SYS_PID_SUSPEND = 430 + SYS_PID_RESUME = 431 + SYS_FILEPORT_MAKEPORT = 432 + SYS_FILEPORT_MAKEFD = 433 + SYS_MAXSYSCALL = 434 ) diff --git a/src/pkg/syscall/zsysnum_windows_amd64.go b/src/pkg/syscall/zsysnum_windows_amd64.go new file mode 100644 index 000000000..36bf065d1 --- /dev/null +++ b/src/pkg/syscall/zsysnum_windows_amd64.go @@ -0,0 +1,3 @@ +// nothing to see here + +package syscall diff --git a/src/pkg/syscall/ztypes_linux_386.go b/src/pkg/syscall/ztypes_linux_386.go index 65c8b87db..252fbff74 100644 --- a/src/pkg/syscall/ztypes_linux_386.go +++ b/src/pkg/syscall/ztypes_linux_386.go @@ -100,6 +100,8 @@ const ( SizeofIfAddrmsg = 0x8 SizeofRtmsg = 0xc SizeofRtNexthop = 0x8 + SizeofSockFilter = 0x8 + SizeofSockFprog = 0x8 SizeofInotifyEvent = 0x10 ) @@ -400,6 +402,19 @@ type RtNexthop struct { Ifindex int32 } +type SockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} + +type SockFprog struct { + Len uint16 + Pad_godefs_0 [2]byte + Filter *SockFilter +} + type InotifyEvent struct { Wd int32 Mask uint32 diff --git a/src/pkg/syscall/ztypes_linux_amd64.go b/src/pkg/syscall/ztypes_linux_amd64.go index e26b6bfd2..520ba963a 100644 --- a/src/pkg/syscall/ztypes_linux_amd64.go +++ b/src/pkg/syscall/ztypes_linux_amd64.go @@ -100,6 +100,8 @@ const ( SizeofIfAddrmsg = 0x8 SizeofRtmsg = 0xc SizeofRtNexthop = 0x8 + SizeofSockFilter = 0x8 + SizeofSockFprog = 0x10 SizeofInotifyEvent = 0x10 ) @@ -402,6 +404,19 @@ type RtNexthop struct { Ifindex int32 } +type SockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} + +type SockFprog struct { + Len uint16 + Pad_godefs_0 [6]byte + Filter *SockFilter +} + type InotifyEvent struct { Wd int32 Mask uint32 diff --git a/src/pkg/syscall/ztypes_linux_arm.go b/src/pkg/syscall/ztypes_linux_arm.go index ebd5379cb..2421df081 100644 --- a/src/pkg/syscall/ztypes_linux_arm.go +++ b/src/pkg/syscall/ztypes_linux_arm.go @@ -105,6 +105,8 @@ const ( SizeofIfAddrmsg = 0x8 SizeofRtmsg = 0xc SizeofRtNexthop = 0x8 + SizeofSockFilter = 0x8 + SizeofSockFprog = 0x8 SizeofInotifyEvent = 0x10 ) @@ -407,6 +409,19 @@ type RtNexthop struct { Ifindex int32 } +type SockFilter struct { + Code uint16 + Jt uint8 + Jf uint8 + K uint32 +} + +type SockFprog struct { + Len uint16 + Pad_godefs_0 [2]byte + Filter *SockFilter +} + type InotifyEvent struct { Wd int32 Mask uint32 diff --git a/src/pkg/syscall/ztypes_plan9_386.go b/src/pkg/syscall/ztypes_plan9_386.go index 8f823ba65..3e3a8d1f3 100644 --- a/src/pkg/syscall/ztypes_plan9_386.go +++ b/src/pkg/syscall/ztypes_plan9_386.go @@ -9,6 +9,7 @@ const ( O_RDONLY = 0 O_WRONLY = 0x1 O_RDWR = 0x2 + O_TRUNC = 0x10 O_CLOEXEC = 0x20 O_EXCL = 0x1000 STATMAX = 0xffff diff --git a/src/pkg/syscall/ztypes_windows.go b/src/pkg/syscall/ztypes_windows.go new file mode 100644 index 000000000..1a264a405 --- /dev/null +++ b/src/pkg/syscall/ztypes_windows.go @@ -0,0 +1,656 @@ +package syscall + +// TODO(brainman): autogenerate types in ztypes_windows_386.go + +//import "unsafe" + +// Constants +const ( + sizeofPtr = 0x4 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x4 + sizeofLongLong = 0x8 + PathMax = 0x1000 + SizeofLinger = 0x8 + SizeofMsghdr = 0x1c + SizeofCmsghdr = 0xc +) + +const ( + // Windows errors. + ERROR_FILE_NOT_FOUND = 2 + ERROR_PATH_NOT_FOUND = 3 + ERROR_NO_MORE_FILES = 18 + ERROR_BROKEN_PIPE = 109 + ERROR_BUFFER_OVERFLOW = 111 + ERROR_INSUFFICIENT_BUFFER = 122 + ERROR_MOD_NOT_FOUND = 126 + ERROR_PROC_NOT_FOUND = 127 + ERROR_ENVVAR_NOT_FOUND = 203 + ERROR_DIRECTORY = 267 + ERROR_OPERATION_ABORTED = 995 + ERROR_IO_PENDING = 997 +) + +const ( + // Invented values to support what package os expects. + O_RDONLY = 0x00000 + O_WRONLY = 0x00001 + O_RDWR = 0x00002 + O_CREAT = 0x00040 + O_EXCL = 0x00080 + O_NOCTTY = 0x00100 + O_TRUNC = 0x00200 + O_NONBLOCK = 0x00800 + O_APPEND = 0x00400 + O_SYNC = 0x01000 + O_ASYNC = 0x02000 + O_CLOEXEC = 0x80000 +) + +const ( + // More invented values for signals + SIGHUP = 0x1 + SIGINT = 0x2 + SIGQUIT = 0x3 + SIGILL = 0x4 + SIGTRAP = 0x5 + SIGABRT = 0x6 + SIGBUS = 0x7 + SIGFPE = 0x8 + SIGKILL = 0x9 + SIGSEGV = 0xb + SIGPIPE = 0xd + SIGALRM = 0xe + SIGTERM = 0xf +) + +const ( + GENERIC_READ = 0x80000000 + GENERIC_WRITE = 0x40000000 + GENERIC_EXECUTE = 0x20000000 + GENERIC_ALL = 0x10000000 + + FILE_APPEND_DATA = 0x00000004 + FILE_WRITE_ATTRIBUTES = 0x00000100 + + FILE_SHARE_READ = 0x00000001 + FILE_SHARE_WRITE = 0x00000002 + FILE_SHARE_DELETE = 0x00000004 + FILE_ATTRIBUTE_READONLY = 0x00000001 + FILE_ATTRIBUTE_HIDDEN = 0x00000002 + FILE_ATTRIBUTE_SYSTEM = 0x00000004 + FILE_ATTRIBUTE_DIRECTORY = 0x00000010 + FILE_ATTRIBUTE_ARCHIVE = 0x00000020 + FILE_ATTRIBUTE_NORMAL = 0x00000080 + + INVALID_FILE_ATTRIBUTES = 0xffffffff + + CREATE_NEW = 1 + CREATE_ALWAYS = 2 + OPEN_EXISTING = 3 + OPEN_ALWAYS = 4 + TRUNCATE_EXISTING = 5 + + HANDLE_FLAG_INHERIT = 0x00000001 + STARTF_USESTDHANDLES = 0x00000100 + STARTF_USESHOWWINDOW = 0x00000001 + DUPLICATE_CLOSE_SOURCE = 0x00000001 + DUPLICATE_SAME_ACCESS = 0x00000002 + + STD_INPUT_HANDLE = -10 + STD_OUTPUT_HANDLE = -11 + STD_ERROR_HANDLE = -12 + + FILE_BEGIN = 0 + FILE_CURRENT = 1 + FILE_END = 2 + + FORMAT_MESSAGE_ALLOCATE_BUFFER = 256 + FORMAT_MESSAGE_IGNORE_INSERTS = 512 + FORMAT_MESSAGE_FROM_STRING = 1024 + FORMAT_MESSAGE_FROM_HMODULE = 2048 + FORMAT_MESSAGE_FROM_SYSTEM = 4096 + FORMAT_MESSAGE_ARGUMENT_ARRAY = 8192 + FORMAT_MESSAGE_MAX_WIDTH_MASK = 255 + + MAX_PATH = 260 + MAX_LONG_PATH = 32768 + + MAX_COMPUTERNAME_LENGTH = 15 + + TIME_ZONE_ID_UNKNOWN = 0 + TIME_ZONE_ID_STANDARD = 1 + + TIME_ZONE_ID_DAYLIGHT = 2 + IGNORE = 0 + INFINITE = 0xffffffff + + WAIT_TIMEOUT = 258 + WAIT_ABANDONED = 0x00000080 + WAIT_OBJECT_0 = 0x00000000 + WAIT_FAILED = 0xFFFFFFFF + + CREATE_UNICODE_ENVIRONMENT = 0x00000400 + + STANDARD_RIGHTS_READ = 0x00020000 + PROCESS_QUERY_INFORMATION = 0x00000400 + SYNCHRONIZE = 0x00100000 + + PAGE_READONLY = 0x02 + PAGE_READWRITE = 0x04 + PAGE_WRITECOPY = 0x08 + PAGE_EXECUTE_READ = 0x20 + PAGE_EXECUTE_READWRITE = 0x40 + PAGE_EXECUTE_WRITECOPY = 0x80 + + FILE_MAP_COPY = 0x01 + FILE_MAP_WRITE = 0x02 + FILE_MAP_READ = 0x04 + FILE_MAP_EXECUTE = 0x20 +) + +const ( + // wincrypt.h + PROV_RSA_FULL = 1 + PROV_RSA_SIG = 2 + PROV_DSS = 3 + PROV_FORTEZZA = 4 + PROV_MS_EXCHANGE = 5 + PROV_SSL = 6 + PROV_RSA_SCHANNEL = 12 + PROV_DSS_DH = 13 + PROV_EC_ECDSA_SIG = 14 + PROV_EC_ECNRA_SIG = 15 + PROV_EC_ECDSA_FULL = 16 + PROV_EC_ECNRA_FULL = 17 + PROV_DH_SCHANNEL = 18 + PROV_SPYRUS_LYNKS = 20 + PROV_RNG = 21 + PROV_INTEL_SEC = 22 + PROV_REPLACE_OWF = 23 + PROV_RSA_AES = 24 + CRYPT_VERIFYCONTEXT = 0xF0000000 + CRYPT_NEWKEYSET = 0x00000008 + CRYPT_DELETEKEYSET = 0x00000010 + CRYPT_MACHINE_KEYSET = 0x00000020 + CRYPT_SILENT = 0x00000040 + CRYPT_DEFAULT_CONTAINER_OPTIONAL = 0x00000080 +) + +// Types + +type _C_short int16 + +type _C_int int32 + +type _C_long int32 + +type _C_long_long int64 + +// Invented values to support what package os expects. +type Timeval struct { + Sec int32 + Usec int32 +} + +func (tv *Timeval) Nanoseconds() int64 { + return (int64(tv.Sec)*1e6 + int64(tv.Usec)) * 1e3 +} + +func NsecToTimeval(nsec int64) (tv Timeval) { + tv.Sec = int32(nsec / 1e9) + tv.Usec = int32(nsec % 1e9 / 1e3) + return +} + +type SecurityAttributes struct { + Length uint32 + SecurityDescriptor uintptr + InheritHandle uint32 +} + +type Overlapped struct { + Internal uint32 + InternalHigh uint32 + Offset uint32 + OffsetHigh uint32 + HEvent Handle +} + +type Filetime struct { + LowDateTime uint32 + HighDateTime uint32 +} + +func (ft *Filetime) Nanoseconds() int64 { + // 100-nanosecond intervals since January 1, 1601 + nsec := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime) + // change starting time to the Epoch (00:00:00 UTC, January 1, 1970) + nsec -= 116444736000000000 + // convert into nanoseconds + nsec *= 100 + return nsec +} + +func NsecToFiletime(nsec int64) (ft Filetime) { + // convert into 100-nanosecond + nsec /= 100 + // change starting time to January 1, 1601 + nsec += 116444736000000000 + // split into high / low + ft.LowDateTime = uint32(nsec & 0xffffffff) + ft.HighDateTime = uint32(nsec >> 32 & 0xffffffff) + return ft +} + +type Win32finddata struct { + FileAttributes uint32 + CreationTime Filetime + LastAccessTime Filetime + LastWriteTime Filetime + FileSizeHigh uint32 + FileSizeLow uint32 + Reserved0 uint32 + Reserved1 uint32 + FileName [MAX_PATH - 1]uint16 + AlternateFileName [13]uint16 +} + +type ByHandleFileInformation struct { + FileAttributes uint32 + CreationTime Filetime + LastAccessTime Filetime + LastWriteTime Filetime + VolumeSerialNumber uint32 + FileSizeHigh uint32 + FileSizeLow uint32 + NumberOfLinks uint32 + FileIndexHigh uint32 + FileIndexLow uint32 +} + +// ShowWindow constants +const ( + // winuser.h + SW_HIDE = 0 + SW_NORMAL = 1 + SW_SHOWNORMAL = 1 + SW_SHOWMINIMIZED = 2 + SW_SHOWMAXIMIZED = 3 + SW_MAXIMIZE = 3 + SW_SHOWNOACTIVATE = 4 + SW_SHOW = 5 + SW_MINIMIZE = 6 + SW_SHOWMINNOACTIVE = 7 + SW_SHOWNA = 8 + SW_RESTORE = 9 + SW_SHOWDEFAULT = 10 + SW_FORCEMINIMIZE = 11 +) + +type StartupInfo struct { + Cb uint32 + _ *uint16 + Desktop *uint16 + Title *uint16 + X uint32 + Y uint32 + XSize uint32 + YSize uint32 + XCountChars uint32 + YCountChars uint32 + FillAttribute uint32 + Flags uint32 + ShowWindow uint16 + _ uint16 + _ *byte + StdInput Handle + StdOutput Handle + StdErr Handle +} + +type ProcessInformation struct { + Process Handle + Thread Handle + ProcessId uint32 + ThreadId uint32 +} + +// Invented values to support what package os expects. +type Stat_t struct { + Windata Win32finddata + Mode uint32 +} + +type Systemtime struct { + Year uint16 + Month uint16 + DayOfWeek uint16 + Day uint16 + Hour uint16 + Minute uint16 + Second uint16 + Milliseconds uint16 +} + +type Timezoneinformation struct { + Bias int32 + StandardName [32]uint16 + StandardDate Systemtime + StandardBias int32 + DaylightName [32]uint16 + DaylightDate Systemtime + DaylightBias int32 +} + +// Socket related. + +const ( + AF_UNSPEC = 0 + AF_UNIX = 1 + AF_INET = 2 + AF_INET6 = 23 + AF_NETBIOS = 17 + + SOCK_STREAM = 1 + SOCK_DGRAM = 2 + SOCK_RAW = 3 + SOCK_SEQPACKET = 5 + + IPPROTO_IP = 0 + IPPROTO_TCP = 6 + IPPROTO_UDP = 17 + + SOL_SOCKET = 0xffff + SO_REUSEADDR = 4 + SO_KEEPALIVE = 8 + SO_DONTROUTE = 16 + SO_BROADCAST = 32 + SO_LINGER = 128 + SO_RCVBUF = 0x1002 + SO_SNDBUF = 0x1001 + SO_UPDATE_ACCEPT_CONTEXT = 0x700b + + IPPROTO_IPV6 = 0x29 + IPV6_V6ONLY = 0x1b + + SOMAXCONN = 5 + + TCP_NODELAY = 1 + + SHUT_RD = 0 + SHUT_WR = 1 + SHUT_RDWR = 2 + + WSADESCRIPTION_LEN = 256 + WSASYS_STATUS_LEN = 128 +) + +type WSAData struct { + Version uint16 + HighVersion uint16 + Description [WSADESCRIPTION_LEN + 1]byte + SystemStatus [WSASYS_STATUS_LEN + 1]byte + MaxSockets uint16 + MaxUdpDg uint16 + VendorInfo *byte +} + +type WSABuf struct { + Len uint32 + Buf *byte +} + +// TODO(brainman): fix all needed for os + +const ( + PROT_READ = 0x1 + PROT_WRITE = 0x2 + MAP_SHARED = 0x1 + SYS_FORK = 0 + SYS_PTRACE = 0 + SYS_CHDIR = 0 + SYS_DUP2 = 0 + SYS_FCNTL = 0 + SYS_EXECVE = 0 + F_GETFD = 0x1 + F_SETFD = 0x2 + F_GETFL = 0x3 + F_SETFL = 0x4 + FD_CLOEXEC = 0 + S_IFMT = 0x1f000 + S_IFIFO = 0x1000 + S_IFCHR = 0x2000 + S_IFDIR = 0x4000 + S_IFBLK = 0x6000 + S_IFREG = 0x8000 + S_IFLNK = 0xa000 + S_IFSOCK = 0xc000 + S_ISUID = 0x800 + S_ISGID = 0x400 + S_ISVTX = 0x200 + S_IRUSR = 0x100 + S_IWRITE = 0x80 + S_IWUSR = 0x80 + S_IXUSR = 0x40 +) + +const ( + FILE_TYPE_CHAR = 0x0002 + FILE_TYPE_DISK = 0x0001 + FILE_TYPE_PIPE = 0x0003 + FILE_TYPE_REMOTE = 0x8000 + FILE_TYPE_UNKNOWN = 0x0000 +) + +type Hostent struct { + Name *byte + Aliases **byte + AddrType uint16 + Length uint16 + AddrList **byte +} + +type Servent struct { + Name *byte + Aliases **byte + Port uint16 + Proto *byte +} + +const ( + DNS_TYPE_A = 0x0001 + DNS_TYPE_NS = 0x0002 + DNS_TYPE_MD = 0x0003 + DNS_TYPE_MF = 0x0004 + DNS_TYPE_CNAME = 0x0005 + DNS_TYPE_SOA = 0x0006 + DNS_TYPE_MB = 0x0007 + DNS_TYPE_MG = 0x0008 + DNS_TYPE_MR = 0x0009 + DNS_TYPE_NULL = 0x000a + DNS_TYPE_WKS = 0x000b + DNS_TYPE_PTR = 0x000c + DNS_TYPE_HINFO = 0x000d + DNS_TYPE_MINFO = 0x000e + DNS_TYPE_MX = 0x000f + DNS_TYPE_TEXT = 0x0010 + DNS_TYPE_RP = 0x0011 + DNS_TYPE_AFSDB = 0x0012 + DNS_TYPE_X25 = 0x0013 + DNS_TYPE_ISDN = 0x0014 + DNS_TYPE_RT = 0x0015 + DNS_TYPE_NSAP = 0x0016 + DNS_TYPE_NSAPPTR = 0x0017 + DNS_TYPE_SIG = 0x0018 + DNS_TYPE_KEY = 0x0019 + DNS_TYPE_PX = 0x001a + DNS_TYPE_GPOS = 0x001b + DNS_TYPE_AAAA = 0x001c + DNS_TYPE_LOC = 0x001d + DNS_TYPE_NXT = 0x001e + DNS_TYPE_EID = 0x001f + DNS_TYPE_NIMLOC = 0x0020 + DNS_TYPE_SRV = 0x0021 + DNS_TYPE_ATMA = 0x0022 + DNS_TYPE_NAPTR = 0x0023 + DNS_TYPE_KX = 0x0024 + DNS_TYPE_CERT = 0x0025 + DNS_TYPE_A6 = 0x0026 + DNS_TYPE_DNAME = 0x0027 + DNS_TYPE_SINK = 0x0028 + DNS_TYPE_OPT = 0x0029 + DNS_TYPE_DS = 0x002B + DNS_TYPE_RRSIG = 0x002E + DNS_TYPE_NSEC = 0x002F + DNS_TYPE_DNSKEY = 0x0030 + DNS_TYPE_DHCID = 0x0031 + DNS_TYPE_UINFO = 0x0064 + DNS_TYPE_UID = 0x0065 + DNS_TYPE_GID = 0x0066 + DNS_TYPE_UNSPEC = 0x0067 + DNS_TYPE_ADDRS = 0x00f8 + DNS_TYPE_TKEY = 0x00f9 + DNS_TYPE_TSIG = 0x00fa + DNS_TYPE_IXFR = 0x00fb + DNS_TYPE_AXFR = 0x00fc + DNS_TYPE_MAILB = 0x00fd + DNS_TYPE_MAILA = 0x00fe + DNS_TYPE_ALL = 0x00ff + DNS_TYPE_ANY = 0x00ff + DNS_TYPE_WINS = 0xff01 + DNS_TYPE_WINSR = 0xff02 + DNS_TYPE_NBSTAT = 0xff01 +) + +type DNSSRVData struct { + Target *uint16 + Priority uint16 + Weight uint16 + Port uint16 + Pad uint16 +} + +type DNSPTRData struct { + Host *uint16 +} + +type DNSRecord struct { + Next *DNSRecord + Name *uint16 + Type uint16 + Length uint16 + Dw uint32 + Ttl uint32 + Reserved uint32 + Data [40]byte +} + +const ( + TF_DISCONNECT = 1 + TF_REUSE_SOCKET = 2 + TF_WRITE_BEHIND = 4 + TF_USE_DEFAULT_WORKER = 0 + TF_USE_SYSTEM_THREAD = 16 + TF_USE_KERNEL_APC = 32 +) + +type TransmitFileBuffers struct { + Head uintptr + HeadLength uint32 + Tail uintptr + TailLength uint32 +} + +const ( + IFF_UP = 1 + IFF_BROADCAST = 2 + IFF_LOOPBACK = 4 + IFF_POINTTOPOINT = 8 + IFF_MULTICAST = 16 +) + +const SIO_GET_INTERFACE_LIST = 0x4004747F + +// TODO(mattn): SockaddrGen is union of sockaddr/sockaddr_in/sockaddr_in6_old. +// will be fixed to change variable type as suitable. + +type SockaddrGen [24]byte + +type InterfaceInfo struct { + Flags uint32 + Address SockaddrGen + BroadcastAddress SockaddrGen + Netmask SockaddrGen +} + +type IpAddressString struct { + String [16]byte +} + +type IpMaskString IpAddressString + +type IpAddrString struct { + Next *IpAddrString + IpAddress IpAddressString + IpMask IpMaskString + Context uint32 +} + +const MAX_ADAPTER_NAME_LENGTH = 256 +const MAX_ADAPTER_DESCRIPTION_LENGTH = 128 +const MAX_ADAPTER_ADDRESS_LENGTH = 8 + +type IpAdapterInfo struct { + Next *IpAdapterInfo + ComboIndex uint32 + AdapterName [MAX_ADAPTER_NAME_LENGTH + 4]byte + Description [MAX_ADAPTER_DESCRIPTION_LENGTH + 4]byte + AddressLength uint32 + Address [MAX_ADAPTER_ADDRESS_LENGTH]byte + Index uint32 + Type uint32 + DhcpEnabled uint32 + CurrentIpAddress *IpAddrString + IpAddressList IpAddrString + GatewayList IpAddrString + DhcpServer IpAddrString + HaveWins bool + PrimaryWinsServer IpAddrString + SecondaryWinsServer IpAddrString + LeaseObtained int64 + LeaseExpires int64 +} + +const MAXLEN_PHYSADDR = 8 +const MAX_INTERFACE_NAME_LEN = 256 +const MAXLEN_IFDESCR = 256 + +type MibIfRow struct { + Name [MAX_INTERFACE_NAME_LEN]uint16 + Index uint32 + Type uint32 + Mtu uint32 + Speed uint32 + PhysAddrLen uint32 + PhysAddr [MAXLEN_PHYSADDR]byte + AdminStatus uint32 + OperStatus uint32 + LastChange uint32 + InOctets uint32 + InUcastPkts uint32 + InNUcastPkts uint32 + InDiscards uint32 + InErrors uint32 + InUnknownProtos uint32 + OutOctets uint32 + OutUcastPkts uint32 + OutNUcastPkts uint32 + OutDiscards uint32 + OutErrors uint32 + OutQLen uint32 + DescrLen uint32 + Descr [MAXLEN_IFDESCR]byte +} diff --git a/src/pkg/syscall/ztypes_windows_386.go b/src/pkg/syscall/ztypes_windows_386.go index b04fea576..d1008bd03 100644 --- a/src/pkg/syscall/ztypes_windows_386.go +++ b/src/pkg/syscall/ztypes_windows_386.go @@ -1,547 +1,5 @@ -package syscall - -// TODO(brainman): autogenerate types in ztypes_windows_386.go - -//import "unsafe" - -// Constants -const ( - sizeofPtr = 0x4 - sizeofShort = 0x2 - sizeofInt = 0x4 - sizeofLong = 0x4 - sizeofLongLong = 0x8 - PathMax = 0x1000 - SizeofLinger = 0x8 - SizeofMsghdr = 0x1c - SizeofCmsghdr = 0xc -) - -const ( - // Windows errors. - ERROR_FILE_NOT_FOUND = 2 - ERROR_PATH_NOT_FOUND = 3 - ERROR_NO_MORE_FILES = 18 - ERROR_BROKEN_PIPE = 109 - ERROR_INSUFFICIENT_BUFFER = 122 - ERROR_MOD_NOT_FOUND = 126 - ERROR_PROC_NOT_FOUND = 127 - ERROR_ENVVAR_NOT_FOUND = 203 - ERROR_DIRECTORY = 267 - ERROR_OPERATION_ABORTED = 995 - ERROR_IO_PENDING = 997 -) - -const ( - // Invented values to support what package os expects. - O_RDONLY = 0x00000 - O_WRONLY = 0x00001 - O_RDWR = 0x00002 - O_CREAT = 0x00040 - O_EXCL = 0x00080 - O_NOCTTY = 0x00100 - O_TRUNC = 0x00200 - O_NONBLOCK = 0x00800 - O_APPEND = 0x00400 - O_SYNC = 0x01000 - O_ASYNC = 0x02000 - O_CLOEXEC = 0x80000 -) - -const ( - // More invented values for signals - SIGHUP = 0x1 - SIGINT = 0x2 - SIGQUIT = 0x3 - SIGILL = 0x4 - SIGTRAP = 0x5 - SIGABRT = 0x6 - SIGBUS = 0x7 - SIGFPE = 0x8 - SIGKILL = 0x9 - SIGSEGV = 0xb - SIGPIPE = 0xd - SIGALRM = 0xe - SIGTERM = 0xf -) - -const ( - GENERIC_READ = 0x80000000 - GENERIC_WRITE = 0x40000000 - GENERIC_EXECUTE = 0x20000000 - GENERIC_ALL = 0x10000000 - - FILE_APPEND_DATA = 0x00000004 - FILE_WRITE_ATTRIBUTES = 0x00000100 - - FILE_SHARE_READ = 0x00000001 - FILE_SHARE_WRITE = 0x00000002 - FILE_SHARE_DELETE = 0x00000004 - FILE_ATTRIBUTE_READONLY = 0x00000001 - FILE_ATTRIBUTE_HIDDEN = 0x00000002 - FILE_ATTRIBUTE_SYSTEM = 0x00000004 - FILE_ATTRIBUTE_DIRECTORY = 0x00000010 - FILE_ATTRIBUTE_ARCHIVE = 0x00000020 - FILE_ATTRIBUTE_NORMAL = 0x00000080 - - INVALID_FILE_ATTRIBUTES = 0xffffffff - - CREATE_NEW = 1 - CREATE_ALWAYS = 2 - OPEN_EXISTING = 3 - OPEN_ALWAYS = 4 - TRUNCATE_EXISTING = 5 - - HANDLE_FLAG_INHERIT = 0x00000001 - STARTF_USESTDHANDLES = 0x00000100 - STARTF_USESHOWWINDOW = 0x00000001 - DUPLICATE_CLOSE_SOURCE = 0x00000001 - DUPLICATE_SAME_ACCESS = 0x00000002 - - STD_INPUT_HANDLE = -10 - STD_OUTPUT_HANDLE = -11 - STD_ERROR_HANDLE = -12 - - FILE_BEGIN = 0 - FILE_CURRENT = 1 - FILE_END = 2 - - FORMAT_MESSAGE_ALLOCATE_BUFFER = 256 - FORMAT_MESSAGE_IGNORE_INSERTS = 512 - FORMAT_MESSAGE_FROM_STRING = 1024 - FORMAT_MESSAGE_FROM_HMODULE = 2048 - FORMAT_MESSAGE_FROM_SYSTEM = 4096 - FORMAT_MESSAGE_ARGUMENT_ARRAY = 8192 - FORMAT_MESSAGE_MAX_WIDTH_MASK = 255 - - MAX_PATH = 260 - MAX_LONG_PATH = 32768 - - MAX_COMPUTERNAME_LENGTH = 15 - - TIME_ZONE_ID_UNKNOWN = 0 - TIME_ZONE_ID_STANDARD = 1 - - TIME_ZONE_ID_DAYLIGHT = 2 - IGNORE = 0 - INFINITE = 0xffffffff - - WAIT_TIMEOUT = 258 - WAIT_ABANDONED = 0x00000080 - WAIT_OBJECT_0 = 0x00000000 - WAIT_FAILED = 0xFFFFFFFF - - CREATE_UNICODE_ENVIRONMENT = 0x00000400 - - STANDARD_RIGHTS_READ = 0x00020000 - PROCESS_QUERY_INFORMATION = 0x00000400 - SYNCHRONIZE = 0x00100000 - - PAGE_READONLY = 0x02 - PAGE_READWRITE = 0x04 - PAGE_WRITECOPY = 0x08 - PAGE_EXECUTE_READ = 0x20 - PAGE_EXECUTE_READWRITE = 0x40 - PAGE_EXECUTE_WRITECOPY = 0x80 - - FILE_MAP_COPY = 0x01 - FILE_MAP_WRITE = 0x02 - FILE_MAP_READ = 0x04 - FILE_MAP_EXECUTE = 0x20 -) - -const ( - // wincrypt.h - PROV_RSA_FULL = 1 - PROV_RSA_SIG = 2 - PROV_DSS = 3 - PROV_FORTEZZA = 4 - PROV_MS_EXCHANGE = 5 - PROV_SSL = 6 - PROV_RSA_SCHANNEL = 12 - PROV_DSS_DH = 13 - PROV_EC_ECDSA_SIG = 14 - PROV_EC_ECNRA_SIG = 15 - PROV_EC_ECDSA_FULL = 16 - PROV_EC_ECNRA_FULL = 17 - PROV_DH_SCHANNEL = 18 - PROV_SPYRUS_LYNKS = 20 - PROV_RNG = 21 - PROV_INTEL_SEC = 22 - PROV_REPLACE_OWF = 23 - PROV_RSA_AES = 24 - CRYPT_VERIFYCONTEXT = 0xF0000000 - CRYPT_NEWKEYSET = 0x00000008 - CRYPT_DELETEKEYSET = 0x00000010 - CRYPT_MACHINE_KEYSET = 0x00000020 - CRYPT_SILENT = 0x00000040 - CRYPT_DEFAULT_CONTAINER_OPTIONAL = 0x00000080 -) - -// Types - -type _C_short int16 - -type _C_int int32 - -type _C_long int32 - -type _C_long_long int64 - -// Invented values to support what package os expects. -type Timeval struct { - Sec int32 - Usec int32 -} - -func (tv *Timeval) Nanoseconds() int64 { - return (int64(tv.Sec)*1e6 + int64(tv.Usec)) * 1e3 -} - -func NsecToTimeval(nsec int64) (tv Timeval) { - tv.Sec = int32(nsec / 1e9) - tv.Usec = int32(nsec % 1e9 / 1e3) - return -} +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. -type SecurityAttributes struct { - Length uint32 - SecurityDescriptor uintptr - InheritHandle uint32 -} - -type Overlapped struct { - Internal uint32 - InternalHigh uint32 - Offset uint32 - OffsetHigh uint32 - HEvent int32 -} - -type Filetime struct { - LowDateTime uint32 - HighDateTime uint32 -} - -func (ft *Filetime) Nanoseconds() int64 { - // 100-nanosecond intervals since January 1, 1601 - nsec := int64(ft.HighDateTime)<<32 + int64(ft.LowDateTime) - // change starting time to the Epoch (00:00:00 UTC, January 1, 1970) - nsec -= 116444736000000000 - // convert into nanoseconds - nsec *= 100 - return nsec -} - -func NsecToFiletime(nsec int64) (ft Filetime) { - // convert into 100-nanosecond - nsec /= 100 - // change starting time to January 1, 1601 - nsec += 116444736000000000 - // split into high / low - ft.LowDateTime = uint32(nsec & 0xffffffff) - ft.HighDateTime = uint32(nsec >> 32 & 0xffffffff) - return ft -} - -type Win32finddata struct { - FileAttributes uint32 - CreationTime Filetime - LastAccessTime Filetime - LastWriteTime Filetime - FileSizeHigh uint32 - FileSizeLow uint32 - Reserved0 uint32 - Reserved1 uint32 - FileName [MAX_PATH - 1]uint16 - AlternateFileName [13]uint16 -} - -type ByHandleFileInformation struct { - FileAttributes uint32 - CreationTime Filetime - LastAccessTime Filetime - LastWriteTime Filetime - VolumeSerialNumber uint32 - FileSizeHigh uint32 - FileSizeLow uint32 - NumberOfLinks uint32 - FileIndexHigh uint32 - FileIndexLow uint32 -} - -// ShowWindow constants -const ( - // winuser.h - SW_HIDE = 0 - SW_NORMAL = 1 - SW_SHOWNORMAL = 1 - SW_SHOWMINIMIZED = 2 - SW_SHOWMAXIMIZED = 3 - SW_MAXIMIZE = 3 - SW_SHOWNOACTIVATE = 4 - SW_SHOW = 5 - SW_MINIMIZE = 6 - SW_SHOWMINNOACTIVE = 7 - SW_SHOWNA = 8 - SW_RESTORE = 9 - SW_SHOWDEFAULT = 10 - SW_FORCEMINIMIZE = 11 -) - -type StartupInfo struct { - Cb uint32 - _ *uint16 - Desktop *uint16 - Title *uint16 - X uint32 - Y uint32 - XSize uint32 - YSize uint32 - XCountChars uint32 - YCountChars uint32 - FillAttribute uint32 - Flags uint32 - ShowWindow uint16 - _ uint16 - _ *byte - StdInput int32 - StdOutput int32 - StdErr int32 -} - -type ProcessInformation struct { - Process int32 - Thread int32 - ProcessId uint32 - ThreadId uint32 -} - -// Invented values to support what package os expects. -type Stat_t struct { - Windata Win32finddata - Mode uint32 -} - -type Systemtime struct { - Year uint16 - Month uint16 - DayOfWeek uint16 - Day uint16 - Hour uint16 - Minute uint16 - Second uint16 - Milliseconds uint16 -} - -type Timezoneinformation struct { - Bias int32 - StandardName [32]uint16 - StandardDate Systemtime - StandardBias int32 - DaylightName [32]uint16 - DaylightDate Systemtime - DaylightBias int32 -} - -// Socket related. - -const ( - AF_UNIX = 1 - AF_INET = 2 - AF_INET6 = 23 - AF_NETBIOS = 17 - - SOCK_STREAM = 1 - SOCK_DGRAM = 2 - SOCK_RAW = 3 - SOCK_SEQPACKET = 5 - - IPPROTO_IP = 0 - IPPROTO_TCP = 6 - IPPROTO_UDP = 17 - - SOL_SOCKET = 0xffff - SO_REUSEADDR = 4 - SO_KEEPALIVE = 8 - SO_DONTROUTE = 16 - SO_BROADCAST = 32 - SO_LINGER = 128 - SO_RCVBUF = 0x1002 - SO_SNDBUF = 0x1001 - SO_UPDATE_ACCEPT_CONTEXT = 0x700b - - IPPROTO_IPV6 = 0x29 - IPV6_V6ONLY = 0x1b - - SOMAXCONN = 5 - - TCP_NODELAY = 1 - - SHUT_RD = 0 - SHUT_WR = 1 - SHUT_RDWR = 2 - - WSADESCRIPTION_LEN = 256 - WSASYS_STATUS_LEN = 128 -) - -type WSAData struct { - Version uint16 - HighVersion uint16 - Description [WSADESCRIPTION_LEN + 1]byte - SystemStatus [WSASYS_STATUS_LEN + 1]byte - MaxSockets uint16 - MaxUdpDg uint16 - VendorInfo *byte -} - -type WSABuf struct { - Len uint32 - Buf *byte -} - -// TODO(brainman): fix all needed for os - -const ( - PROT_READ = 0x1 - PROT_WRITE = 0x2 - MAP_SHARED = 0x1 - SYS_FORK = 0 - SYS_PTRACE = 0 - SYS_CHDIR = 0 - SYS_DUP2 = 0 - SYS_FCNTL = 0 - SYS_EXECVE = 0 - F_GETFD = 0x1 - F_SETFD = 0x2 - F_GETFL = 0x3 - F_SETFL = 0x4 - FD_CLOEXEC = 0 - S_IFMT = 0x1f000 - S_IFIFO = 0x1000 - S_IFCHR = 0x2000 - S_IFDIR = 0x4000 - S_IFBLK = 0x6000 - S_IFREG = 0x8000 - S_IFLNK = 0xa000 - S_IFSOCK = 0xc000 - S_ISUID = 0x800 - S_ISGID = 0x400 - S_ISVTX = 0x200 - S_IRUSR = 0x100 - S_IWRITE = 0x80 - S_IWUSR = 0x80 - S_IXUSR = 0x40 -) - -const ( - FILE_TYPE_CHAR = 0x0002 - FILE_TYPE_DISK = 0x0001 - FILE_TYPE_PIPE = 0x0003 - FILE_TYPE_REMOTE = 0x8000 - FILE_TYPE_UNKNOWN = 0x0000 -) - -type Hostent struct { - Name *byte - Aliases **byte - AddrType uint16 - Length uint16 - AddrList **byte -} - -type Servent struct { - Name *byte - Aliases **byte - Port uint16 - Proto *byte -} - -const ( - DNS_TYPE_A = 0x0001 - DNS_TYPE_NS = 0x0002 - DNS_TYPE_MD = 0x0003 - DNS_TYPE_MF = 0x0004 - DNS_TYPE_CNAME = 0x0005 - DNS_TYPE_SOA = 0x0006 - DNS_TYPE_MB = 0x0007 - DNS_TYPE_MG = 0x0008 - DNS_TYPE_MR = 0x0009 - DNS_TYPE_NULL = 0x000a - DNS_TYPE_WKS = 0x000b - DNS_TYPE_PTR = 0x000c - DNS_TYPE_HINFO = 0x000d - DNS_TYPE_MINFO = 0x000e - DNS_TYPE_MX = 0x000f - DNS_TYPE_TEXT = 0x0010 - DNS_TYPE_RP = 0x0011 - DNS_TYPE_AFSDB = 0x0012 - DNS_TYPE_X25 = 0x0013 - DNS_TYPE_ISDN = 0x0014 - DNS_TYPE_RT = 0x0015 - DNS_TYPE_NSAP = 0x0016 - DNS_TYPE_NSAPPTR = 0x0017 - DNS_TYPE_SIG = 0x0018 - DNS_TYPE_KEY = 0x0019 - DNS_TYPE_PX = 0x001a - DNS_TYPE_GPOS = 0x001b - DNS_TYPE_AAAA = 0x001c - DNS_TYPE_LOC = 0x001d - DNS_TYPE_NXT = 0x001e - DNS_TYPE_EID = 0x001f - DNS_TYPE_NIMLOC = 0x0020 - DNS_TYPE_SRV = 0x0021 - DNS_TYPE_ATMA = 0x0022 - DNS_TYPE_NAPTR = 0x0023 - DNS_TYPE_KX = 0x0024 - DNS_TYPE_CERT = 0x0025 - DNS_TYPE_A6 = 0x0026 - DNS_TYPE_DNAME = 0x0027 - DNS_TYPE_SINK = 0x0028 - DNS_TYPE_OPT = 0x0029 - DNS_TYPE_DS = 0x002B - DNS_TYPE_RRSIG = 0x002E - DNS_TYPE_NSEC = 0x002F - DNS_TYPE_DNSKEY = 0x0030 - DNS_TYPE_DHCID = 0x0031 - DNS_TYPE_UINFO = 0x0064 - DNS_TYPE_UID = 0x0065 - DNS_TYPE_GID = 0x0066 - DNS_TYPE_UNSPEC = 0x0067 - DNS_TYPE_ADDRS = 0x00f8 - DNS_TYPE_TKEY = 0x00f9 - DNS_TYPE_TSIG = 0x00fa - DNS_TYPE_IXFR = 0x00fb - DNS_TYPE_AXFR = 0x00fc - DNS_TYPE_MAILB = 0x00fd - DNS_TYPE_MAILA = 0x00fe - DNS_TYPE_ALL = 0x00ff - DNS_TYPE_ANY = 0x00ff - DNS_TYPE_WINS = 0xff01 - DNS_TYPE_WINSR = 0xff02 - DNS_TYPE_NBSTAT = 0xff01 -) - -type DNSSRVData struct { - Target *uint16 - Priority uint16 - Weight uint16 - Port uint16 - Pad uint16 -} - -type DNSPTRData struct { - Host *uint16 -} - -type DNSRecord struct { - Next *DNSRecord - Name *uint16 - Type uint16 - Length uint16 - Dw uint32 - Ttl uint32 - Reserved uint32 - Data [40]byte -} +package syscall diff --git a/src/pkg/syscall/ztypes_windows_amd64.go b/src/pkg/syscall/ztypes_windows_amd64.go new file mode 100644 index 000000000..d1008bd03 --- /dev/null +++ b/src/pkg/syscall/ztypes_windows_amd64.go @@ -0,0 +1,5 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package syscall diff --git a/src/pkg/syslog/syslog_unix.go b/src/pkg/syslog/syslog_unix.go index fa15e882d..b1516715b 100644 --- a/src/pkg/syslog/syslog_unix.go +++ b/src/pkg/syslog/syslog_unix.go @@ -27,5 +27,5 @@ func unixSyslog() (conn serverConn, err os.Error) { } } } - return nil, os.ErrorString("Unix syslog delivery error") + return nil, os.NewError("Unix syslog delivery error") } diff --git a/src/pkg/template/execute.go b/src/pkg/template/execute.go index 5bc7ff7e9..464b620c9 100644 --- a/src/pkg/template/execute.go +++ b/src/pkg/template/execute.go @@ -114,7 +114,7 @@ func (t *Template) findVar(st *state, s string) reflect.Value { if s == "@" { return indirectPtr(data, numStars) } - for _, elem := range strings.Split(s, ".", -1) { + for _, elem := range strings.Split(s, ".") { // Look up field; data must be a struct or map. data = t.lookup(st, data, elem) if !data.IsValid() { diff --git a/src/pkg/template/parse.go b/src/pkg/template/parse.go index b4aa5fcd2..dedf9ad8e 100644 --- a/src/pkg/template/parse.go +++ b/src/pkg/template/parse.go @@ -483,7 +483,7 @@ func extractFormatters(words []string) (formatters []string) { } } words[len(words)-1] = lastWord[0:bar] - formatters = strings.Split(lastWord[bar+1:], "|", -1) + formatters = strings.Split(lastWord[bar+1:], "|") return } diff --git a/src/pkg/testing/benchmark.go b/src/pkg/testing/benchmark.go index f8b53e63a..3b416acfa 100644 --- a/src/pkg/testing/benchmark.go +++ b/src/pkg/testing/benchmark.go @@ -13,6 +13,7 @@ import ( ) var matchBenchmarks = flag.String("test.bench", "", "regular expression to select benchmarks to run") +var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds") // An internal type but exported because it is cross-package; part of the implementation // of gotest. @@ -34,7 +35,11 @@ type B struct { // StartTimer starts timing a test. This function is called automatically // before a benchmark starts, but it can also used to resume timing after // a call to StopTimer. -func (b *B) StartTimer() { b.start = time.Nanoseconds() } +func (b *B) StartTimer() { + if b.start == 0 { + b.start = time.Nanoseconds() + } +} // StopTimer stops timing a test. This can be used to pause the timer // while performing complex initialization that you don't @@ -46,9 +51,12 @@ func (b *B) StopTimer() { b.start = 0 } -// ResetTimer stops the timer and sets the elapsed benchmark time to zero. +// ResetTimer sets the elapsed benchmark time to zero. +// It does not affect whether the timer is running. func (b *B) ResetTimer() { - b.start = 0 + if b.start > 0 { + b.start = time.Nanoseconds() + } b.ns = 0 } @@ -125,14 +133,15 @@ func (b *B) run() BenchmarkResult { // Run the benchmark for a single iteration in case it's expensive. n := 1 b.runN(n) - // Run the benchmark for at least a second. - for b.ns < 1e9 && n < 1e9 { + // Run the benchmark for at least the specified amount of time. + time := int64(*benchTime * 1e9) + for b.ns < time && n < 1e9 { last := n // Predict iterations/sec. if b.nsPerOp() == 0 { n = 1e9 } else { - n = 1e9 / int(b.nsPerOp()) + n = int(time / b.nsPerOp()) } // Run more iterations than we think we'll need for a second (1.5x). // Don't grow too fast in case we had timing errors previously. @@ -172,7 +181,18 @@ func (r BenchmarkResult) String() string { if mbs != 0 { mb = fmt.Sprintf("\t%7.2f MB/s", mbs) } - return fmt.Sprintf("%8d\t%10d ns/op%s", r.N, r.NsPerOp(), mb) + nsop := r.NsPerOp() + ns := fmt.Sprintf("%10d ns/op", nsop) + if r.N > 0 && nsop < 100 { + // The format specifiers here make sure that + // the ones digits line up for all three possible formats. + if nsop < 10 { + ns = fmt.Sprintf("%13.2f ns/op", float64(r.Ns)/float64(r.N)) + } else { + ns = fmt.Sprintf("%12.1f ns/op", float64(r.Ns)/float64(r.N)) + } + } + return fmt.Sprintf("%8d\t%s%s", r.N, ns, mb) } // An internal function but exported because it is cross-package; part of the implementation @@ -182,7 +202,6 @@ func RunBenchmarks(matchString func(pat, str string) (bool, os.Error), benchmark if len(*matchBenchmarks) == 0 { return } - procs := runtime.GOMAXPROCS(-1) for _, Benchmark := range benchmarks { matched, err := matchString(*matchBenchmarks, Benchmark.Name) if err != nil { @@ -192,14 +211,19 @@ func RunBenchmarks(matchString func(pat, str string) (bool, os.Error), benchmark if !matched { continue } - b := &B{benchmark: Benchmark} - r := b.run() - print(fmt.Sprintf("%s\t%v\n", Benchmark.Name, r)) - if p := runtime.GOMAXPROCS(-1); p != procs { - print(fmt.Sprintf("%s left GOMAXPROCS set to %d\n", Benchmark.Name, p)) - procs = p + for _, procs := range cpuList { + runtime.GOMAXPROCS(procs) + b := &B{benchmark: Benchmark} + r := b.run() + benchName := Benchmark.Name + if procs != 1 { + benchName = fmt.Sprintf("%s-%d", Benchmark.Name, procs) + } + print(fmt.Sprintf("%s\t%v\n", benchName, r)) + if p := runtime.GOMAXPROCS(-1); p != procs { + print(fmt.Sprintf("%s left GOMAXPROCS set to %d\n", benchName, p)) + } } - } } diff --git a/src/pkg/testing/iotest/reader.go b/src/pkg/testing/iotest/reader.go index e4003d744..daa6ede08 100644 --- a/src/pkg/testing/iotest/reader.go +++ b/src/pkg/testing/iotest/reader.go @@ -58,7 +58,7 @@ func (r *dataErrReader) Read(p []byte) (n int, err os.Error) { r.unread = r.data[0:n1] err = err1 } - if n > 0 { + if n > 0 || err != nil { break } n = copy(p, r.unread) @@ -66,3 +66,22 @@ func (r *dataErrReader) Read(p []byte) (n int, err os.Error) { } return } + +var ErrTimeout = os.NewError("timeout") + +// TimeoutReader returns ErrTimeout on the second read +// with no data. Subsequent calls to read succeed. +func TimeoutReader(r io.Reader) io.Reader { return &timeoutReader{r, 0} } + +type timeoutReader struct { + r io.Reader + count int +} + +func (r *timeoutReader) Read(p []byte) (int, os.Error) { + r.count++ + if r.count == 2 { + return 0, ErrTimeout + } + return r.r.Read(p) +} diff --git a/src/pkg/testing/testing.go b/src/pkg/testing/testing.go index 3b2dd377a..ba721523e 100644 --- a/src/pkg/testing/testing.go +++ b/src/pkg/testing/testing.go @@ -44,6 +44,8 @@ import ( "os" "runtime" "runtime/pprof" + "strings" + "strconv" "time" ) @@ -62,6 +64,9 @@ var ( memProfileRate = flag.Int("test.memprofilerate", 0, "if >=0, sets runtime.MemProfileRate") cpuProfile = flag.String("test.cpuprofile", "", "write a cpu profile to the named file during execution") timeout = flag.Int64("test.timeout", 0, "if > 0, sets time limit for tests in seconds") + cpuListStr = flag.String("test.cpu", "", "comma-separated list of number of CPUs to use for each test") + + cpuList []int ) // Short reports whether the -test.short flag is set. @@ -157,6 +162,7 @@ func tRunner(t *T, test *InternalTest) { // of gotest. func Main(matchString func(pat, str string) (bool, os.Error), tests []InternalTest, benchmarks []InternalBenchmark) { flag.Parse() + parseCpuList() before() startAlarm() @@ -171,7 +177,6 @@ func RunTests(matchString func(pat, str string) (bool, os.Error), tests []Intern if len(tests) == 0 { println("testing: warning: no tests to run") } - procs := runtime.GOMAXPROCS(-1) for i := 0; i < len(tests); i++ { matched, err := matchString(*match, tests[i].Name) if err != nil { @@ -181,28 +186,34 @@ func RunTests(matchString func(pat, str string) (bool, os.Error), tests []Intern if !matched { continue } - if *chatty { - println("=== RUN ", tests[i].Name) - } - ns := -time.Nanoseconds() - t := new(T) - t.ch = make(chan *T) - go tRunner(t, &tests[i]) - <-t.ch - ns += time.Nanoseconds() - tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9) - if p := runtime.GOMAXPROCS(-1); t.failed == false && p != procs { - t.failed = true - t.errors = fmt.Sprintf("%s left GOMAXPROCS set to %d\n", tests[i].Name, p) - procs = p - } - if t.failed { - println("--- FAIL:", tests[i].Name, tstr) - print(t.errors) - ok = false - } else if *chatty { - println("--- PASS:", tests[i].Name, tstr) - print(t.errors) + for _, procs := range cpuList { + runtime.GOMAXPROCS(procs) + testName := tests[i].Name + if procs != 1 { + testName = fmt.Sprintf("%s-%d", tests[i].Name, procs) + } + if *chatty { + println("=== RUN ", testName) + } + ns := -time.Nanoseconds() + t := new(T) + t.ch = make(chan *T) + go tRunner(t, &tests[i]) + <-t.ch + ns += time.Nanoseconds() + tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9) + if p := runtime.GOMAXPROCS(-1); t.failed == false && p != procs { + t.failed = true + t.errors = fmt.Sprintf("%s left GOMAXPROCS set to %d\n", testName, p) + } + if t.failed { + println("--- FAIL:", testName, tstr) + print(t.errors) + ok = false + } else if *chatty { + println("--- PASS:", testName, tstr) + print(t.errors) + } } } if !ok { @@ -271,3 +282,18 @@ func stopAlarm() { func alarm() { panic("test timed out") } + +func parseCpuList() { + if len(*cpuListStr) == 0 { + cpuList = append(cpuList, runtime.GOMAXPROCS(-1)) + } else { + for _, val := range strings.Split(*cpuListStr, ",") { + cpu, err := strconv.Atoi(val) + if err != nil || cpu <= 0 { + println("invalid value for -test.cpu") + os.Exit(1) + } + cpuList = append(cpuList, cpu) + } + } +} diff --git a/src/pkg/time/Makefile b/src/pkg/time/Makefile index 3fa96065e..023e8775e 100644 --- a/src/pkg/time/Makefile +++ b/src/pkg/time/Makefile @@ -13,17 +13,29 @@ GOFILES=\ time.go\ GOFILES_freebsd=\ + sys_posix.go\ + zoneinfo_posix.go\ zoneinfo_unix.go\ GOFILES_darwin=\ + sys_posix.go\ + zoneinfo_posix.go\ zoneinfo_unix.go\ GOFILES_linux=\ + sys_posix.go\ + zoneinfo_posix.go\ zoneinfo_unix.go\ GOFILES_windows=\ + sys_posix.go\ zoneinfo_windows.go\ +GOFILES_plan9=\ + sys_plan9.go\ + zoneinfo_posix.go\ + zoneinfo_plan9.go\ + GOFILES+=$(GOFILES_$(GOOS)) include ../../Make.pkg diff --git a/src/pkg/time/format.go b/src/pkg/time/format.go index 47d736342..26f40d141 100644 --- a/src/pkg/time/format.go +++ b/src/pkg/time/format.go @@ -248,7 +248,7 @@ func (t *Time) Format(layout string) string { var p string switch std { case stdYear: - p = strconv.Itoa64(t.Year % 100) + p = zeroPad(int(t.Year % 100)) case stdLongYear: p = strconv.Itoa64(t.Year) case stdMonth: @@ -355,7 +355,7 @@ func (t *Time) String() string { return t.Format(UnixDate) } -var errBad = os.ErrorString("bad") // just a marker; not returned to user +var errBad = os.NewError("bad") // just a marker; not returned to user // ParseError describes a problem parsing a time string. type ParseError struct { diff --git a/src/pkg/time/sleep_test.go b/src/pkg/time/sleep_test.go index 25e79f9fb..a4a1a429f 100644 --- a/src/pkg/time/sleep_test.go +++ b/src/pkg/time/sleep_test.go @@ -172,7 +172,7 @@ func testAfterQueuing(t *testing.T) os.Error { for _, slot := range slots { go await(slot, result, After(int64(slot)*Delta)) } - sort.SortInts(slots) + sort.Ints(slots) for _, slot := range slots { r := <-result if r.slot != slot { diff --git a/src/pkg/time/sys.go b/src/pkg/time/sys.go index 63f4cbf3d..9fde3b3b6 100644 --- a/src/pkg/time/sys.go +++ b/src/pkg/time/sys.go @@ -4,10 +4,7 @@ package time -import ( - "os" - "syscall" -) +import "os" // Seconds reports the number of seconds since the Unix epoch, // January 1, 1970 00:00:00 UTC. @@ -52,11 +49,3 @@ func sleep(t, ns int64) (int64, os.Error) { } return t, nil } - -func sysSleep(t int64) os.Error { - errno := syscall.Sleep(t) - if errno != 0 && errno != syscall.EINTR { - return os.NewSyscallError("sleep", errno) - } - return nil -} diff --git a/src/pkg/time/sys_plan9.go b/src/pkg/time/sys_plan9.go new file mode 100644 index 000000000..abe8649a2 --- /dev/null +++ b/src/pkg/time/sys_plan9.go @@ -0,0 +1,18 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package time + +import ( + "os" + "syscall" +) + +func sysSleep(t int64) os.Error { + err := syscall.Sleep(t) + if err != nil { + return os.NewSyscallError("sleep", err) + } + return nil +} diff --git a/src/pkg/time/sys_posix.go b/src/pkg/time/sys_posix.go new file mode 100644 index 000000000..0d1eb72fc --- /dev/null +++ b/src/pkg/time/sys_posix.go @@ -0,0 +1,18 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package time + +import ( + "os" + "syscall" +) + +func sysSleep(t int64) os.Error { + errno := syscall.Sleep(t) + if errno != 0 && errno != syscall.EINTR { + return os.NewSyscallError("sleep", errno) + } + return nil +} diff --git a/src/pkg/time/tick.go b/src/pkg/time/tick.go index dde18000d..852bae9c9 100644 --- a/src/pkg/time/tick.go +++ b/src/pkg/time/tick.go @@ -160,7 +160,7 @@ var onceStartTickerLoop sync.Once // ns must be greater than zero; if not, NewTicker will panic. func NewTicker(ns int64) *Ticker { if ns <= 0 { - panic(os.ErrorString("non-positive interval for NewTicker")) + panic(os.NewError("non-positive interval for NewTicker")) } c := make(chan int64, 1) // See comment on send in tickerLoop t := &Ticker{ diff --git a/src/pkg/time/time_test.go b/src/pkg/time/time_test.go index eb676bf64..eec8a7a5c 100644 --- a/src/pkg/time/time_test.go +++ b/src/pkg/time/time_test.go @@ -142,21 +142,22 @@ type FormatTest struct { } var formatTests = []FormatTest{ - {"ANSIC", ANSIC, "Thu Feb 4 21:00:57 2010"}, - {"UnixDate", UnixDate, "Thu Feb 4 21:00:57 PST 2010"}, - {"RubyDate", RubyDate, "Thu Feb 04 21:00:57 -0800 2010"}, - {"RFC822", RFC822, "04 Feb 10 2100 PST"}, - {"RFC850", RFC850, "Thursday, 04-Feb-10 21:00:57 PST"}, - {"RFC1123", RFC1123, "Thu, 04 Feb 2010 21:00:57 PST"}, - {"RFC3339", RFC3339, "2010-02-04T21:00:57-08:00"}, + {"ANSIC", ANSIC, "Wed Feb 4 21:00:57 2009"}, + {"UnixDate", UnixDate, "Wed Feb 4 21:00:57 PST 2009"}, + {"RubyDate", RubyDate, "Wed Feb 04 21:00:57 -0800 2009"}, + {"RFC822", RFC822, "04 Feb 09 2100 PST"}, + {"RFC850", RFC850, "Wednesday, 04-Feb-09 21:00:57 PST"}, + {"RFC1123", RFC1123, "Wed, 04 Feb 2009 21:00:57 PST"}, + {"RFC3339", RFC3339, "2009-02-04T21:00:57-08:00"}, {"Kitchen", Kitchen, "9:00PM"}, {"am/pm", "3pm", "9pm"}, {"AM/PM", "3PM", "9PM"}, + {"two-digit year", "06 01 02", "09 02 04"}, } func TestFormat(t *testing.T) { // The numeric time represents Thu Feb 4 21:00:57 PST 2010 - time := SecondsToLocalTime(1265346057) + time := SecondsToLocalTime(1233810057) for _, test := range formatTests { result := time.Format(test.format) if result != test.result { diff --git a/src/pkg/time/zoneinfo_plan9.go b/src/pkg/time/zoneinfo_plan9.go new file mode 100644 index 000000000..3c3e7c424 --- /dev/null +++ b/src/pkg/time/zoneinfo_plan9.go @@ -0,0 +1,59 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Parse Plan 9 timezone(2) files. + +package time + +import ( + "os" + "strconv" + "strings" +) + +func parseZones(s string) (zt []zonetime) { + f := strings.Fields(s) + if len(f) < 4 { + return + } + + // standard timezone offset + o, err := strconv.Atoi(f[1]) + if err != nil { + return + } + std := &zone{name: f[0], utcoff: o, isdst: false} + + // alternate timezone offset + o, err = strconv.Atoi(f[3]) + if err != nil { + return + } + dst := &zone{name: f[2], utcoff: o, isdst: true} + + // transition time pairs + f = f[4:] + for i := 0; i < len(f); i++ { + z := std + if i%2 == 0 { + z = dst + } + t, err := strconv.Atoi(f[i]) + if err != nil { + return nil + } + t -= std.utcoff + zt = append(zt, zonetime{time: int32(t), zone: z}) + } + return +} + +func setupZone() { + t, err := os.Getenverror("timezone") + if err != nil { + // do nothing: use UTC + return + } + zones = parseZones(t) +} diff --git a/src/pkg/time/zoneinfo_posix.go b/src/pkg/time/zoneinfo_posix.go new file mode 100644 index 000000000..b49216410 --- /dev/null +++ b/src/pkg/time/zoneinfo_posix.go @@ -0,0 +1,62 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package time + +import "sync" + +// Parsed representation +type zone struct { + utcoff int + isdst bool + name string +} + +type zonetime struct { + time int32 // transition time, in seconds since 1970 GMT + zone *zone // the zone that goes into effect at that time + isstd, isutc bool // ignored - no idea what these mean +} + +var zones []zonetime +var onceSetupZone sync.Once + +// Look up the correct time zone (daylight savings or not) for the given unix time, in the current location. +func lookupTimezone(sec int64) (zone string, offset int) { + onceSetupZone.Do(setupZone) + if len(zones) == 0 { + return "UTC", 0 + } + + // Binary search for entry with largest time <= sec + tz := zones + for len(tz) > 1 { + m := len(tz) / 2 + if sec < int64(tz[m].time) { + tz = tz[0:m] + } else { + tz = tz[m:] + } + } + z := tz[0].zone + return z.name, z.utcoff +} + +// lookupByName returns the time offset for the +// time zone with the given abbreviation. It only considers +// time zones that apply to the current system. +// For example, for a system configured as being in New York, +// it only recognizes "EST" and "EDT". +// For a system in San Francisco, "PST" and "PDT". +// For a system in Sydney, "EST" and "EDT", though they have +// different meanings than they do in New York. +func lookupByName(name string) (off int, found bool) { + onceSetupZone.Do(setupZone) + for _, z := range zones { + if name == z.zone.name { + return z.zone.utcoff, true + } + } + return 0, false +} diff --git a/src/pkg/time/zoneinfo_unix.go b/src/pkg/time/zoneinfo_unix.go index 42659ed60..2a83e0c16 100644 --- a/src/pkg/time/zoneinfo_unix.go +++ b/src/pkg/time/zoneinfo_unix.go @@ -12,7 +12,6 @@ package time import ( "io/ioutil" "os" - "sync" ) const ( @@ -66,19 +65,6 @@ func byteString(p []byte) string { return string(p) } -// Parsed representation -type zone struct { - utcoff int - isdst bool - name string -} - -type zonetime struct { - time int32 // transition time, in seconds since 1970 GMT - zone *zone // the zone that goes into effect at that time - isstd, isutc bool // ignored - no idea what these mean -} - func parseinfo(bytes []byte) (zt []zonetime, ok bool) { d := data{bytes, false} @@ -201,9 +187,6 @@ func readinfofile(name string) ([]zonetime, bool) { return parseinfo(buf) } -var zones []zonetime -var onceSetupZone sync.Once - func setupZone() { // consult $TZ to find the time zone to use. // no $TZ means use the system default /etc/localtime. @@ -230,42 +213,3 @@ func setupZone() { // do nothing: use UTC } } - -// Look up the correct time zone (daylight savings or not) for the given unix time, in the current location. -func lookupTimezone(sec int64) (zone string, offset int) { - onceSetupZone.Do(setupZone) - if len(zones) == 0 { - return "UTC", 0 - } - - // Binary search for entry with largest time <= sec - tz := zones - for len(tz) > 1 { - m := len(tz) / 2 - if sec < int64(tz[m].time) { - tz = tz[0:m] - } else { - tz = tz[m:] - } - } - z := tz[0].zone - return z.name, z.utcoff -} - -// lookupByName returns the time offset for the -// time zone with the given abbreviation. It only considers -// time zones that apply to the current system. -// For example, for a system configured as being in New York, -// it only recognizes "EST" and "EDT". -// For a system in San Francisco, "PST" and "PDT". -// For a system in Sydney, "EST" and "EDT", though they have -// different meanings than they do in New York. -func lookupByName(name string) (off int, found bool) { - onceSetupZone.Do(setupZone) - for _, z := range zones { - if name == z.zone.name { - return z.zone.utcoff, true - } - } - return 0, false -} diff --git a/src/pkg/unicode/letter.go b/src/pkg/unicode/letter.go index a0c55bbf7..dbd8638ea 100644 --- a/src/pkg/unicode/letter.go +++ b/src/pkg/unicode/letter.go @@ -275,3 +275,52 @@ func (special SpecialCase) ToLower(rune int) int { } return r } + +// caseOrbit is defined in tables.go as []foldPair. Right now all the +// entries fit in uint16, so use uint16. If that changes, compilation +// will fail (the constants in the composite literal will not fit in uint16) +// and the types here can change to uint32. +type foldPair struct { + From uint16 + To uint16 +} + +// SimpleFold iterates over Unicode code points equivalent under +// the Unicode-defined simple case folding. Among the code points +// equivalent to rune (including rune itself), SimpleFold returns the +// smallest r >= rune if one exists, or else the smallest r >= 0. +// +// For example: +// SimpleFold('A') = 'a' +// SimpleFold('a') = 'A' +// +// SimpleFold('K') = 'k' +// SimpleFold('k') = '\u212A' (Kelvin symbol, K) +// SimpleFold('\u212A') = 'K' +// +// SimpleFold('1') = '1' +// +func SimpleFold(rune int) int { + // Consult caseOrbit table for special cases. + lo := 0 + hi := len(caseOrbit) + for lo < hi { + m := lo + (hi-lo)/2 + if int(caseOrbit[m].From) < rune { + lo = m + 1 + } else { + hi = m + } + } + if lo < len(caseOrbit) && int(caseOrbit[lo].From) == rune { + return int(caseOrbit[lo].To) + } + + // No folding specified. This is a one- or two-element + // equivalence class containing rune and ToLower(rune) + // and ToUpper(rune) if they are different from rune. + if l := ToLower(rune); l != rune { + return l + } + return ToUpper(rune) +} diff --git a/src/pkg/unicode/letter_test.go b/src/pkg/unicode/letter_test.go index 4c24ffc51..c4e26df58 100644 --- a/src/pkg/unicode/letter_test.go +++ b/src/pkg/unicode/letter_test.go @@ -376,3 +376,49 @@ func TestTurkishCase(t *testing.T) { } } } + +var simpleFoldTests = []string{ + // SimpleFold could order its returned slices in any order it wants, + // but we know it orders them in increasing order starting at in + // and looping around from MaxRune to 0. + + // Easy cases. + "Aa", + "aA", + "δΔ", + "Δδ", + + // ASCII special cases. + "KkK", + "kKK", + "KKk", + "Ssſ", + "sſS", + "ſSs", + + // Non-ASCII special cases. + "ρϱΡ", + "ϱΡρ", + "Ρρϱ", + "ͅΙιι", + "Ιιιͅ", + "ιιͅΙ", + "ιͅΙι", + + // Extra special cases: has lower/upper but no case fold. + "İ", + "ı", +} + +func TestSimpleFold(t *testing.T) { + for _, tt := range simpleFoldTests { + cycle := []int(tt) + rune := cycle[len(cycle)-1] + for _, out := range cycle { + if r := SimpleFold(rune); r != out { + t.Errorf("SimpleFold(%#U) = %#U, want %#U", rune, r, out) + } + rune = out + } + } +} diff --git a/src/pkg/unicode/maketables.go b/src/pkg/unicode/maketables.go index 655fe46e4..07b931d7e 100644 --- a/src/pkg/unicode/maketables.go +++ b/src/pkg/unicode/maketables.go @@ -24,15 +24,18 @@ import ( func main() { flag.Parse() loadChars() // always needed + loadCasefold() printCategories() printScriptOrProperty(false) printScriptOrProperty(true) printCases() printLatinProperties() + printCasefold() printSizes() } var dataURL = flag.String("data", "", "full URL for UnicodeData.txt; defaults to --url/UnicodeData.txt") +var casefoldingURL = flag.String("casefolding", "", "full URL for CaseFolding.txt; defaults to --url/CaseFolding.txt") var url = flag.String("url", "http://www.unicode.org/Public/6.0.0/ucd/", "URL of Unicode database directory") @@ -70,7 +73,7 @@ var category = map[string]bool{ // UnicodeData.txt has form: // 0037;DIGIT SEVEN;Nd;0;EN;;7;7;7;N;;;;; // 007A;LATIN SMALL LETTER Z;Ll;0;L;;;;;N;;;005A;;005A -// See http://www.unicode.org/Public/5.1.0/ucd/UCD.html for full explanation +// See http://www.unicode.org/reports/tr44/ for a full explanation // The fields: const ( FCodePoint = iota @@ -78,10 +81,10 @@ const ( FGeneralCategory FCanonicalCombiningClass FBidiClass - FDecompositionType - FDecompositionMapping + FDecompositionTypeAndMapping FNumericType - FNumericValue + FNumericDigit // If a decimal digit. + FNumericValue // Includes non-decimal, e.g. U+2155=1/5 FBidiMirrored FUnicode1Name FISOComment @@ -94,21 +97,21 @@ const ( ) var fieldName = []string{ - "CodePoint", - "Name", - "GeneralCategory", - "CanonicalCombiningClass", - "BidiClass", - "DecompositionType", - "DecompositionMapping", - "NumericType", - "NumericValue", - "BidiMirrored", - "Unicode1Name", - "ISOComment", - "SimpleUppercaseMapping", - "SimpleLowercaseMapping", - "SimpleTitlecaseMapping", + FCodePoint: "CodePoint", + FName: "Name", + FGeneralCategory: "GeneralCategory", + FCanonicalCombiningClass: "CanonicalCombiningClass", + FBidiClass: "BidiClass", + FDecompositionTypeAndMapping: "DecompositionTypeAndMapping", + FNumericType: "NumericType", + FNumericDigit: "NumericDigit", + FNumericValue: "NumericValue", + FBidiMirrored: "BidiMirrored", + FUnicode1Name: "Unicode1Name", + FISOComment: "ISOComment", + FSimpleUppercaseMapping: "SimpleUppercaseMapping", + FSimpleLowercaseMapping: "SimpleLowercaseMapping", + FSimpleTitlecaseMapping: "SimpleTitlecaseMapping", } // This contains only the properties we're interested in. @@ -119,6 +122,8 @@ type Char struct { upperCase int lowerCase int titleCase int + foldCase int // simple case folding + caseOrbit int // next in simple case folding orbit } // Scripts.txt has form: @@ -151,7 +156,7 @@ const ( ) func parseCategory(line string) (state State) { - field := strings.Split(line, ";", -1) + field := strings.Split(line, ";") if len(field) != NumField { logger.Fatalf("%5s: %d fields (expected %d)\n", line, len(field), NumField) } @@ -248,7 +253,7 @@ func all(scripts map[string][]Script) []string { // Extract the version number from the URL func version() string { // Break on slashes and look for the first numeric field - fields := strings.Split(*url, "/", -1) + fields := strings.Split(*url, "/") for _, f := range fields { if len(f) > 0 && '0' <= f[0] && f[0] <= '9' { return f @@ -308,8 +313,53 @@ func loadChars() { resp.Body.Close() } +func loadCasefold() { + if *casefoldingURL == "" { + flag.Set("casefolding", *url+"CaseFolding.txt") + } + resp, err := http.Get(*casefoldingURL) + if err != nil { + logger.Fatal(err) + } + if resp.StatusCode != 200 { + logger.Fatal("bad GET status for CaseFolding.txt", resp.Status) + } + input := bufio.NewReader(resp.Body) + for { + line, err := input.ReadString('\n') + if err != nil { + if err == os.EOF { + break + } + logger.Fatal(err) + } + if line[0] == '#' { + continue + } + field := strings.Split(line, "; ") + if len(field) != 4 { + logger.Fatalf("CaseFolding.txt %.5s...: %d fields (expected %d)\n", line, len(field), 4) + } + kind := field[1] + if kind != "C" && kind != "S" { + // Only care about 'common' and 'simple' foldings. + continue + } + p1, err := strconv.Btoui64(field[0], 16) + if err != nil { + logger.Fatalf("CaseFolding.txt %.5s...: %s", line, err) + } + p2, err := strconv.Btoui64(field[2], 16) + if err != nil { + logger.Fatalf("CaseFolding.txt %.5s...: %s", line, err) + } + chars[p1].foldCase = int(p2) + } + resp.Body.Close() +} + const progHeader = `// Generated by running -// maketables --tables=%s --data=%s +// maketables --tables=%s --data=%s --casefolding=%s // DO NOT EDIT package unicode @@ -322,7 +372,7 @@ func printCategories() { return } // Find out which categories to dump - list := strings.Split(*tablelist, ",", -1) + list := strings.Split(*tablelist, ",") if *tablelist == "all" { list = allCategories() } @@ -330,7 +380,7 @@ func printCategories() { fullCategoryTest(list) return } - fmt.Printf(progHeader, *tablelist, *dataURL) + fmt.Printf(progHeader, *tablelist, *dataURL, *casefoldingURL) fmt.Println("// Version is the Unicode edition from which the tables are derived.") fmt.Printf("const Version = %q\n\n", version()) @@ -344,7 +394,7 @@ func printCategories() { fmt.Print("}\n\n") } - decl := make(sort.StringArray, len(list)) + decl := make(sort.StringSlice, len(list)) ndecl := 0 for _, name := range list { if _, ok := category[name]; !ok { @@ -538,7 +588,7 @@ func parseScript(line string, scripts map[string][]Script) { if len(line) == 0 { return } - field := strings.Split(line, ";", -1) + field := strings.Split(line, ";") if len(field) != 2 { logger.Fatalf("%s: %d fields (expected 2)\n", line, len(field)) } @@ -635,7 +685,7 @@ func printScriptOrProperty(doProps bool) { resp.Body.Close() // Find out which scripts to dump - list := strings.Split(flaglist, ",", -1) + list := strings.Split(flaglist, ",") if flaglist == "all" { list = all(table) } @@ -665,7 +715,7 @@ func printScriptOrProperty(doProps bool) { fmt.Print("}\n\n") } - decl := make(sort.StringArray, len(list)) + decl := make(sort.StringSlice, len(list)) ndecl := 0 for _, name := range list { if doProps { @@ -837,13 +887,13 @@ func printCases() { } fmt.Printf( "// Generated by running\n"+ - "// maketables --data=%s\n"+ + "// maketables --data=%s --casefolding=%s\n"+ "// DO NOT EDIT\n\n"+ "// CaseRanges is the table describing case mappings for all letters with\n"+ "// non-self mappings.\n"+ "var CaseRanges = _CaseRanges\n"+ "var _CaseRanges = []CaseRange {\n", - *dataURL) + *dataURL, *casefoldingURL) var startState *caseState // the start of a run; nil for not active var prevState = &caseState{} // the state of the previous character @@ -946,13 +996,246 @@ func printLatinProperties() { if code == ' ' { property = "pZ | pp" } - fmt.Printf("\t0x%.2X: %s, // %q\n", code, property, code) + fmt.Printf("\t0x%02X: %s, // %q\n", code, property, code) } - fmt.Println("}") + fmt.Printf("}\n\n") } -var range16Count = 0 // Number of entries in the 16-bit range tables. -var range32Count = 0 // Number of entries in the 32-bit range tables. +func printCasefold() { + // Build list of case-folding groups attached to each canonical folded char (typically lower case). + var caseOrbit = make([][]int, MaxChar+1) + for i := range chars { + c := &chars[i] + if c.foldCase == 0 { + continue + } + orb := caseOrbit[c.foldCase] + if orb == nil { + orb = append(orb, c.foldCase) + } + caseOrbit[c.foldCase] = append(orb, i) + } + + // Insert explicit 1-element groups when assuming [lower, upper] would be wrong. + for i := range chars { + c := &chars[i] + f := c.foldCase + if f == 0 { + f = i + } + orb := caseOrbit[f] + if orb == nil && (c.upperCase != 0 && c.upperCase != i || c.lowerCase != 0 && c.lowerCase != i) { + // Default assumption of [upper, lower] is wrong. + caseOrbit[i] = []int{i} + } + } + + // Delete the groups for which assuming [lower, upper] is right. + for i, orb := range caseOrbit { + if len(orb) == 2 && chars[orb[0]].upperCase == orb[1] && chars[orb[1]].lowerCase == orb[0] { + caseOrbit[i] = nil + } + } + + // Record orbit information in chars. + for _, orb := range caseOrbit { + if orb == nil { + continue + } + sort.Ints(orb) + c := orb[len(orb)-1] + for _, d := range orb { + chars[c].caseOrbit = d + c = d + } + } + + printCaseOrbit() + + // Tables of category and script folding exceptions: code points + // that must be added when interpreting a particular category/script + // in a case-folding context. + cat := make(map[string]map[int]bool) + for name := range category { + if x := foldExceptions(inCategory(name)); len(x) > 0 { + cat[name] = x + } + } + + scr := make(map[string]map[int]bool) + for name := range scripts { + if x := foldExceptions(inScript(name)); len(x) > 0 { + cat[name] = x + } + } + + printCatFold("FoldCategory", cat) + printCatFold("FoldScript", scr) +} + +// inCategory returns a list of all the runes in the category. +func inCategory(name string) []int { + var x []int + for i := range chars { + c := &chars[i] + if c.category == name || len(name) == 1 && len(c.category) > 1 && c.category[0] == name[0] { + x = append(x, i) + } + } + return x +} + +// inScript returns a list of all the runes in the script. +func inScript(name string) []int { + var x []int + for _, s := range scripts[name] { + for c := s.lo; c <= s.hi; c++ { + x = append(x, int(c)) + } + } + return x +} + +// foldExceptions returns a list of all the runes fold-equivalent +// to runes in class but not in class themselves. +func foldExceptions(class []int) map[int]bool { + // Create map containing class and all fold-equivalent chars. + m := make(map[int]bool) + for _, r := range class { + c := &chars[r] + if c.caseOrbit == 0 { + // Just upper and lower. + if u := c.upperCase; u != 0 { + m[u] = true + } + if l := c.lowerCase; l != 0 { + m[l] = true + } + m[r] = true + continue + } + // Otherwise walk orbit. + r0 := r + for { + m[r] = true + r = chars[r].caseOrbit + if r == r0 { + break + } + } + } + + // Remove class itself. + for _, r := range class { + m[r] = false, false + } + + // What's left is the exceptions. + return m +} + +var comment = map[string]string{ + "FoldCategory": "// FoldCategory maps a category name to a table of\n" + + "// code points outside the category that are equivalent under\n" + + "// simple case folding to code points inside the category.\n" + + "// If there is no entry for a category name, there are no such points.\n", + + "FoldScript": "// FoldScript maps a script name to a table of\n" + + "// code points outside the script that are equivalent under\n" + + "// simple case folding to code points inside the script.\n" + + "// If there is no entry for a script name, there are no such points.\n", +} + +func printCaseOrbit() { + if *test { + for i := range chars { + c := &chars[i] + f := c.caseOrbit + if f == 0 { + if c.lowerCase != i && c.lowerCase != 0 { + f = c.lowerCase + } else if c.upperCase != i && c.upperCase != 0 { + f = c.upperCase + } else { + f = i + } + } + if g := unicode.SimpleFold(i); g != f { + fmt.Fprintf(os.Stderr, "unicode.SimpleFold(%#U) = %#U, want %#U\n", i, g, f) + } + } + return + } + + fmt.Printf("var caseOrbit = []foldPair{\n") + for i := range chars { + c := &chars[i] + if c.caseOrbit != 0 { + fmt.Printf("\t{0x%04X, 0x%04X},\n", i, c.caseOrbit) + foldPairCount++ + } + } + fmt.Printf("}\n\n") +} + +func printCatFold(name string, m map[string]map[int]bool) { + if *test { + var pkgMap map[string]*unicode.RangeTable + if name == "FoldCategory" { + pkgMap = unicode.FoldCategory + } else { + pkgMap = unicode.FoldScript + } + if len(pkgMap) != len(m) { + fmt.Fprintf(os.Stderr, "unicode.%s has %d elements, want %d\n", name, len(pkgMap), len(m)) + return + } + for k, v := range m { + t, ok := pkgMap[k] + if !ok { + fmt.Fprintf(os.Stderr, "unicode.%s[%q] missing\n", name, k) + continue + } + n := 0 + for _, r := range t.R16 { + for c := int(r.Lo); c <= int(r.Hi); c += int(r.Stride) { + if !v[c] { + fmt.Fprintf(os.Stderr, "unicode.%s[%q] contains %#U, should not\n", name, k, c) + } + n++ + } + } + for _, r := range t.R32 { + for c := int(r.Lo); c <= int(r.Hi); c += int(r.Stride) { + if !v[c] { + fmt.Fprintf(os.Stderr, "unicode.%s[%q] contains %#U, should not\n", name, k, c) + } + n++ + } + } + if n != len(v) { + fmt.Fprintf(os.Stderr, "unicode.%s[%q] has %d code points, want %d\n", name, k, n, len(v)) + } + } + return + } + + fmt.Print(comment[name]) + fmt.Printf("var %s = map[string]*RangeTable{\n", name) + for name := range m { + fmt.Printf("\t%q: fold%s,\n", name, name) + } + fmt.Printf("}\n\n") + for name, class := range m { + dumpRange( + fmt.Sprintf("var fold%s = &RangeTable{\n", name), + func(code int) bool { return class[code] }) + } +} + +var range16Count = 0 // Number of entries in the 16-bit range tables. +var range32Count = 0 // Number of entries in the 32-bit range tables. +var foldPairCount = 0 // Number of fold pairs in the exception tables. func printSizes() { if *test { @@ -963,4 +1246,6 @@ func printSizes() { range16Bytes := range16Count * 3 * 2 range32Bytes := range32Count * 3 * 4 fmt.Printf("// Range bytes: %d 16-bit, %d 32-bit, %d total.\n", range16Bytes, range32Bytes, range16Bytes+range32Bytes) + fmt.Println() + fmt.Printf("// Fold orbit bytes: %d pairs, %d bytes\n", foldPairCount, foldPairCount*2*2) } diff --git a/src/pkg/unicode/tables.go b/src/pkg/unicode/tables.go index 32681a8c0..a75011adb 100644 --- a/src/pkg/unicode/tables.go +++ b/src/pkg/unicode/tables.go @@ -1,5 +1,5 @@ // Generated by running -// maketables --tables=all --data=http://www.unicode.org/Public/6.0.0/ucd/UnicodeData.txt +// maketables --tables=all --data=http://www.unicode.org/Public/6.0.0/ucd/UnicodeData.txt --casefolding=http://www.unicode.org/Public/6.0.0/ucd/CaseFolding.txt // DO NOT EDIT package unicode @@ -5150,7 +5150,7 @@ var ( ) // Generated by running -// maketables --data=http://www.unicode.org/Public/6.0.0/ucd/UnicodeData.txt +// maketables --data=http://www.unicode.org/Public/6.0.0/ucd/UnicodeData.txt --casefolding=http://www.unicode.org/Public/6.0.0/ucd/CaseFolding.txt // DO NOT EDIT // CaseRanges is the table describing case mappings for all letters with @@ -5539,7 +5539,7 @@ var properties = [MaxLatin1 + 1]uint8{ 0x7C: pS | pp, // '|' 0x7D: pP | pp, // '}' 0x7E: pS | pp, // '~' - 0x7F: pC, // '\x7f' + 0x7F: pC, // '\u007f' 0x80: pC, // '\u0080' 0x81: pC, // '\u0081' 0x82: pC, // '\u0082' @@ -5573,102 +5573,534 @@ var properties = [MaxLatin1 + 1]uint8{ 0x9E: pC, // '\u009e' 0x9F: pC, // '\u009f' 0xA0: pZ, // '\u00a0' - 0xA1: pP | pp, // '\u00a1' - 0xA2: pS | pp, // '\u00a2' - 0xA3: pS | pp, // '\u00a3' - 0xA4: pS | pp, // '\u00a4' - 0xA5: pS | pp, // '\u00a5' - 0xA6: pS | pp, // '\u00a6' - 0xA7: pS | pp, // '\u00a7' - 0xA8: pS | pp, // '\u00a8' - 0xA9: pS | pp, // '\u00a9' - 0xAA: pLl | pp, // '\u00aa' - 0xAB: pP | pp, // '\u00ab' - 0xAC: pS | pp, // '\u00ac' + 0xA1: pP | pp, // '¡' + 0xA2: pS | pp, // '¢' + 0xA3: pS | pp, // '£' + 0xA4: pS | pp, // '¤' + 0xA5: pS | pp, // '¥' + 0xA6: pS | pp, // '¦' + 0xA7: pS | pp, // '§' + 0xA8: pS | pp, // '¨' + 0xA9: pS | pp, // '©' + 0xAA: pLl | pp, // 'ª' + 0xAB: pP | pp, // '«' + 0xAC: pS | pp, // '¬' 0xAD: 0, // '\u00ad' - 0xAE: pS | pp, // '\u00ae' - 0xAF: pS | pp, // '\u00af' - 0xB0: pS | pp, // '\u00b0' - 0xB1: pS | pp, // '\u00b1' - 0xB2: pN | pp, // '\u00b2' - 0xB3: pN | pp, // '\u00b3' - 0xB4: pS | pp, // '\u00b4' - 0xB5: pLl | pp, // '\u00b5' - 0xB6: pS | pp, // '\u00b6' - 0xB7: pP | pp, // '\u00b7' - 0xB8: pS | pp, // '\u00b8' - 0xB9: pN | pp, // '\u00b9' - 0xBA: pLl | pp, // '\u00ba' - 0xBB: pP | pp, // '\u00bb' - 0xBC: pN | pp, // '\u00bc' - 0xBD: pN | pp, // '\u00bd' - 0xBE: pN | pp, // '\u00be' - 0xBF: pP | pp, // '\u00bf' - 0xC0: pLu | pp, // '\u00c0' - 0xC1: pLu | pp, // '\u00c1' - 0xC2: pLu | pp, // '\u00c2' - 0xC3: pLu | pp, // '\u00c3' - 0xC4: pLu | pp, // '\u00c4' - 0xC5: pLu | pp, // '\u00c5' - 0xC6: pLu | pp, // '\u00c6' - 0xC7: pLu | pp, // '\u00c7' - 0xC8: pLu | pp, // '\u00c8' - 0xC9: pLu | pp, // '\u00c9' - 0xCA: pLu | pp, // '\u00ca' - 0xCB: pLu | pp, // '\u00cb' - 0xCC: pLu | pp, // '\u00cc' - 0xCD: pLu | pp, // '\u00cd' - 0xCE: pLu | pp, // '\u00ce' - 0xCF: pLu | pp, // '\u00cf' - 0xD0: pLu | pp, // '\u00d0' - 0xD1: pLu | pp, // '\u00d1' - 0xD2: pLu | pp, // '\u00d2' - 0xD3: pLu | pp, // '\u00d3' - 0xD4: pLu | pp, // '\u00d4' - 0xD5: pLu | pp, // '\u00d5' - 0xD6: pLu | pp, // '\u00d6' - 0xD7: pS | pp, // '\u00d7' - 0xD8: pLu | pp, // '\u00d8' - 0xD9: pLu | pp, // '\u00d9' - 0xDA: pLu | pp, // '\u00da' - 0xDB: pLu | pp, // '\u00db' - 0xDC: pLu | pp, // '\u00dc' - 0xDD: pLu | pp, // '\u00dd' - 0xDE: pLu | pp, // '\u00de' - 0xDF: pLl | pp, // '\u00df' - 0xE0: pLl | pp, // '\u00e0' - 0xE1: pLl | pp, // '\u00e1' - 0xE2: pLl | pp, // '\u00e2' - 0xE3: pLl | pp, // '\u00e3' - 0xE4: pLl | pp, // '\u00e4' - 0xE5: pLl | pp, // '\u00e5' - 0xE6: pLl | pp, // '\u00e6' - 0xE7: pLl | pp, // '\u00e7' - 0xE8: pLl | pp, // '\u00e8' - 0xE9: pLl | pp, // '\u00e9' - 0xEA: pLl | pp, // '\u00ea' - 0xEB: pLl | pp, // '\u00eb' - 0xEC: pLl | pp, // '\u00ec' - 0xED: pLl | pp, // '\u00ed' - 0xEE: pLl | pp, // '\u00ee' - 0xEF: pLl | pp, // '\u00ef' - 0xF0: pLl | pp, // '\u00f0' - 0xF1: pLl | pp, // '\u00f1' - 0xF2: pLl | pp, // '\u00f2' - 0xF3: pLl | pp, // '\u00f3' - 0xF4: pLl | pp, // '\u00f4' - 0xF5: pLl | pp, // '\u00f5' - 0xF6: pLl | pp, // '\u00f6' - 0xF7: pS | pp, // '\u00f7' - 0xF8: pLl | pp, // '\u00f8' - 0xF9: pLl | pp, // '\u00f9' - 0xFA: pLl | pp, // '\u00fa' - 0xFB: pLl | pp, // '\u00fb' - 0xFC: pLl | pp, // '\u00fc' - 0xFD: pLl | pp, // '\u00fd' - 0xFE: pLl | pp, // '\u00fe' - 0xFF: pLl | pp, // '\u00ff' -} - -// Range entries: 3190 16-bit, 657 32-bit, 3847 total. -// Range bytes: 19140 16-bit, 7884 32-bit, 27024 total. + 0xAE: pS | pp, // '®' + 0xAF: pS | pp, // '¯' + 0xB0: pS | pp, // '°' + 0xB1: pS | pp, // '±' + 0xB2: pN | pp, // '²' + 0xB3: pN | pp, // '³' + 0xB4: pS | pp, // '´' + 0xB5: pLl | pp, // 'µ' + 0xB6: pS | pp, // '¶' + 0xB7: pP | pp, // '·' + 0xB8: pS | pp, // '¸' + 0xB9: pN | pp, // '¹' + 0xBA: pLl | pp, // 'º' + 0xBB: pP | pp, // '»' + 0xBC: pN | pp, // '¼' + 0xBD: pN | pp, // '½' + 0xBE: pN | pp, // '¾' + 0xBF: pP | pp, // '¿' + 0xC0: pLu | pp, // 'À' + 0xC1: pLu | pp, // 'Á' + 0xC2: pLu | pp, // 'Â' + 0xC3: pLu | pp, // 'Ã' + 0xC4: pLu | pp, // 'Ä' + 0xC5: pLu | pp, // 'Å' + 0xC6: pLu | pp, // 'Æ' + 0xC7: pLu | pp, // 'Ç' + 0xC8: pLu | pp, // 'È' + 0xC9: pLu | pp, // 'É' + 0xCA: pLu | pp, // 'Ê' + 0xCB: pLu | pp, // 'Ë' + 0xCC: pLu | pp, // 'Ì' + 0xCD: pLu | pp, // 'Í' + 0xCE: pLu | pp, // 'Î' + 0xCF: pLu | pp, // 'Ï' + 0xD0: pLu | pp, // 'Ð' + 0xD1: pLu | pp, // 'Ñ' + 0xD2: pLu | pp, // 'Ò' + 0xD3: pLu | pp, // 'Ó' + 0xD4: pLu | pp, // 'Ô' + 0xD5: pLu | pp, // 'Õ' + 0xD6: pLu | pp, // 'Ö' + 0xD7: pS | pp, // '×' + 0xD8: pLu | pp, // 'Ø' + 0xD9: pLu | pp, // 'Ù' + 0xDA: pLu | pp, // 'Ú' + 0xDB: pLu | pp, // 'Û' + 0xDC: pLu | pp, // 'Ü' + 0xDD: pLu | pp, // 'Ý' + 0xDE: pLu | pp, // 'Þ' + 0xDF: pLl | pp, // 'ß' + 0xE0: pLl | pp, // 'à' + 0xE1: pLl | pp, // 'á' + 0xE2: pLl | pp, // 'â' + 0xE3: pLl | pp, // 'ã' + 0xE4: pLl | pp, // 'ä' + 0xE5: pLl | pp, // 'å' + 0xE6: pLl | pp, // 'æ' + 0xE7: pLl | pp, // 'ç' + 0xE8: pLl | pp, // 'è' + 0xE9: pLl | pp, // 'é' + 0xEA: pLl | pp, // 'ê' + 0xEB: pLl | pp, // 'ë' + 0xEC: pLl | pp, // 'ì' + 0xED: pLl | pp, // 'í' + 0xEE: pLl | pp, // 'î' + 0xEF: pLl | pp, // 'ï' + 0xF0: pLl | pp, // 'ð' + 0xF1: pLl | pp, // 'ñ' + 0xF2: pLl | pp, // 'ò' + 0xF3: pLl | pp, // 'ó' + 0xF4: pLl | pp, // 'ô' + 0xF5: pLl | pp, // 'õ' + 0xF6: pLl | pp, // 'ö' + 0xF7: pS | pp, // '÷' + 0xF8: pLl | pp, // 'ø' + 0xF9: pLl | pp, // 'ù' + 0xFA: pLl | pp, // 'ú' + 0xFB: pLl | pp, // 'û' + 0xFC: pLl | pp, // 'ü' + 0xFD: pLl | pp, // 'ý' + 0xFE: pLl | pp, // 'þ' + 0xFF: pLl | pp, // 'ÿ' +} + +var caseOrbit = []foldPair{ + {0x004B, 0x006B}, + {0x0053, 0x0073}, + {0x006B, 0x212A}, + {0x0073, 0x017F}, + {0x00B5, 0x039C}, + {0x00C5, 0x00E5}, + {0x00DF, 0x1E9E}, + {0x00E5, 0x212B}, + {0x0130, 0x0130}, + {0x0131, 0x0131}, + {0x017F, 0x0053}, + {0x01C4, 0x01C5}, + {0x01C5, 0x01C6}, + {0x01C6, 0x01C4}, + {0x01C7, 0x01C8}, + {0x01C8, 0x01C9}, + {0x01C9, 0x01C7}, + {0x01CA, 0x01CB}, + {0x01CB, 0x01CC}, + {0x01CC, 0x01CA}, + {0x01F1, 0x01F2}, + {0x01F2, 0x01F3}, + {0x01F3, 0x01F1}, + {0x0345, 0x0399}, + {0x0392, 0x03B2}, + {0x0395, 0x03B5}, + {0x0398, 0x03B8}, + {0x0399, 0x03B9}, + {0x039A, 0x03BA}, + {0x039C, 0x03BC}, + {0x03A0, 0x03C0}, + {0x03A1, 0x03C1}, + {0x03A3, 0x03C2}, + {0x03A6, 0x03C6}, + {0x03A9, 0x03C9}, + {0x03B2, 0x03D0}, + {0x03B5, 0x03F5}, + {0x03B8, 0x03D1}, + {0x03B9, 0x1FBE}, + {0x03BA, 0x03F0}, + {0x03BC, 0x00B5}, + {0x03C0, 0x03D6}, + {0x03C1, 0x03F1}, + {0x03C2, 0x03C3}, + {0x03C3, 0x03A3}, + {0x03C6, 0x03D5}, + {0x03C9, 0x2126}, + {0x03D0, 0x0392}, + {0x03D1, 0x03F4}, + {0x03D5, 0x03A6}, + {0x03D6, 0x03A0}, + {0x03F0, 0x039A}, + {0x03F1, 0x03A1}, + {0x03F4, 0x0398}, + {0x03F5, 0x0395}, + {0x1E60, 0x1E61}, + {0x1E61, 0x1E9B}, + {0x1E9B, 0x1E60}, + {0x1E9E, 0x00DF}, + {0x1FBE, 0x0345}, + {0x2126, 0x03A9}, + {0x212A, 0x004B}, + {0x212B, 0x00C5}, + {0x2160, 0x2170}, + {0x2161, 0x2171}, + {0x2162, 0x2172}, + {0x2163, 0x2173}, + {0x2164, 0x2174}, + {0x2165, 0x2175}, + {0x2166, 0x2176}, + {0x2167, 0x2177}, + {0x2168, 0x2178}, + {0x2169, 0x2179}, + {0x216A, 0x217A}, + {0x216B, 0x217B}, + {0x216C, 0x217C}, + {0x216D, 0x217D}, + {0x216E, 0x217E}, + {0x216F, 0x217F}, + {0x2170, 0x2160}, + {0x2171, 0x2161}, + {0x2172, 0x2162}, + {0x2173, 0x2163}, + {0x2174, 0x2164}, + {0x2175, 0x2165}, + {0x2176, 0x2166}, + {0x2177, 0x2167}, + {0x2178, 0x2168}, + {0x2179, 0x2169}, + {0x217A, 0x216A}, + {0x217B, 0x216B}, + {0x217C, 0x216C}, + {0x217D, 0x216D}, + {0x217E, 0x216E}, + {0x217F, 0x216F}, + {0x24B6, 0x24D0}, + {0x24B7, 0x24D1}, + {0x24B8, 0x24D2}, + {0x24B9, 0x24D3}, + {0x24BA, 0x24D4}, + {0x24BB, 0x24D5}, + {0x24BC, 0x24D6}, + {0x24BD, 0x24D7}, + {0x24BE, 0x24D8}, + {0x24BF, 0x24D9}, + {0x24C0, 0x24DA}, + {0x24C1, 0x24DB}, + {0x24C2, 0x24DC}, + {0x24C3, 0x24DD}, + {0x24C4, 0x24DE}, + {0x24C5, 0x24DF}, + {0x24C6, 0x24E0}, + {0x24C7, 0x24E1}, + {0x24C8, 0x24E2}, + {0x24C9, 0x24E3}, + {0x24CA, 0x24E4}, + {0x24CB, 0x24E5}, + {0x24CC, 0x24E6}, + {0x24CD, 0x24E7}, + {0x24CE, 0x24E8}, + {0x24CF, 0x24E9}, + {0x24D0, 0x24B6}, + {0x24D1, 0x24B7}, + {0x24D2, 0x24B8}, + {0x24D3, 0x24B9}, + {0x24D4, 0x24BA}, + {0x24D5, 0x24BB}, + {0x24D6, 0x24BC}, + {0x24D7, 0x24BD}, + {0x24D8, 0x24BE}, + {0x24D9, 0x24BF}, + {0x24DA, 0x24C0}, + {0x24DB, 0x24C1}, + {0x24DC, 0x24C2}, + {0x24DD, 0x24C3}, + {0x24DE, 0x24C4}, + {0x24DF, 0x24C5}, + {0x24E0, 0x24C6}, + {0x24E1, 0x24C7}, + {0x24E2, 0x24C8}, + {0x24E3, 0x24C9}, + {0x24E4, 0x24CA}, + {0x24E5, 0x24CB}, + {0x24E6, 0x24CC}, + {0x24E7, 0x24CD}, + {0x24E8, 0x24CE}, + {0x24E9, 0x24CF}, +} + +// FoldCategory maps a category name to a table of +// code points outside the category that are equivalent under +// simple case folding to code points inside the category. +// If there is no entry for a category name, there are no such points. +var FoldCategory = map[string]*RangeTable{ + "Ll": foldLl, + "Inherited": foldInherited, + "M": foldM, + "L": foldL, + "Mn": foldMn, + "Common": foldCommon, + "Greek": foldGreek, + "Lu": foldLu, + "Lt": foldLt, +} + +var foldLl = &RangeTable{ + R16: []Range16{ + {0x0041, 0x005a, 1}, + {0x00c0, 0x00d6, 1}, + {0x00d8, 0x00de, 1}, + {0x0100, 0x012e, 2}, + {0x0132, 0x0136, 2}, + {0x0139, 0x0147, 2}, + {0x014a, 0x0178, 2}, + {0x0179, 0x017d, 2}, + {0x0181, 0x0182, 1}, + {0x0184, 0x0186, 2}, + {0x0187, 0x0189, 2}, + {0x018a, 0x018b, 1}, + {0x018e, 0x0191, 1}, + {0x0193, 0x0194, 1}, + {0x0196, 0x0198, 1}, + {0x019c, 0x019d, 1}, + {0x019f, 0x01a0, 1}, + {0x01a2, 0x01a6, 2}, + {0x01a7, 0x01a9, 2}, + {0x01ac, 0x01ae, 2}, + {0x01af, 0x01b1, 2}, + {0x01b2, 0x01b3, 1}, + {0x01b5, 0x01b7, 2}, + {0x01b8, 0x01bc, 4}, + {0x01c4, 0x01c5, 1}, + {0x01c7, 0x01c8, 1}, + {0x01ca, 0x01cb, 1}, + {0x01cd, 0x01db, 2}, + {0x01de, 0x01ee, 2}, + {0x01f1, 0x01f2, 1}, + {0x01f4, 0x01f6, 2}, + {0x01f7, 0x01f8, 1}, + {0x01fa, 0x0232, 2}, + {0x023a, 0x023b, 1}, + {0x023d, 0x023e, 1}, + {0x0241, 0x0243, 2}, + {0x0244, 0x0246, 1}, + {0x0248, 0x024e, 2}, + {0x0345, 0x0370, 43}, + {0x0372, 0x0376, 4}, + {0x0386, 0x0388, 2}, + {0x0389, 0x038a, 1}, + {0x038c, 0x038e, 2}, + {0x038f, 0x0391, 2}, + {0x0392, 0x03a1, 1}, + {0x03a3, 0x03ab, 1}, + {0x03cf, 0x03d8, 9}, + {0x03da, 0x03ee, 2}, + {0x03f4, 0x03f7, 3}, + {0x03f9, 0x03fa, 1}, + {0x03fd, 0x042f, 1}, + {0x0460, 0x0480, 2}, + {0x048a, 0x04c0, 2}, + {0x04c1, 0x04cd, 2}, + {0x04d0, 0x0526, 2}, + {0x0531, 0x0556, 1}, + {0x10a0, 0x10c5, 1}, + {0x1e00, 0x1e94, 2}, + {0x1e9e, 0x1efe, 2}, + {0x1f08, 0x1f0f, 1}, + {0x1f18, 0x1f1d, 1}, + {0x1f28, 0x1f2f, 1}, + {0x1f38, 0x1f3f, 1}, + {0x1f48, 0x1f4d, 1}, + {0x1f59, 0x1f5f, 2}, + {0x1f68, 0x1f6f, 1}, + {0x1f88, 0x1f8f, 1}, + {0x1f98, 0x1f9f, 1}, + {0x1fa8, 0x1faf, 1}, + {0x1fb8, 0x1fbc, 1}, + {0x1fc8, 0x1fcc, 1}, + {0x1fd8, 0x1fdb, 1}, + {0x1fe8, 0x1fec, 1}, + {0x1ff8, 0x1ffc, 1}, + {0x2126, 0x212a, 4}, + {0x212b, 0x2132, 7}, + {0x2183, 0x2c00, 2685}, + {0x2c01, 0x2c2e, 1}, + {0x2c60, 0x2c62, 2}, + {0x2c63, 0x2c64, 1}, + {0x2c67, 0x2c6d, 2}, + {0x2c6e, 0x2c70, 1}, + {0x2c72, 0x2c75, 3}, + {0x2c7e, 0x2c80, 1}, + {0x2c82, 0x2ce2, 2}, + {0x2ceb, 0x2ced, 2}, + {0xa640, 0xa66c, 2}, + {0xa680, 0xa696, 2}, + {0xa722, 0xa72e, 2}, + {0xa732, 0xa76e, 2}, + {0xa779, 0xa77d, 2}, + {0xa77e, 0xa786, 2}, + {0xa78b, 0xa78d, 2}, + {0xa790, 0xa7a0, 16}, + {0xa7a2, 0xa7a8, 2}, + {0xff21, 0xff3a, 1}, + }, + R32: []Range32{ + {0x10400, 0x10427, 1}, + }, +} + +var foldInherited = &RangeTable{ + R16: []Range16{ + {0x0399, 0x03b9, 32}, + {0x1fbe, 0x1fbe, 1}, + }, +} + +var foldM = &RangeTable{ + R16: []Range16{ + {0x0399, 0x03b9, 32}, + {0x1fbe, 0x1fbe, 1}, + }, +} + +var foldL = &RangeTable{ + R16: []Range16{ + {0x0345, 0x0345, 1}, + }, +} + +var foldMn = &RangeTable{ + R16: []Range16{ + {0x0399, 0x03b9, 32}, + {0x1fbe, 0x1fbe, 1}, + }, +} + +var foldCommon = &RangeTable{ + R16: []Range16{ + {0x039c, 0x03bc, 32}, + }, +} + +var foldGreek = &RangeTable{ + R16: []Range16{ + {0x00b5, 0x0345, 656}, + }, +} + +var foldLu = &RangeTable{ + R16: []Range16{ + {0x0061, 0x007a, 1}, + {0x00b5, 0x00df, 42}, + {0x00e0, 0x00f6, 1}, + {0x00f8, 0x00ff, 1}, + {0x0101, 0x012f, 2}, + {0x0133, 0x0137, 2}, + {0x013a, 0x0148, 2}, + {0x014b, 0x0177, 2}, + {0x017a, 0x017e, 2}, + {0x017f, 0x0180, 1}, + {0x0183, 0x0185, 2}, + {0x0188, 0x018c, 4}, + {0x0192, 0x0195, 3}, + {0x0199, 0x019a, 1}, + {0x019e, 0x01a1, 3}, + {0x01a3, 0x01a5, 2}, + {0x01a8, 0x01ad, 5}, + {0x01b0, 0x01b4, 4}, + {0x01b6, 0x01b9, 3}, + {0x01bd, 0x01bf, 2}, + {0x01c5, 0x01c6, 1}, + {0x01c8, 0x01c9, 1}, + {0x01cb, 0x01cc, 1}, + {0x01ce, 0x01dc, 2}, + {0x01dd, 0x01ef, 2}, + {0x01f2, 0x01f3, 1}, + {0x01f5, 0x01f9, 4}, + {0x01fb, 0x021f, 2}, + {0x0223, 0x0233, 2}, + {0x023c, 0x023f, 3}, + {0x0240, 0x0242, 2}, + {0x0247, 0x024f, 2}, + {0x0250, 0x0254, 1}, + {0x0256, 0x0257, 1}, + {0x0259, 0x025b, 2}, + {0x0260, 0x0263, 3}, + {0x0265, 0x0268, 3}, + {0x0269, 0x026b, 2}, + {0x026f, 0x0271, 2}, + {0x0272, 0x0275, 3}, + {0x027d, 0x0283, 3}, + {0x0288, 0x028c, 1}, + {0x0292, 0x0345, 179}, + {0x0371, 0x0373, 2}, + {0x0377, 0x037b, 4}, + {0x037c, 0x037d, 1}, + {0x03ac, 0x03af, 1}, + {0x03b1, 0x03ce, 1}, + {0x03d0, 0x03d1, 1}, + {0x03d5, 0x03d7, 1}, + {0x03d9, 0x03ef, 2}, + {0x03f0, 0x03f2, 1}, + {0x03f5, 0x03fb, 3}, + {0x0430, 0x045f, 1}, + {0x0461, 0x0481, 2}, + {0x048b, 0x04bf, 2}, + {0x04c2, 0x04ce, 2}, + {0x04cf, 0x0527, 2}, + {0x0561, 0x0586, 1}, + {0x1d79, 0x1d7d, 4}, + {0x1e01, 0x1e95, 2}, + {0x1e9b, 0x1ea1, 6}, + {0x1ea3, 0x1eff, 2}, + {0x1f00, 0x1f07, 1}, + {0x1f10, 0x1f15, 1}, + {0x1f20, 0x1f27, 1}, + {0x1f30, 0x1f37, 1}, + {0x1f40, 0x1f45, 1}, + {0x1f51, 0x1f57, 2}, + {0x1f60, 0x1f67, 1}, + {0x1f70, 0x1f7d, 1}, + {0x1fb0, 0x1fb1, 1}, + {0x1fbe, 0x1fd0, 18}, + {0x1fd1, 0x1fe0, 15}, + {0x1fe1, 0x1fe5, 4}, + {0x214e, 0x2184, 54}, + {0x2c30, 0x2c5e, 1}, + {0x2c61, 0x2c65, 4}, + {0x2c66, 0x2c6c, 2}, + {0x2c73, 0x2c76, 3}, + {0x2c81, 0x2ce3, 2}, + {0x2cec, 0x2cee, 2}, + {0x2d00, 0x2d25, 1}, + {0xa641, 0xa66d, 2}, + {0xa681, 0xa697, 2}, + {0xa723, 0xa72f, 2}, + {0xa733, 0xa76f, 2}, + {0xa77a, 0xa77c, 2}, + {0xa77f, 0xa787, 2}, + {0xa78c, 0xa791, 5}, + {0xa7a1, 0xa7a9, 2}, + {0xff41, 0xff5a, 1}, + }, + R32: []Range32{ + {0x10428, 0x1044f, 1}, + }, +} + +var foldLt = &RangeTable{ + R16: []Range16{ + {0x01c4, 0x01c6, 2}, + {0x01c7, 0x01c9, 2}, + {0x01ca, 0x01cc, 2}, + {0x01f1, 0x01f3, 2}, + {0x1f80, 0x1f87, 1}, + {0x1f90, 0x1f97, 1}, + {0x1fa0, 0x1fa7, 1}, + {0x1fb3, 0x1fc3, 16}, + {0x1ff3, 0x1ff3, 1}, + }, +} + +// FoldScript maps a script name to a table of +// code points outside the script that are equivalent under +// simple case folding to code points inside the script. +// If there is no entry for a script name, there are no such points. +var FoldScript = map[string]*RangeTable{} + + +// Range entries: 3391 16-bit, 659 32-bit, 4050 total. +// Range bytes: 20346 16-bit, 7908 32-bit, 28254 total. + +// Fold orbit bytes: 147 pairs, 588 bytes diff --git a/src/pkg/websocket/client.go b/src/pkg/websocket/client.go index e28382196..f066a1832 100644 --- a/src/pkg/websocket/client.go +++ b/src/pkg/websocket/client.go @@ -19,11 +19,13 @@ import ( ) type ProtocolError struct { - os.ErrorString + ErrorString string } +func (err *ProtocolError) String() string { return string(err.ErrorString) } + var ( - ErrBadScheme = os.ErrorString("bad scheme") + ErrBadScheme = &ProtocolError{"bad scheme"} ErrBadStatus = &ProtocolError{"bad status"} ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"} ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"} diff --git a/src/pkg/xml/Makefile b/src/pkg/xml/Makefile index b780face6..d66c4988a 100644 --- a/src/pkg/xml/Makefile +++ b/src/pkg/xml/Makefile @@ -7,6 +7,7 @@ include ../../Make.inc TARG=xml GOFILES=\ + marshal.go\ read.go\ xml.go\ diff --git a/src/pkg/xml/atom_test.go b/src/pkg/xml/atom_test.go new file mode 100644 index 000000000..d365510bf --- /dev/null +++ b/src/pkg/xml/atom_test.go @@ -0,0 +1,50 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xml + +var atomValue = &Feed{ + Title: "Example Feed", + Link: []Link{{Href: "http://example.org/"}}, + Updated: ParseTime("2003-12-13T18:30:02Z"), + Author: Person{Name: "John Doe"}, + Id: "urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6", + + Entry: []Entry{ + { + Title: "Atom-Powered Robots Run Amok", + Link: []Link{{Href: "http://example.org/2003/12/13/atom03"}}, + Id: "urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a", + Updated: ParseTime("2003-12-13T18:30:02Z"), + Summary: NewText("Some text."), + }, + }, +} + +var atomXml = `` + + `<feed xmlns="http://www.w3.org/2005/Atom">` + + `<Title>Example Feed</Title>` + + `<Id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</Id>` + + `<Link href="http://example.org/"></Link>` + + `<Updated>2003-12-13T18:30:02Z</Updated>` + + `<Author><Name>John Doe</Name><URI></URI><Email></Email></Author>` + + `<Entry>` + + `<Title>Atom-Powered Robots Run Amok</Title>` + + `<Id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</Id>` + + `<Link href="http://example.org/2003/12/13/atom03"></Link>` + + `<Updated>2003-12-13T18:30:02Z</Updated>` + + `<Author><Name></Name><URI></URI><Email></Email></Author>` + + `<Summary>Some text.</Summary>` + + `</Entry>` + + `</feed>` + +func ParseTime(str string) Time { + return Time(str) +} + +func NewText(text string) Text { + return Text{ + Body: text, + } +} diff --git a/src/pkg/xml/embed_test.go b/src/pkg/xml/embed_test.go index abfe781ac..ec7f478be 100644 --- a/src/pkg/xml/embed_test.go +++ b/src/pkg/xml/embed_test.go @@ -12,14 +12,14 @@ type C struct { } type A struct { - XMLName Name "http://domain a" + XMLName Name `xml:"http://domain a"` C B B FieldA string } type B struct { - XMLName Name "b" + XMLName Name `xml:"b"` C FieldB string } @@ -65,7 +65,7 @@ func TestEmbedded1(t *testing.T) { } type A2 struct { - XMLName Name "http://domain a" + XMLName Name `xml:"http://domain a"` XY string Xy string } @@ -92,7 +92,7 @@ func TestEmbedded2(t *testing.T) { } type A3 struct { - XMLName Name "http://domain a" + XMLName Name `xml:"http://domain a"` xy string } @@ -108,7 +108,7 @@ func TestEmbedded3(t *testing.T) { } type A4 struct { - XMLName Name "http://domain a" + XMLName Name `xml:"http://domain a"` Any string } diff --git a/src/pkg/xml/marshal.go b/src/pkg/xml/marshal.go new file mode 100644 index 000000000..2ac03a91e --- /dev/null +++ b/src/pkg/xml/marshal.go @@ -0,0 +1,228 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xml + +import ( + "bufio" + "io" + "os" + "reflect" + "strconv" + "strings" +) + +const ( + // A generic XML header suitable for use with the output of Marshal and MarshalIndent. + // This is not automatically added to any output of this package, it is provided as a + // convenience. + Header = `<?xml version="1.0" encoding="UTF-8">\n` +) + +// A Marshaler can produce well-formatted XML representing its internal state. +// It is used by both Marshal and MarshalIndent. +type Marshaler interface { + MarshalXML() ([]byte, os.Error) +} + +type printer struct { + *bufio.Writer +} + +// Marshal writes an XML-formatted representation of v to w. +// +// If v implements Marshaler, then Marshal calls its MarshalXML method. +// Otherwise, Marshal uses the following procedure to create the XML. +// +// Marshal handles an array or slice by marshalling each of the elements. +// Marshal handles a pointer by marshalling the value it points at or, if the +// pointer is nil, by writing nothing. Marshal handles an interface value by +// marshalling the value it contains or, if the interface value is nil, by +// writing nothing. Marshal handles all other data by writing a single XML +// element containing the data. +// +// The name of that XML element is taken from, in order of preference: +// - the tag on an XMLName field, if the data is a struct +// - the value of an XMLName field of type xml.Name +// - the tag of the struct field used to obtain the data +// - the name of the struct field used to obtain the data +// - the name '???'. +// +// The XML element for a struct contains marshalled elements for each of the +// exported fields of the struct, with these exceptions: +// - the XMLName field, described above, is omitted. +// - a field with tag "attr" becomes an attribute in the XML element. +// - a field with tag "chardata" is written as character data, +// not as an XML element. +// - a field with tag "innerxml" is written verbatim, +// not subject to the usual marshalling procedure. +// +// Marshal will return an error if asked to marshal a channel, function, or map. +func Marshal(w io.Writer, v interface{}) (err os.Error) { + p := &printer{bufio.NewWriter(w)} + err = p.marshalValue(reflect.ValueOf(v), "???") + p.Flush() + return err +} + +func (p *printer) marshalValue(val reflect.Value, name string) os.Error { + if !val.IsValid() { + return nil + } + + kind := val.Kind() + typ := val.Type() + + // Try Marshaler + if typ.NumMethod() > 0 { + if marshaler, ok := val.Interface().(Marshaler); ok { + bytes, err := marshaler.MarshalXML() + if err != nil { + return err + } + p.Write(bytes) + return nil + } + } + + // Drill into pointers/interfaces + if kind == reflect.Ptr || kind == reflect.Interface { + if val.IsNil() { + return nil + } + return p.marshalValue(val.Elem(), name) + } + + // Slices and arrays iterate over the elements. They do not have an enclosing tag. + if (kind == reflect.Slice || kind == reflect.Array) && typ.Elem().Kind() != reflect.Uint8 { + for i, n := 0, val.Len(); i < n; i++ { + if err := p.marshalValue(val.Index(i), name); err != nil { + return err + } + } + return nil + } + + // Find XML name + xmlns := "" + if kind == reflect.Struct { + if f, ok := typ.FieldByName("XMLName"); ok { + if tag := f.Tag.Get("xml"); tag != "" { + if i := strings.Index(tag, " "); i >= 0 { + xmlns, name = tag[:i], tag[i+1:] + } else { + name = tag + } + } else if v, ok := val.FieldByIndex(f.Index).Interface().(Name); ok && v.Local != "" { + xmlns, name = v.Space, v.Local + } + } + } + + p.WriteByte('<') + p.WriteString(name) + + // Attributes + if kind == reflect.Struct { + if len(xmlns) > 0 { + p.WriteString(` xmlns="`) + Escape(p, []byte(xmlns)) + p.WriteByte('"') + } + + for i, n := 0, typ.NumField(); i < n; i++ { + if f := typ.Field(i); f.PkgPath == "" && f.Tag.Get("xml") == "attr" { + if f.Type.Kind() == reflect.String { + if str := val.Field(i).String(); str != "" { + p.WriteByte(' ') + p.WriteString(strings.ToLower(f.Name)) + p.WriteString(`="`) + Escape(p, []byte(str)) + p.WriteByte('"') + } + } + } + } + } + p.WriteByte('>') + + switch k := val.Kind(); k { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + p.WriteString(strconv.Itoa64(val.Int())) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p.WriteString(strconv.Uitoa64(val.Uint())) + case reflect.Float32, reflect.Float64: + p.WriteString(strconv.Ftoa64(val.Float(), 'g', -1)) + case reflect.String: + Escape(p, []byte(val.String())) + case reflect.Bool: + p.WriteString(strconv.Btoa(val.Bool())) + case reflect.Array: + // will be [...]byte + bytes := make([]byte, val.Len()) + for i := range bytes { + bytes[i] = val.Index(i).Interface().(byte) + } + Escape(p, bytes) + case reflect.Slice: + // will be []byte + bytes := val.Interface().([]byte) + Escape(p, bytes) + case reflect.Struct: + for i, n := 0, val.NumField(); i < n; i++ { + if f := typ.Field(i); f.Name != "XMLName" && f.PkgPath == "" { + name := f.Name + switch tag := f.Tag.Get("xml"); tag { + case "": + case "chardata": + if tk := f.Type.Kind(); tk == reflect.String { + p.Write([]byte(val.Field(i).String())) + } else if tk == reflect.Slice { + if elem, ok := val.Field(i).Interface().([]byte); ok { + Escape(p, elem) + } + } + continue + case "innerxml": + iface := val.Field(i).Interface() + switch raw := iface.(type) { + case []byte: + p.Write(raw) + continue + case string: + p.WriteString(raw) + continue + } + case "attr": + continue + default: + name = tag + } + + if err := p.marshalValue(val.Field(i), name); err != nil { + return err + } + } + } + default: + return &UnsupportedTypeError{typ} + } + + p.WriteByte('<') + p.WriteByte('/') + p.WriteString(name) + p.WriteByte('>') + + return nil +} + +// A MarshalXMLError is returned when Marshal or MarshalIndent encounter a type +// that cannot be converted into XML. +type UnsupportedTypeError struct { + Type reflect.Type +} + +func (e *UnsupportedTypeError) String() string { + return "xml: unsupported type: " + e.Type.String() +} diff --git a/src/pkg/xml/marshal_test.go b/src/pkg/xml/marshal_test.go new file mode 100644 index 000000000..77b2e726d --- /dev/null +++ b/src/pkg/xml/marshal_test.go @@ -0,0 +1,299 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xml + +import ( + "reflect" + "testing" + + "os" + "bytes" + "strings" + "strconv" +) + +type DriveType int + +const ( + HyperDrive DriveType = iota + ImprobabilityDrive +) + +type Passenger struct { + Name []string `xml:"name"` + Weight float32 `xml:"weight"` +} + +type Ship struct { + XMLName Name `xml:"spaceship"` + + Name string `xml:"attr"` + Pilot string `xml:"attr"` + Drive DriveType `xml:"drive"` + Age uint `xml:"age"` + Passenger []*Passenger `xml:"passenger"` + secret string +} + +type RawXML string + +func (rx RawXML) MarshalXML() ([]byte, os.Error) { + return []byte(rx), nil +} + +type NamedType string + +type Port struct { + XMLName Name `xml:"port"` + Type string `xml:"attr"` + Number string `xml:"chardata"` +} + +type Domain struct { + XMLName Name `xml:"domain"` + Country string `xml:"attr"` + Name []byte `xml:"chardata"` +} + +type SecretAgent struct { + XMLName Name `xml:"agent"` + Handle string `xml:"attr"` + Identity string + Obfuscate string `xml:"innerxml"` +} + +var nilStruct *Ship + +var marshalTests = []struct { + Value interface{} + ExpectXML string +}{ + // Test nil marshals to nothing + {Value: nil, ExpectXML: ``}, + {Value: nilStruct, ExpectXML: ``}, + + // Test value types (no tag name, so ???) + {Value: true, ExpectXML: `<???>true</???>`}, + {Value: int(42), ExpectXML: `<???>42</???>`}, + {Value: int8(42), ExpectXML: `<???>42</???>`}, + {Value: int16(42), ExpectXML: `<???>42</???>`}, + {Value: int32(42), ExpectXML: `<???>42</???>`}, + {Value: uint(42), ExpectXML: `<???>42</???>`}, + {Value: uint8(42), ExpectXML: `<???>42</???>`}, + {Value: uint16(42), ExpectXML: `<???>42</???>`}, + {Value: uint32(42), ExpectXML: `<???>42</???>`}, + {Value: float32(1.25), ExpectXML: `<???>1.25</???>`}, + {Value: float64(1.25), ExpectXML: `<???>1.25</???>`}, + {Value: uintptr(0xFFDD), ExpectXML: `<???>65501</???>`}, + {Value: "gopher", ExpectXML: `<???>gopher</???>`}, + {Value: []byte("gopher"), ExpectXML: `<???>gopher</???>`}, + {Value: "</>", ExpectXML: `<???></></???>`}, + {Value: []byte("</>"), ExpectXML: `<???></></???>`}, + {Value: [3]byte{'<', '/', '>'}, ExpectXML: `<???></></???>`}, + {Value: NamedType("potato"), ExpectXML: `<???>potato</???>`}, + {Value: []int{1, 2, 3}, ExpectXML: `<???>1</???><???>2</???><???>3</???>`}, + {Value: [3]int{1, 2, 3}, ExpectXML: `<???>1</???><???>2</???><???>3</???>`}, + + // Test innerxml + {Value: RawXML("</>"), ExpectXML: `</>`}, + { + Value: &SecretAgent{ + Handle: "007", + Identity: "James Bond", + Obfuscate: "<redacted/>", + }, + //ExpectXML: `<agent handle="007"><redacted/></agent>`, + ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`, + }, + + // Test structs + {Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl">443</port>`}, + {Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`}, + {Value: &Port{Type: "<unix>"}, ExpectXML: `<port type="<unix>"></port>`}, + {Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&friends</domain>`}, + {Value: atomValue, ExpectXML: atomXml}, + { + Value: &Ship{ + Name: "Heart of Gold", + Pilot: "Computer", + Age: 1, + Drive: ImprobabilityDrive, + Passenger: []*Passenger{ + &Passenger{ + Name: []string{"Zaphod", "Beeblebrox"}, + Weight: 7.25, + }, + &Passenger{ + Name: []string{"Trisha", "McMillen"}, + Weight: 5.5, + }, + &Passenger{ + Name: []string{"Ford", "Prefect"}, + Weight: 7, + }, + &Passenger{ + Name: []string{"Arthur", "Dent"}, + Weight: 6.75, + }, + }, + }, + ExpectXML: `<spaceship name="Heart of Gold" pilot="Computer">` + + `<drive>` + strconv.Itoa(int(ImprobabilityDrive)) + `</drive>` + + `<age>1</age>` + + `<passenger>` + + `<name>Zaphod</name>` + + `<name>Beeblebrox</name>` + + `<weight>7.25</weight>` + + `</passenger>` + + `<passenger>` + + `<name>Trisha</name>` + + `<name>McMillen</name>` + + `<weight>5.5</weight>` + + `</passenger>` + + `<passenger>` + + `<name>Ford</name>` + + `<name>Prefect</name>` + + `<weight>7</weight>` + + `</passenger>` + + `<passenger>` + + `<name>Arthur</name>` + + `<name>Dent</name>` + + `<weight>6.75</weight>` + + `</passenger>` + + `</spaceship>`, + }, +} + +func TestMarshal(t *testing.T) { + for idx, test := range marshalTests { + buf := bytes.NewBuffer(nil) + err := Marshal(buf, test.Value) + if err != nil { + t.Errorf("#%d: Error: %s", idx, err) + continue + } + if got, want := buf.String(), test.ExpectXML; got != want { + if strings.Contains(want, "\n") { + t.Errorf("#%d: marshal(%#v) - GOT:\n%s\nWANT:\n%s", idx, test.Value, got, want) + } else { + t.Errorf("#%d: marshal(%#v) = %#q want %#q", idx, test.Value, got, want) + } + } + } +} + +var marshalErrorTests = []struct { + Value interface{} + ExpectErr string + ExpectKind reflect.Kind +}{ + { + Value: make(chan bool), + ExpectErr: "xml: unsupported type: chan bool", + ExpectKind: reflect.Chan, + }, + { + Value: map[string]string{ + "question": "What do you get when you multiply six by nine?", + "answer": "42", + }, + ExpectErr: "xml: unsupported type: map[string] string", + ExpectKind: reflect.Map, + }, + { + Value: map[*Ship]bool{nil: false}, + ExpectErr: "xml: unsupported type: map[*xml.Ship] bool", + ExpectKind: reflect.Map, + }, +} + +func TestMarshalErrors(t *testing.T) { + for idx, test := range marshalErrorTests { + buf := bytes.NewBuffer(nil) + err := Marshal(buf, test.Value) + if got, want := err, test.ExpectErr; got == nil { + t.Errorf("#%d: want error %s", idx, want) + continue + } else if got.String() != want { + t.Errorf("#%d: marshal(%#v) = [error] %q, want %q", idx, test.Value, got, want) + } + if got, want := err.(*UnsupportedTypeError).Type.Kind(), test.ExpectKind; got != want { + t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, got, want) + } + } +} + +// Do invertibility testing on the various structures that we test +func TestUnmarshal(t *testing.T) { + for i, test := range marshalTests { + // Skip the nil pointers + if i <= 1 { + continue + } + + var dest interface{} + + switch test.Value.(type) { + case *Ship, Ship: + dest = &Ship{} + case *Port, Port: + dest = &Port{} + case *Domain, Domain: + dest = &Domain{} + case *Feed, Feed: + dest = &Feed{} + default: + continue + } + + buffer := bytes.NewBufferString(test.ExpectXML) + err := Unmarshal(buffer, dest) + + // Don't compare XMLNames + switch fix := dest.(type) { + case *Ship: + fix.XMLName = Name{} + case *Port: + fix.XMLName = Name{} + case *Domain: + fix.XMLName = Name{} + case *Feed: + fix.XMLName = Name{} + fix.Author.InnerXML = "" + for i := range fix.Entry { + fix.Entry[i].Author.InnerXML = "" + } + } + + if err != nil { + t.Errorf("#%d: unexpected error: %#v", i, err) + } else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) { + t.Errorf("#%d: unmarshal(%#s) = %#v, want %#v", i, test.ExpectXML, got, want) + } + } +} + +func BenchmarkMarshal(b *testing.B) { + idx := len(marshalTests) - 1 + test := marshalTests[idx] + + buf := bytes.NewBuffer(nil) + for i := 0; i < b.N; i++ { + Marshal(buf, test.Value) + buf.Truncate(0) + } +} + +func BenchmarkUnmarshal(b *testing.B) { + idx := len(marshalTests) - 1 + test := marshalTests[idx] + sm := &Ship{} + xml := []byte(test.ExpectXML) + + for i := 0; i < b.N; i++ { + buffer := bytes.NewBuffer(xml) + Unmarshal(buffer, sm) + } +} diff --git a/src/pkg/xml/read.go b/src/pkg/xml/read.go index e2b349c3f..786b69f5a 100644 --- a/src/pkg/xml/read.go +++ b/src/pkg/xml/read.go @@ -31,16 +31,16 @@ import ( // For example, given these definitions: // // type Email struct { -// Where string "attr" +// Where string `xml:"attr"` // Addr string // } // // type Result struct { -// XMLName xml.Name "result" +// XMLName xml.Name `xml:"result"` // Name string // Phone string // Email []Email -// Groups []string "group>value" +// Groups []string `xml:"group>value"` // } // // result := Result{Name: "name", Phone: "phone", Email: nil} @@ -79,11 +79,13 @@ import ( // Groups was assigned considering the element path provided in the // field tag. // -// Because Unmarshal uses the reflect package, it can only -// assign to upper case fields. Unmarshal uses a case-insensitive +// Because Unmarshal uses the reflect package, it can only assign +// to exported (upper case) fields. Unmarshal uses a case-insensitive // comparison to match XML element names to struct field names. // -// Unmarshal maps an XML element to a struct using the following rules: +// Unmarshal maps an XML element to a struct using the following rules. +// In the rules, the tag of a field refers to the value associated with the +// key 'xml' in the struct field's tag (see the example above). // // * If the struct has a field of type []byte or string with tag "innerxml", // Unmarshal accumulates the raw XML nested inside the element @@ -92,9 +94,9 @@ import ( // * If the struct has a field named XMLName of type xml.Name, // Unmarshal records the element name in that field. // -// * If the XMLName field has an associated tag string of the form -// "tag" or "namespace-URL tag", the XML element must have -// the given tag (and, optionally, name space) or else Unmarshal +// * If the XMLName field has an associated tag of the form +// "name" or "namespace-URL name", the XML element must have +// the given name (and, optionally, name space) or else Unmarshal // returns an error. // // * If the XML element has an attribute whose name matches a @@ -106,31 +108,41 @@ import ( // The struct field may have type []byte or string. // If there is no such field, the character data is discarded. // +// * If the XML element contains comments, they are accumulated in +// the first struct field that has tag "comments". The struct +// field may have type []byte or string. If there is no such +// field, the comments are discarded. +// // * If the XML element contains a sub-element whose name matches -// the prefix of a struct field tag formatted as "a>b>c", unmarshal +// the prefix of a tag formatted as "a>b>c", unmarshal // will descend into the XML structure looking for elements with the // given names, and will map the innermost elements to that struct field. -// A struct field tag starting with ">" is equivalent to one starting +// A tag starting with ">" is equivalent to one starting // with the field name followed by ">". // // * If the XML element contains a sub-element whose name -// matches a struct field whose tag is neither "attr" nor "chardata", +// matches a field whose tag is neither "attr" nor "chardata", // Unmarshal maps the sub-element to that struct field. // Otherwise, if the struct has a field named Any, unmarshal // maps the sub-element to that struct field. // // Unmarshal maps an XML element to a string or []byte by saving the -// concatenation of that element's character data in the string or []byte. +// concatenation of that element's character data in the string or +// []byte. +// +// Unmarshal maps an attribute value to a string or []byte by saving +// the value in the string or slice. // -// Unmarshal maps an XML element to a slice by extending the length -// of the slice and mapping the element to the newly created value. +// Unmarshal maps an XML element to a slice by extending the length of +// the slice and mapping the element to the newly created value. // -// Unmarshal maps an XML element to a bool by setting it to the boolean -// value represented by the string. +// Unmarshal maps an XML element or attribute value to a bool by +// setting it to the boolean value represented by the string. // -// Unmarshal maps an XML element to an integer or floating-point -// field by setting the field to the result of interpreting the string -// value in decimal. There is no check for overflow. +// Unmarshal maps an XML element or attribute value to an integer or +// floating-point field by setting the field to the result of +// interpreting the string value in decimal. There is no check for +// overflow. // // Unmarshal maps an XML element to an xml.Name by recording the // element name. @@ -241,7 +253,7 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { switch v := val; v.Kind() { default: - return os.ErrorString("unknown type " + v.Type().String()) + return os.NewError("unknown type " + v.Type().String()) case reflect.Slice: typ := v.Type() @@ -287,8 +299,7 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { // Assign name. if f, ok := typ.FieldByName("XMLName"); ok { // Validate element name. - if f.Tag != "" { - tag := f.Tag + if tag := f.Tag.Get("xml"); tag != "" { ns := "" i := strings.LastIndex(tag, " ") if i >= 0 { @@ -320,12 +331,9 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { // Also, determine whether we need to save character data or comments. for i, n := 0, typ.NumField(); i < n; i++ { f := typ.Field(i) - switch f.Tag { + switch f.Tag.Get("xml") { case "attr": strv := sv.FieldByIndex(f.Index) - if strv.Kind() != reflect.String { - return UnmarshalError(sv.Type().String() + " field " + f.Name + " has attr tag but is not type string") - } // Look for attribute. val := "" k := strings.ToLower(f.Name) @@ -335,7 +343,7 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { break } } - strv.SetString(val) + copyValue(strv, []byte(val)) case "comment": if !saveComment.IsValid() { @@ -359,15 +367,15 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { } default: - if strings.Contains(f.Tag, ">") { + if tag := f.Tag.Get("xml"); strings.Contains(tag, ">") { if fieldPaths == nil { fieldPaths = make(map[string]pathInfo) } - path := strings.ToLower(f.Tag) - if strings.HasPrefix(f.Tag, ">") { + path := strings.ToLower(tag) + if strings.HasPrefix(tag, ">") { path = strings.ToLower(f.Name) + path } - if strings.HasSuffix(f.Tag, ">") { + if strings.HasSuffix(tag, ">") { path = path[:len(path)-1] } err := addFieldPath(sv, fieldPaths, path, f.Index) @@ -454,33 +462,54 @@ Loop: } } - var err os.Error + if err := copyValue(saveData, data); err != nil { + return err + } + + switch t := saveComment; t.Kind() { + case reflect.String: + t.SetString(string(comment)) + case reflect.Slice: + t.Set(reflect.ValueOf(comment)) + } + + switch t := saveXML; t.Kind() { + case reflect.String: + t.SetString(string(saveXMLData)) + case reflect.Slice: + t.Set(reflect.ValueOf(saveXMLData)) + } + + return nil +} + +func copyValue(dst reflect.Value, src []byte) (err os.Error) { // Helper functions for integer and unsigned integer conversions var itmp int64 getInt64 := func() bool { - itmp, err = strconv.Atoi64(string(data)) + itmp, err = strconv.Atoi64(string(src)) // TODO: should check sizes return err == nil } var utmp uint64 getUint64 := func() bool { - utmp, err = strconv.Atoui64(string(data)) + utmp, err = strconv.Atoui64(string(src)) // TODO: check for overflow? return err == nil } var ftmp float64 getFloat64 := func() bool { - ftmp, err = strconv.Atof64(string(data)) + ftmp, err = strconv.Atof64(string(src)) // TODO: check for overflow? return err == nil } // Save accumulated data and comments - switch t := saveData; t.Kind() { + switch t := dst; t.Kind() { case reflect.Invalid: // Probably a comment, handled below default: - return os.ErrorString("cannot happen: unknown type " + t.Type().String()) + return os.NewError("cannot happen: unknown type " + t.Type().String()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if !getInt64() { return err @@ -497,31 +526,16 @@ Loop: } t.SetFloat(ftmp) case reflect.Bool: - value, err := strconv.Atob(strings.TrimSpace(string(data))) + value, err := strconv.Atob(strings.TrimSpace(string(src))) if err != nil { return err } t.SetBool(value) case reflect.String: - t.SetString(string(data)) + t.SetString(string(src)) case reflect.Slice: - t.Set(reflect.ValueOf(data)) + t.Set(reflect.ValueOf(src)) } - - switch t := saveComment; t.Kind() { - case reflect.String: - t.SetString(string(comment)) - case reflect.Slice: - t.Set(reflect.ValueOf(comment)) - } - - switch t := saveXML; t.Kind() { - case reflect.String: - t.SetString(string(saveXMLData)) - case reflect.Slice: - t.Set(reflect.ValueOf(saveXMLData)) - } - return nil } @@ -561,7 +575,7 @@ func tagError(sv reflect.Value, idx1 []int, idx2 []int) os.Error { t := sv.Type() f1 := t.FieldByIndex(idx1) f2 := t.FieldByIndex(idx2) - return &TagPathError{t, f1.Name, f1.Tag, f2.Name, f2.Tag} + return &TagPathError{t, f1.Name, f1.Tag.Get("xml"), f2.Name, f2.Tag.Get("xml")} } // unmarshalPaths walks down an XML structure looking for diff --git a/src/pkg/xml/read_test.go b/src/pkg/xml/read_test.go index d4ae3700d..2126da3c7 100644 --- a/src/pkg/xml/read_test.go +++ b/src/pkg/xml/read_test.go @@ -78,7 +78,7 @@ not being used from outside intra_region_diff.py. </summary></entry></feed> ` type Feed struct { - XMLName Name "http://www.w3.org/2005/Atom feed" + XMLName Name `xml:"http://www.w3.org/2005/Atom feed"` Title string Id string Link []Link @@ -97,20 +97,20 @@ type Entry struct { } type Link struct { - Rel string "attr" - Href string "attr" + Rel string `xml:"attr"` + Href string `xml:"attr"` } type Person struct { Name string URI string Email string - InnerXML string "innerxml" + InnerXML string `xml:"innerxml"` } type Text struct { - Type string "attr" - Body string "chardata" + Type string `xml:"attr"` + Body string `xml:"chardata"` } type Time string @@ -255,18 +255,18 @@ type PathTestItem struct { } type PathTestA struct { - Items []PathTestItem ">item1" + Items []PathTestItem `xml:">item1"` Before, After string } type PathTestB struct { - Other []PathTestItem "items>Item1" + Other []PathTestItem `xml:"items>Item1"` Before, After string } type PathTestC struct { - Values1 []string "items>item1>value" - Values2 []string "items>item2>value" + Values1 []string `xml:"items>item1>value"` + Values2 []string `xml:"items>item2>value"` Before, After string } @@ -275,7 +275,7 @@ type PathTestSet struct { } type PathTestD struct { - Other PathTestSet "items>" + Other PathTestSet `xml:"items>"` Before, After string } @@ -299,15 +299,15 @@ func TestUnmarshalPaths(t *testing.T) { } type BadPathTestA struct { - First string "items>item1" - Other string "items>item2" - Second string "items>" + First string `xml:"items>item1"` + Other string `xml:"items>item2"` + Second string `xml:"items>"` } type BadPathTestB struct { - Other string "items>item2>value" - First string "items>item1" - Second string "items>item1>value" + Other string `xml:"items>item2>value"` + First string `xml:"items>item1"` + Second string `xml:"items>item1>value"` } var badPathTests = []struct { @@ -325,3 +325,47 @@ func TestUnmarshalBadPaths(t *testing.T) { } } } + +func TestUnmarshalAttrs(t *testing.T) { + var f AttrTest + if err := Unmarshal(StringReader(attrString), &f); err != nil { + t.Fatalf("Unmarshal: %s", err) + } + if !reflect.DeepEqual(f, attrStruct) { + t.Fatalf("have %#v\nwant %#v", f, attrStruct) + } +} + +type AttrTest struct { + Test1 Test1 + Test2 Test2 +} + +type Test1 struct { + Int int `xml:"attr"` + Float float64 `xml:"attr"` + Uint8 uint8 `xml:"attr"` +} + +type Test2 struct { + Bool bool `xml:"attr"` +} + +const attrString = ` +<?xml version="1.0" charset="utf-8"?> +<attrtest> + <test1 int="8" float="23.5" uint8="255"/> + <test2 bool="true"/> +</attrtest> +` + +var attrStruct = AttrTest{ + Test1: Test1{ + Int: 8, + Float: 23.5, + Uint8: 255, + }, + Test2: Test2{ + Bool: true, + }, +} diff --git a/src/pkg/xml/xml.go b/src/pkg/xml/xml.go index 2cebbce75..e5d73dd02 100644 --- a/src/pkg/xml/xml.go +++ b/src/pkg/xml/xml.go @@ -659,17 +659,22 @@ func (p *Parser) RawToken() (Token, os.Error) { return nil, p.err } if b != '=' { - p.err = p.syntaxError("attribute name without = in element") - return nil, p.err - } - p.space() - data := p.attrval() - if data == nil { - return nil, p.err + if p.Strict { + p.err = p.syntaxError("attribute name without = in element") + return nil, p.err + } else { + p.ungetc(b) + a.Value = a.Name.Local + } + } else { + p.space() + data := p.attrval() + if data == nil { + return nil, p.err + } + a.Value = string(data) } - a.Value = string(data) } - if empty { p.needClose = true p.toClose = name diff --git a/src/pkg/xml/xml_test.go b/src/pkg/xml/xml_test.go index 4e51cd53a..aba21a2b4 100644 --- a/src/pkg/xml/xml_test.go +++ b/src/pkg/xml/xml_test.go @@ -445,6 +445,33 @@ func TestUnquotedAttrs(t *testing.T) { } } +func TestValuelessAttrs(t *testing.T) { + tests := [][3]string{ + {"<p nowrap>", "p", "nowrap"}, + {"<p nowrap >", "p", "nowrap"}, + {"<input checked/>", "input", "checked"}, + {"<input checked />", "input", "checked"}, + } + for _, test := range tests { + p := NewParser(StringReader(test[0])) + p.Strict = false + token, err := p.Token() + if _, ok := err.(*SyntaxError); ok { + t.Errorf("Unexpected error: %v", err) + } + if token.(StartElement).Name.Local != test[1] { + t.Errorf("Unexpected tag name: %v", token.(StartElement).Name.Local) + } + attr := token.(StartElement).Attr[0] + if attr.Value != test[2] { + t.Errorf("Unexpected attribute value: %v", attr.Value) + } + if attr.Name.Local != test[2] { + t.Errorf("Unexpected attribute name: %v", attr.Name.Local) + } + } +} + func TestCopyTokenCharData(t *testing.T) { data := []byte("same data") var tok1 Token = CharData(data) diff --git a/src/run.bash b/src/run.bash index bb3d06c45..d125fd454 100755 --- a/src/run.bash +++ b/src/run.bash @@ -34,7 +34,7 @@ if $rebuild; then (xcd pkg gomake clean time gomake install - ) || exit $i + ) || exit $? fi (xcd pkg diff --git a/src/version.bash b/src/version.bash index b45f15a6c..ce5a9969a 100755 --- a/src/version.bash +++ b/src/version.bash @@ -16,8 +16,15 @@ if [ $? != 0 ]; then VERSION=$(echo $OLD | awk '{print $1}') fi -# Find most recent known release tag. +# Get branch type +BRANCH=release +if [ "$(hg identify -b 2>/dev/null)" == "default" ]; then + BRANCH=weekly +fi + +# Find most recent known release or weekly tag. TAG=$(hg tags | + grep $BRANCH | sed 's/:.*//' | sort -rn -k2 | awk -v ver=$VERSION '$2 <= ver && $1~/^(release|weekly)\./ {print $1}' | |