echo back context of call in error if likely to be useful.
For example, if os.Open("/etc/passwd", os.O_RDONLY)
fails with syscall.EPERM, it returns as the os.Error
&PathError{
Op: "open",
Path: "/etc/passwd"
Error: os.EPERM
}
which formats as
open /etc/passwd: permission denied
Not converted:
datafmt
go/...
google/...
regexp
tabwriter
template
R=r
DELTA=1153 (561 added, 156 deleted, 436 changed)
OCL=30738
CL=30781
func Open(name string, mode int, perm int) (file *File, err os.Error) {
r, e := syscall.Open(name, mode, perm);
- return newFile(r, name), os.ErrnoToError(e)
+ if e != 0 {
+ err = os.Errno(e);
+ }
+ return newFile(r, name), err
}
func (file *File) Close() os.Error {
}
e := syscall.Close(file.fd);
file.fd = -1; // so it can't be closed again
- return os.ErrnoToError(e);
+ if e != 0 {
+ return os.Errno(e);
+ }
+ return nil
}
func (file *File) Read(b []byte) (ret int, err os.Error) {
return -1, os.EINVAL
}
r, e := syscall.Read(file.fd, b);
- return int(r), os.ErrnoToError(e)
+ if e != 0 {
+ err = os.Errno(e);
+ }
+ return int(r), err
}
func (file *File) Write(b []byte) (ret int, err os.Error) {
return -1, os.EINVAL
}
r, e := syscall.Write(file.fd, b);
- return int(r), os.ErrnoToError(e)
+ if e != 0 {
+ err = os.Errno(e);
+ }
+ return int(r), err
}
func (file *File) String() string {
if err != nil {
fatal("run /bin/rm: %v", err);
}
- w, err1 := p.Wait(0);
- if err1 != nil {
+ w, err := p.Wait(0);
+ if err != nil {
fatal("wait /bin/rm: %v", err);
}
if !w.Exited() || w.ExitStatus() != 0 {
archive/tar.install: bufio.install bytes.install io.install os.install strconv.install
base64.install: bytes.install io.install os.install strconv.install
bignum.install: fmt.install
-bufio.install: io.install os.install utf8.install
+bufio.install: io.install os.install strconv.install utf8.install
bytes.install: utf8.install
compress/flate.install: bufio.install io.install os.install strconv.install
compress/gzip.install: bufio.install compress/flate.install hash.install hash/crc32.install io.install os.install
container/list.install:
container/vector.install:
-crypto/aes.install: os.install
-crypto/block.install: fmt.install io.install os.install
+crypto/aes.install: os.install strconv.install
+crypto/block.install: fmt.install io.install os.install strconv.install
crypto/hmac.install: crypto/md5.install crypto/sha1.install hash.install os.install
crypto/md5.install: hash.install os.install
crypto/sha1.install: hash.install os.install
hash.install: io.install
hash/adler32.install: hash.install os.install
hash/crc32.install: hash.install os.install
-http.install: bufio.install container/vector.install fmt.install io.install log.install net.install os.install path.install strconv.install strings.install utf8.install
+http.install: bufio.install bytes.install container/vector.install fmt.install io.install log.install net.install os.install path.install strconv.install strings.install utf8.install
io.install: bytes.install os.install sync.install
json.install: container/vector.install fmt.install io.install math.install reflect.install strconv.install strings.install utf8.install
log.install: fmt.install io.install os.install runtime.install time.install
import (
"io";
"os";
+ "strconv";
"utf8";
)
}
var (
- PhaseError os.Error = &Error{"bufio: phase error"};
- BufferFull os.Error = &Error{"bufio: buffer full"};
- InternalError os.Error = &Error{"bufio: internal error"};
- BadBufSize os.Error = &Error{"bufio: bad buffer size"};
+ ErrInvalidUnreadByte os.Error = &Error{"bufio: invalid use of UnreadByte"};
+ ErrBufferFull os.Error = &Error{"bufio: buffer full"};
+ errInternal os.Error = &Error{"bufio: internal error"};
)
+// BufSizeError is the error representing an invalid buffer size.
+type BufSizeError int
+func (b BufSizeError) String() string {
+ return "bufio: bad buffer size " + strconv.Itoa(int(b));
+}
+
func copySlice(dst []byte, src []byte) {
for i := 0; i < len(dst); i++ {
dst[i] = src[i]
// It returns the Reader and any error.
func NewReaderSize(rd io.Reader, size int) (*Reader, os.Error) {
if size <= 0 {
- return nil, BadBufSize
+ return nil, BufSizeError(size)
}
// Is it already a Reader?
b, ok := rd.(*Reader);
return c, nil
}
-// UnreadByte unreads the last byte. Only one byte may be unread at a given time.
+// UnreadByte unreads the last byte. Only the most recently read byte can be unread.
func (b *Reader) UnreadByte() os.Error {
if b.err != nil {
return b.err
return nil;
}
if b.r <= 0 {
- return PhaseError
+ return ErrInvalidUnreadByte
}
b.r--;
b.lastbyte = -1;
// Buffer is full?
if b.Buffered() >= len(b.buf) {
- return nil, BufferFull
+ return nil, ErrBufferFull
}
}
if e == nil { // got final fragment
break
}
- if e != BufferFull { // unexpected error
+ if e != ErrBufferFull { // unexpected error
err = e;
break
}
}
if n != len(buf) {
frag = buf[0:n];
- err = InternalError;
+ err = errInternal;
break
}
// It returns the Writer and any error.
func NewWriterSize(wr io.Writer, size int) (*Writer, os.Error) {
if size <= 0 {
- return nil, BadBufSize
+ return nil, BufSizeError(size)
}
// Is it already a Writer?
b, ok := wr.(*Writer);
// A CorruptInputError reports the presence of corrupt input at a given offset.
type CorruptInputError int64
func (e CorruptInputError) String() string {
- return "flate: corrupt input at offset " + strconv.Itoa64(int64(e));
+ return "flate: corrupt input before offset " + strconv.Itoa64(int64(e));
}
// An InternalError reports an error in the flate code itself.
buf [4]byte;
}
-// TODO(rsc): This works around a 6g bug.
-func (f *inflater) getRoffset() int64 {
- return f.roffset;
-}
-
func (f *inflater) dataBlock() os.Error
func (f *inflater) readHuffman() os.Error
func (f *inflater) decodeBlock(hl, hd *huffmanDecoder) os.Error
}
default:
// 3 is reserved.
- // TODO(rsc): Works around the same 6g bug.
- var i int64 = f.getRoffset();
- i--;
- err = CorruptInputError(i);
+ err = CorruptInputError(f.roffset);
}
}
return;
f.codebits[codeOrder[i]] = 0;
}
if !f.h1.init(&f.codebits) {
- return os.ErrorString("huff and puff");
+ return CorruptInputError(f.roffset);
}
// HLIT + 257 code lengths, HDIST + 1 code lengths,
rep = 3;
nb = 2;
if i == 0 {
- return CorruptInputError(f.getRoffset());
+ return CorruptInputError(f.roffset);
}
b = f.bits[i-1];
case 17:
f.b >>= nb;
f.nb -= nb;
if i+rep > n {
- return CorruptInputError(f.getRoffset());
+ return CorruptInputError(f.roffset);
}
for j := 0; j < rep; j++ {
f.bits[i] = b;
}
if !f.h1.init(f.bits[0:nlit]) || !f.h2.init(f.bits[nlit:nlit+ndist]) {
- return CorruptInputError(f.getRoffset());
+ return CorruptInputError(f.roffset);
}
return nil;
case dist < 4:
dist++;
case dist >= 30:
- return CorruptInputError(f.getRoffset());
+ return CorruptInputError(f.roffset);
default:
nb := uint(dist - 2) >> 1;
// have 1 bit in bottom of dist, need nb more.
// No check on length; encoding can be prescient.
if !f.hfull && dist > f.hp {
- return CorruptInputError(f.getRoffset());
+ return CorruptInputError(f.roffset);
}
p := f.hp - dist;
n := int(f.buf[0]) | int(f.buf[1])<<8;
nn := int(f.buf[2]) | int(f.buf[3])<<8;
if nn != ^n {
- return CorruptInputError(f.getRoffset());
+ return CorruptInputError(f.roffset);
}
// Read len bytes into history,
func (f *inflater) moreBits() os.Error {
c, err := f.r.ReadByte();
if err != nil {
+ if err == os.EOF {
+ err = io.ErrUnexpectedEOF;
+ }
return err;
}
f.roffset++;
return h.codes[v - h.base[n]], nil;
}
}
- return 0, CorruptInputError(f.getRoffset());
+ return 0, CorruptInputError(f.roffset);
}
// Flush any buffered output to the underlying writer.
import (
"crypto/aes";
"os";
+ "strconv";
)
// The AES block size in bytes.
dec []uint32;
}
+type KeySizeError int
+func (k KeySizeError) String() string {
+ return "crypto/aes: invalid key size " + strconv.Itoa(int(k));
+}
+
// NewCipher creates and returns a new Cipher.
// The key argument should be the AES key,
// either 16, 24, or 32 bytes to select
// AES-128, AES-192, or AES-256.
func NewCipher(key []byte) (*Cipher, os.Error) {
- switch len(key) {
+ k := len(key);
+ switch k {
default:
- return nil, os.ErrorString("crypto/aes: invalid key size");
+ return nil, KeySizeError(k);
case 16, 24, 32:
break;
}
- n := len(key) + 28;
+ n := k + 28;
c := &Cipher{make([]uint32, n), make([]uint32, n)};
expandKey(key, c.enc, c.dec);
return c, nil;
"crypto/block";
"io";
"os";
+ "strconv";
)
type ecbDecrypter struct {
return n;
}
+type ecbFragmentError int
+func (n ecbFragmentError) String() string {
+ return "crypto/block: " + strconv.Itoa(int(n)) + "-byte fragment at EOF";
+}
+
func (x *ecbDecrypter) Read(p []byte) (n int, err os.Error) {
if len(p) == 0 {
return;
// other options cause Wait to return for other
// process events; see package os for details.
func (p *Cmd) Wait(options int) (*os.Waitmsg, os.Error) {
- if p.Pid < 0 {
- return nil, os.EINVAL;
+ if p.Pid <= 0 {
+ return nil, os.ErrorString("exec: invalid use of Cmd.Wait");
}
w, err := os.Wait(p.Pid, options);
if w != nil && (w.Exited() || w.Signaled()) {
// if it hasn't already, and then closes the non-nil file descriptors
// p.Stdin, p.Stdout, and p.Stderr.
func (p *Cmd) Close() os.Error {
- if p.Pid >= 0 {
+ if p.Pid > 0 {
// Loop on interrupt, but
// ignore other errors -- maybe
// caller has already waited for pid.
// connections, it may no longer make sense to have a method with this signature.
func send(req *Request) (resp *Response, err os.Error) {
if req.Url.Scheme != "http" {
- return nil, os.ErrorString("Unsupported protocol: " + req.Url.Scheme);
+ return nil, &badStringError{"unsupported protocol scheme", req.Url.Scheme};
}
addr := req.Url.Host;
}
conn, err := net.Dial("tcp", "", addr);
if err != nil {
- return nil, os.ErrorString("Error dialing " + addr + ": " + err.String());
+ return nil, err;
}
// Close the connection if we encounter an error during header parsing. We'll
}
f := strings.Split(line, " ", 3);
if len(f) < 3 {
- return nil, os.ErrorString(fmt.Sprintf("Invalid first line in HTTP response: %q", line));
+ return nil, &badStringError{"malformed HTTP response", line};
}
resp.Status = f[1] + " " + f[2];
resp.StatusCode, err = strconv.Atoi(f[1]);
if err != nil {
- return nil, os.ErrorString(fmt.Sprintf("Invalid status code in HTTP response: %q", line));
+ return nil, &badStringError{"malformed HTTP status code", f[1]};
}
// Parse the response headers.
// URL unless redirects were followed.
//
// Caller should close r.Body when done reading it.
-func Get(url string) (r *Response, finalUrl string, err os.Error) {
+func Get(url string) (r *Response, finalURL string, err os.Error) {
// TODO: if/when we add cookie support, the redirected request shouldn't
// necessarily supply the same cookies as the original.
- // TODO: adjust referrer header on redirects.
- for redirectCount := 0; redirectCount < 10; redirectCount++ {
- var req Request;
- req.Url, err = ParseURL(url);
- if err != nil {
- return nil, url, err;
+ // TODO: set referrer header on redirects.
+ for redirect := 0;; redirect++ {
+ if redirect >= 10 {
+ err = os.ErrorString("stopped after 10 redirects");
+ break;
}
- r, err := send(&req);
- if err != nil {
- return nil, url, err;
+ var req Request;
+ if req.Url, err = ParseURL(url); err != nil {
+ break;
}
-
- if !shouldRedirect(r.StatusCode) {
- return r, url, nil;
+ if r, err = send(&req); err != nil {
+ break;
}
-
- r.Body.Close();
- url := r.GetHeader("Location");
- if url == "" {
- return r, url, os.ErrorString("302 result with no Location header");
+ if shouldRedirect(r.StatusCode) {
+ r.Body.Close();
+ if url = r.GetHeader("Location"); url == "" {
+ err = os.ErrorString(fmt.Sprintf("%d response missing Location header", r.StatusCode));
+ break;
+ }
}
+ finalURL = url;
+ return;
}
- return nil, url, os.ErrorString("Too many redirects");
+ err = &URLError{"Get", url, err};
+ return;
}
b, err = io.ReadAll(r.Body);
r.Body.Close();
}
-
if err != nil {
- t.Errorf("Error fetching URL: %v", err);
+ t.Error(err);
} else if s := string(b); !strings.HasPrefix(s, "User-agent:") {
t.Errorf("Incorrect page body (did not begin with User-agent): %q", s);
}
import (
"bufio";
+ "bytes";
"container/vector";
"fmt";
"http";
os.ErrorString
}
var (
- LineTooLong = &ProtocolError{"http header line too long"};
- ValueTooLong = &ProtocolError{"http header value too long"};
- HeaderTooLong = &ProtocolError{"http header too long"};
- BadContentLength = &ProtocolError{"invalid content length"};
- ShortEntityBody = &ProtocolError{"entity body too short"};
- NoEntityBody = &ProtocolError{"no entity body"};
- BadHeader = &ProtocolError{"malformed http header"};
- BadRequest = &ProtocolError{"invalid http request"};
- BadHTTPVersion = &ProtocolError{"unsupported http version"};
- UnknownContentType = &ProtocolError{"unknown content type"};
- BadChunkedEncoding = &ProtocolError{"bad chunked encoding"};
+ ErrLineTooLong = &ProtocolError{"header line too long"};
+ ErrHeaderTooLong = &ProtocolError{"header too long"};
+ ErrShortBody = &ProtocolError{"entity body too short"};
)
+type badStringError struct {
+ what string;
+ str string;
+}
+
+func (e *badStringError) String() string {
+ return fmt.Sprintf("%s %q", e.what, e.str);
+}
+
// A Request represents a parsed HTTP request header.
type Request struct {
Method string; // GET, POST, PUT, etc.
// the bufio, so they are only valid until the next bufio read.
func readLineBytes(b *bufio.Reader) (p []byte, err os.Error) {
if p, err = b.ReadLineSlice('\n'); err != nil {
+ // We always know when EOF is coming.
+ // If the caller asked for a line, there should be a line.
+ if err == os.EOF {
+ err = io.ErrUnexpectedEOF;
+ }
return nil, err
}
if len(p) >= maxLineLength {
- return nil, LineTooLong
+ return nil, ErrLineTooLong
}
// Chop off trailing white space.
return string(p), nil
}
+var colon = []byte{':'}
+
// Read a key/value pair from b.
// A key/value has the form Key: Value\r\n
// and the Value can continue on multiple lines if each continuation line
}
// Scan first line for colon.
- for i := 0; i < len(line); i++ {
- switch line[i] {
- case ' ':
- // Key field has space - no good.
- return "", "", BadHeader;
- case ':':
- key = string(line[0:i]);
- // Skip initial space before value.
- for i++; i < len(line); i++ {
- if line[i] != ' ' {
- break
- }
- }
- value = string(line[i:len(line)]);
+ i := bytes.Index(line, colon);
+ if i < 0 {
+ goto Malformed;
+ }
- // Look for extension lines, which must begin with space.
- for {
- var c byte;
+ key = string(line[0:i]);
+ if strings.Index(key, " ") >= 0 {
+ // Key field has space - no good.
+ goto Malformed;
+ }
- if c, e = b.ReadByte(); e != nil {
- return "", "", e
- }
- if c != ' ' {
- // Not leading space; stop.
- b.UnreadByte();
- break
- }
+ // Skip initial space before value.
+ for i++; i < len(line); i++ {
+ if line[i] != ' ' {
+ break
+ }
+ }
+ value = string(line[i:len(line)]);
- // Eat leading space.
- for c == ' ' {
- if c, e = b.ReadByte(); e != nil {
- return "", "", e
- }
- }
+ // Look for extension lines, which must begin with space.
+ for {
+ c, e := b.ReadByte();
+ if c != ' ' {
+ if e != os.EOF {
b.UnreadByte();
+ }
+ break;
+ }
- // Read the rest of the line and add to value.
- if line, e = readLineBytes(b); e != nil {
- return "", "", e
- }
- value += " " + string(line);
-
- if len(value) >= maxValueLength {
- return "", "", ValueTooLong
+ // Eat leading space.
+ for c == ' ' {
+ if c, e = b.ReadByte(); e != nil {
+ if e == os.EOF {
+ e = io.ErrUnexpectedEOF;
}
+ return "", "", e
}
- return key, value, nil
+ }
+ b.UnreadByte();
+
+ // Read the rest of the line and add to value.
+ if line, e = readLineBytes(b); e != nil {
+ return "", "", e
+ }
+ value += " " + string(line);
+
+ if len(value) >= maxValueLength {
+ return "", "", &badStringError{"value too long for key", key};
}
}
+ return key, value, nil;
- // Line ended before space or colon.
- return "", "", BadHeader;
+Malformed:
+ return "", "", &badStringError{"malformed header line", string(line)};
}
// Convert decimal at s[i:len(s)] to integer,
var nb int;
if nb, cr.err = io.ReadFull(cr.r, b); cr.err == nil {
if b[0] != '\r' || b[1] != '\n' {
- cr.err = BadChunkedEncoding;
+ cr.err = os.NewError("malformed chunked encoding");
}
}
}
var f []string;
if f = strings.Split(s, " ", 3); len(f) < 3 {
- return nil, BadRequest
+ return nil, &badStringError{"malformed HTTP request", s};
}
req.Method, req.RawUrl, req.Proto = f[0], f[1], f[2];
var ok bool;
if req.ProtoMajor, req.ProtoMinor, ok = parseHTTPVersion(req.Proto); !ok {
- return nil, BadHTTPVersion
+ return nil, &badStringError{"malformed HTTP version", req.Proto};
}
if req.Url, err = ParseURL(req.RawUrl); err != nil {
break
}
if nheader++; nheader >= maxHeaderLines {
- return nil, HeaderTooLong
+ return nil, ErrHeaderTooLong
}
key = CanonicalHeaderKey(key);
} else if v, present := req.Header["Content-Length"]; present {
length, err := strconv.Btoui64(v, 10);
if err != nil {
- return nil, BadContentLength
+ return nil, &badStringError{"invalid Content-Length", v};
}
// TODO: limit the Content-Length. This is an easy DoS vector.
raw := make([]byte, length);
n, err := b.Read(raw);
if err != nil || uint64(n) < length {
- return nil, ShortEntityBody
+ return nil, ErrShortBody
}
req.Body = io.NewByteReader(raw);
}
// TODO(dsymonds): Parse r.Url.RawQuery instead for GET requests.
func (r *Request) ParseForm() (err os.Error) {
if r.Body == nil {
- return NoEntityBody
+ return os.ErrorString("missing form body");
}
ct, ok := r.Header["Content-Type"];
if !ok {
return err
// TODO(dsymonds): Handle multipart/form-data
}
- return UnknownContentType
+ return &badStringError{"unknown Content-Type", ct};
}
import (
"os";
- "strings"
+ "strconv";
+ "strings";
)
-// Errors introduced by ParseURL.
-type BadURL struct {
- os.ErrorString
+// URLError reports an error and the operation and URL that caused it.
+type URLError struct {
+ Op string;
+ URL string;
+ Error os.Error;
+}
+
+func (e *URLError) String() string {
+ return e.Op + " " + e.URL + ": " + e.Error.String();
}
func ishex(c byte) bool {
return 0
}
+type URLEscapeError string
+func (e URLEscapeError) String() string {
+ return "invalid URL escape " + strconv.Quote(string(e));
+}
+
// Return true if the specified character should be escaped when appearing in a
// URL string.
//
// URLUnescape unescapes a URL-encoded string,
// converting %AB into the byte 0xAB and '+' into ' ' (space).
-// It returns a BadURL error if any % is not followed
+// It returns an error if any % is not followed
// by two hexadecimal digits.
func URLUnescape(s string) (string, os.Error) {
// Count %, check that they're well-formed.
case '%':
n++;
if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) {
- return "", BadURL{"invalid hexadecimal escape"}
+ s = s[i:len(s)];
+ if len(s) > 3 {
+ s = s[0:3];
+ }
+ return "", URLEscapeError(s);
}
i += 3;
case '+':
t := make([]byte, len(s)+2*hexCount);
j := 0;
for i := 0; i < len(s); i++ {
- c := s[i];
- if !shouldEscape(c) {
- t[j] = s[i];
- j++;
- } else if c == ' ' {
+ switch c := s[i]; {
+ case c == ' ':
t[j] = '+';
j++;
- } else {
+ case shouldEscape(c):
t[j] = '%';
t[j+1] = "0123456789abcdef"[c>>4];
t[j+2] = "0123456789abcdef"[c&15];
j += 3;
+ default:
+ t[j] = s[i];
+ j++;
}
}
return string(t);
}
case c == ':':
if i == 0 {
- return "", "", BadURL{"missing protocol scheme"}
+ return "", "", os.ErrorString("missing protocol scheme")
}
return rawurl[0:i], rawurl[i+1:len(rawurl)], nil
default:
return s, ""
}
+// TODO(rsc): The BUG comment is supposed to appear in the godoc output
+// in a BUGS section, but that got lost in the transition to godoc.
+
// BUG(rsc): ParseURL should canonicalize the path,
// removing unnecessary . and .. elements.
+
+
// ParseURL parses rawurl into a URL structure.
// The string rawurl is assumed not to have a #fragment suffix.
// (Web browsers strip #fragment before sending the URL to a web server.)
func ParseURL(rawurl string) (url *URL, err os.Error) {
if rawurl == "" {
- return nil, BadURL{"empty url"}
+ err = os.ErrorString("empty url");
+ goto Error;
}
url = new(URL);
url.Raw = rawurl;
// split off possible leading "http:", "mailto:", etc.
var path string;
if url.Scheme, path, err = getscheme(rawurl); err != nil {
- return nil, err
+ goto Error;
}
url.RawPath = path;
// What's left is the path.
// TODO: Canonicalize (remove . and ..)?
if url.Path, err = URLUnescape(path); err != nil {
- return nil, err
+ goto Error;
}
// Remove escapes from the Authority and Userinfo fields, and verify
// that Scheme and Host contain no escapes (that would be illegal).
if url.Authority, err = URLUnescape(url.Authority); err != nil {
- return nil, err
+ goto Error;
}
if url.Userinfo, err = URLUnescape(url.Userinfo); err != nil {
- return nil, err
+ goto Error;
}
if strings.Index(url.Scheme, "%") >= 0 {
- return nil, BadURL{"hexadecimal escape in scheme"}
+ err = os.ErrorString("hexadecimal escape in scheme");
+ goto Error;
}
if strings.Index(url.Host, "%") >= 0 {
- return nil, BadURL{"hexadecimal escape in host"}
+ err = os.ErrorString("hexadecimal escape in host");
+ goto Error;
}
- return url, nil
+ return url, nil;
+
+Error:
+ return nil, &URLError{"parse", rawurl, err}
+
}
// ParseURLReference is like ParseURL but allows a trailing #fragment.
return nil, err
}
if url.Fragment, err = URLUnescape(frag); err != nil {
- return nil, err
+ return nil, &URLError{"parse", rawurl, err}
}
return url, nil
}
URLEscapeTest{
"%", // not enough characters after %
"",
- BadURL{"invalid hexadecimal escape"}
+ URLEscapeError("%"),
},
URLEscapeTest{
"%a", // not enough characters after %
"",
- BadURL{"invalid hexadecimal escape"}
+ URLEscapeError("%a"),
},
URLEscapeTest{
"%1", // not enough characters after %
"",
- BadURL{"invalid hexadecimal escape"}
+ URLEscapeError("%1"),
},
URLEscapeTest{
"123%45%6", // not enough characters after %
"",
- BadURL{"invalid hexadecimal escape"}
+ URLEscapeError("%6"),
},
URLEscapeTest{
- "%zz", // invalid hex digits
+ "%zzzzz", // invalid hex digits
"",
- BadURL{"invalid hexadecimal escape"}
+ URLEscapeError("%zz"),
},
}
if b.off >= len(b.buf) {
return 0, os.EOF;
}
- c = b.buf[b.off];
+ c = b.buf[b.off];
b.off++;
return c, nil;
}
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
+
# DO NOT EDIT. Automatically generated by gobuild.
# gobuild -m dnsclient.go dnsconfig.go dnsmsg.go fd.go fd_${GOOS}.go ip.go net.go parse.go port.go >Makefile
coverage: packages
gotest
- 6cov -g `pwd` | grep -v '_test\.go:'
+ 6cov -g $$(pwd) | grep -v '_test\.go:'
%.$O: %.go
$(GC) -I_obj $*.go
$(AS) $*.s
O1=\
- dnsmsg.$O\
+ fd_$(GOOS).$O\
parse.$O\
O2=\
- fd_$(GOOS).$O\
+ fd.$O\
ip.$O\
- port.$O\
O3=\
dnsconfig.$O\
- fd.$O\
-
-O4=\
+ dnsmsg.$O\
net.$O\
-O5=\
+O4=\
dnsclient.$O\
+ port.$O\
-phases: a1 a2 a3 a4 a5
+phases: a1 a2 a3 a4
_obj$D/net.a: phases
a1: $(O1)
- $(AR) grc _obj$D/net.a dnsmsg.$O parse.$O
+ $(AR) grc _obj$D/net.a fd_$(GOOS).$O parse.$O
rm -f $(O1)
a2: $(O2)
- $(AR) grc _obj$D/net.a fd_$(GOOS).$O ip.$O port.$O
+ $(AR) grc _obj$D/net.a fd.$O ip.$O
rm -f $(O2)
a3: $(O3)
- $(AR) grc _obj$D/net.a dnsconfig.$O fd.$O
+ $(AR) grc _obj$D/net.a dnsconfig.$O dnsmsg.$O net.$O
rm -f $(O3)
a4: $(O4)
- $(AR) grc _obj$D/net.a net.$O
+ $(AR) grc _obj$D/net.a dnsclient.$O port.$O
rm -f $(O4)
-a5: $(O5)
- $(AR) grc _obj$D/net.a dnsclient.$O
- rm -f $(O5)
-
newpkg: clean
mkdir -p _obj$D
$(O3): a2
$(O4): a3
$(O5): a4
-$(O6): a5
nuke: clean
rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/net.a
import (
"flag";
- "fmt";
"io";
"net";
"os";
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// DNS client.
+// DNS client: see RFC 1035.
// Has to be linked into package net for Dial.
// TODO(rsc):
// Random UDP source port (net.Dial should do that for us).
// Random request IDs.
// More substantial error reporting.
-// Remove use of fmt?
package net
import (
- "fmt";
"io";
"net";
"once";
"strings";
)
-// DNS errors returned by LookupHost.
+// DNSError represents a DNS lookup error.
type DNSError struct {
- os.ErrorString
+ Error string; // description of the error
+ Name string; // name looked for
+ Server string; // server used
}
-var (
- DNS_InternalError os.Error = &DNSError{"internal dns error"};
- DNS_MissingConfig os.Error = &DNSError{"no dns configuration"};
- DNS_No_Answer os.Error = &DNSError{"dns got no answer"};
- DNS_BadRequest os.Error = &DNSError{"malformed dns request"};
- DNS_BadReply os.Error = &DNSError{"malformed dns reply"};
- DNS_ServerFailure os.Error = &DNSError{"dns server failure"};
- DNS_NoServers os.Error = &DNSError{"no dns servers"};
- DNS_NameTooLong os.Error = &DNSError{"dns name too long"};
- DNS_RedirectLoop os.Error = &DNSError{"dns redirect loop"};
- DNS_NameNotFound os.Error = &DNSError{"dns name not found"};
-)
+
+func (e *DNSError) String() string {
+ s := "lookup " + e.Name;
+ if e.Server != "" {
+ s += " on " + e.Server;
+ }
+ s += ": " + e.Error;
+ return s;
+}
+
+const noSuchHost = "no such host"
// Send a request on the connection and hope for a reply.
// Up to cfg.attempts attempts.
func _Exchange(cfg *_DNS_Config, c Conn, name string) (m *_DNS_Msg, err os.Error) {
if len(name) >= 256 {
- return nil, DNS_NameTooLong
+ return nil, &DNSError{"name too long", name, ""}
}
out := new(_DNS_Msg);
out.id = 0x1234;
out.recursion_desired = true;
msg, ok := out.Pack();
if !ok {
- return nil, DNS_InternalError
+ return nil, &DNSError{"internal error - cannot pack message", name, ""}
}
for attempt := 0; attempt < cfg.attempts; attempt++ {
buf := make([]byte, 2000); // More than enough.
n, err = c.Read(buf);
- if err == os.EAGAIN {
+ if isEAGAIN(err) {
+ err = nil;
continue;
}
if err != nil {
}
return in, nil
}
- return nil, DNS_No_Answer
+ return nil, &DNSError{"no answer from server", name, c.RemoteAddr()}
}
// Find answer for name in dns message.
// On return, if err == nil, addrs != nil.
// TODO(rsc): Maybe return []IP instead?
-func answer(name string, dns *_DNS_Msg) (addrs []string, err os.Error) {
+func answer(name, server string, dns *_DNS_Msg) (addrs []string, err *DNSError) {
addrs = make([]string, 0, len(dns.answer));
- if dns.rcode == _DNS_RcodeNameError && dns.authoritative {
- return nil, DNS_NameNotFound // authoritative "no such host"
+ if dns.rcode == _DNS_RcodeNameError && dns.recursion_available {
+ return nil, &DNSError{noSuchHost, name, ""}
}
if dns.rcode != _DNS_RcodeSuccess {
// None of the error codes make sense
// for the query we sent. If we didn't get
// a name error and we didn't get success,
// the server is behaving incorrectly.
- return nil, DNS_ServerFailure
+ return nil, &DNSError{"server misbehaving", name, server}
}
// Look for the name.
n := len(addrs);
a := rr.(*_DNS_RR_A).a;
addrs = addrs[0:n+1];
- addrs[n] = fmt.Sprintf("%d.%d.%d.%d", (a>>24), (a>>16)&0xFF, (a>>8)&0xFF, a&0xFF);
+ addrs[n] = IPv4(byte(a>>24), byte(a>>16), byte(a>>8), byte(a)).String();
case _DNS_TypeCNAME:
// redirect to cname
name = rr.(*_DNS_RR_CNAME).cname;
}
}
if len(addrs) == 0 {
- return nil, DNS_NameNotFound
+ return nil, &DNSError{noSuchHost, name, server}
}
return addrs, nil
}
- // Too many redirects
- return nil, DNS_RedirectLoop
+ return nil, &DNSError{"too many redirects", name, server}
}
// Do a lookup for a single name, which must be rooted
// (otherwise answer will not find the answers).
func tryOneName(cfg *_DNS_Config, name string) (addrs []string, err os.Error) {
- err = DNS_NoServers;
+ if len(cfg.servers) == 0 {
+ return nil, &DNSError{"no DNS servers", 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,
// The DNS config parser has already checked that
// all the cfg.servers[i] are IP addresses, which
// Dial will use without a DNS lookup.
- c, cerr := Dial("udp", "", cfg.servers[i] + ":53");
+ server := cfg.servers[i] + ":53";
+ c, cerr := Dial("udp", "", server);
if cerr != nil {
err = cerr;
continue;
err = merr;
continue;
}
- addrs, aerr := answer(name, msg);
- if aerr != nil && aerr != DNS_NameNotFound {
- err = aerr;
- continue;
+ var dnserr *DNSError;
+ addrs, dnserr = answer(name, server, msg);
+ if dnserr != nil {
+ err = dnserr;
+ } else {
+ err = nil; // nil os.Error, not nil *DNSError
+ }
+ if dnserr == nil || dnserr.Error == noSuchHost {
+ break;
}
- return addrs, aerr;
}
return;
}
cfg, dnserr = _DNS_ReadConfig();
}
+func isDomainName(s string) bool {
+ // Requirements on DNS name:
+ // * must not be empty.
+ // * must be alphanumeric plus - and .
+ // * each of the dot-separated elements must begin
+ // and end with a letter or digit.
+ // RFC 1035 required the element to begin with a letter,
+ // but RFC 3696 says this has been relaxed to allow digits too.
+ // still, there must be a letter somewhere in the entire name.
+ if len(s) == 0 {
+ return false;
+ }
+ if s[len(s)-1] != '.' { // simplify checking loop: make name end in dot
+ s += ".";
+ }
+
+ last := byte('.');
+ ok := false; // ok once we've seen a letter
+ for i := 0; i < len(s); i++ {
+ c := s[i];
+ switch {
+ default:
+ return false;
+ case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
+ ok = true;
+ case '0' <= c && c <= '9':
+ // fine
+ case c == '-':
+ // byte before dash cannot be dot
+ if last == '.' {
+ return false;
+ }
+ case c == '.':
+ // byte before dot cannot be dot, dash
+ if last == '.' || last == '-' {
+ return false;
+ }
+ }
+ last = c;
+ }
+
+ return ok;
+}
+
// LookupHost looks up the host name using the local DNS resolver.
// It returns the canonical name for the host and an array of that
// host's addresses.
-func LookupHost(name string) (cname string, addrs []string, err os.Error)
-{
- // TODO(rsc): Pick out obvious non-DNS names to avoid
- // sending stupid requests to the server?
-
+func LookupHost(name string) (cname string, addrs []string, err os.Error) {
+ if !isDomainName(name) {
+ return name, nil, &DNSError{"invalid domain name", name, ""};
+ }
once.Do(loadConfig);
if dnserr != nil || cfg == nil {
- // better error than file not found.
- err = DNS_MissingConfig;
+ err = dnserr;
return;
}
rname += ".";
}
// Can try as ordinary name.
- addrs, aerr := tryOneName(cfg, rname);
- if aerr == nil {
- return rname, addrs, nil;
+ var dnserr *DNSError;
+ addrs, err = tryOneName(cfg, rname);
+ if err == nil {
+ cname = rname;
+ return;
}
- err = aerr;
}
if rooted {
return
// Otherwise, try suffixes.
for i := 0; i < len(cfg.search); i++ {
- newname := name+"."+cfg.search[i];
- if newname[len(newname)-1] != '.' {
- newname += "."
+ rname := name+"."+cfg.search[i];
+ if rname[len(rname)-1] != '.' {
+ rname += "."
}
- addrs, aerr := tryOneName(cfg, newname);
- if aerr == nil {
- return newname, addrs, nil;
+ var dnserr *DNSError;
+ addrs, err = tryOneName(cfg, rname);
+ if err == nil {
+ cname = rname;
+ return;
}
- err = aerr;
}
return
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// DNS packet assembly.
+// DNS packet assembly. See RFC 1035.
//
// This is intended to support name resolution during net.Dial.
// It doesn't have to be blazing fast.
import (
"fmt";
+ "net";
"os";
"reflect";
)
s += printStructValue(fld.(reflect.StructValue));
case kind == reflect.Uint32Kind && tag == "ipv4":
i := fld.(reflect.Uint32Value).Get();
- s += fmt.Sprintf("%d.%d.%d.%d", (i>>24)&0xFF, (i>>16)&0xFF, (i>>8)&0xFF, i&0xFF);
+ s += IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i)).String();
default:
s += fmt.Sprint(fld.Interface())
}
var e int;
if e = syscall.SetNonblock(s.pr.Fd(), true); e != 0 {
Errno:
- err = os.ErrnoToError(e);
+ err = &os.PathError{"setnonblock", s.pr.Name(), os.Errno(e)};
Error:
s.pr.Close();
s.pw.Close();
once.Do(_StartServer);
}
if e := syscall.SetNonblock(fd, true); e != 0 {
- return nil, os.ErrnoToError(e);
+ return nil, &os.PathError{"setnonblock", laddr, os.Errno(e)};
}
f = new(netFD);
f.fd = fd;
f.net = net;
f.laddr = laddr;
f.raddr = raddr;
- f.file = os.NewFile(fd, "net: " + net + " " + laddr + " " + raddr);
+ f.file = os.NewFile(fd, net + "!" + laddr + "->" + raddr);
f.cr = make(chan *netFD, 1);
f.cw = make(chan *netFD, 1);
return f, nil
}
+func isEAGAIN(e os.Error) bool {
+ if e1, ok := e.(*os.PathError); ok {
+ return e1.Error == os.EAGAIN;
+ }
+ return e == os.EAGAIN;
+}
+
+func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error)
+
+func (fd *netFD) addr() string {
+ sa, e := syscall.Getsockname(fd.fd);
+ if e != 0 {
+ return "";
+ }
+ addr, err := sockaddrToString(sa);
+ return addr;
+}
+
+func (fd *netFD) remoteAddr() string {
+ sa, e := syscall.Getpeername(fd.fd);
+ if e != 0 {
+ return "";
+ }
+ addr, err := sockaddrToString(sa);
+ return addr;
+}
+
func (fd *netFD) Close() os.Error {
if fd == nil || fd.file == nil {
return os.EINVAL
func (fd *netFD) Read(p []byte) (n int, err os.Error) {
if fd == nil || fd.file == nil {
- return -1, os.EINVAL
+ return 0, os.EINVAL
}
fd.rio.Lock();
defer fd.rio.Unlock();
} else {
fd.rdeadline = 0;
}
- n, err = fd.file.Read(p);
- for err == os.EAGAIN && fd.rdeadline >= 0 {
- pollserver.WaitRead(fd);
- n, err = fd.file.Read(p)
+ for {
+ n, err = fd.file.Read(p);
+ if isEAGAIN(err) && fd.rdeadline >= 0 {
+ pollserver.WaitRead(fd);
+ continue;
+ }
+ break;
}
- return n, err
+ return;
}
func (fd *netFD) Write(p []byte) (n int, err os.Error) {
if fd == nil || fd.file == nil {
- return -1, os.EINVAL
+ return 0, os.EINVAL
}
fd.wio.Lock();
defer fd.wio.Unlock();
if nn == len(p) {
break;
}
- if err == os.EAGAIN && fd.wdeadline >= 0 {
+ if isEAGAIN(err) && fd.wdeadline >= 0 {
pollserver.WaitWrite(fd);
continue;
}
return nn, err
}
-func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error)
-
func (fd *netFD) accept() (nfd *netFD, err os.Error) {
if fd == nil || fd.file == nil {
return nil, os.EINVAL
}
if e != 0 {
syscall.ForkLock.RUnlock();
- return nil, os.ErrnoToError(e)
+ return nil, &os.PathError{"accept", fd.addr(), os.Errno(e)}
}
syscall.CloseOnExec(s);
syscall.ForkLock.RUnlock();
}
return nfd, nil
}
-
-func (fd *netFD) addr() string {
- sa, err := syscall.Getsockname(fd.fd);
- if err != 0 {
- return "";
- }
- // TODO(rsc): woud like to say err not err1 but 6g complains
- addr, err1 := sockaddrToString(sa);
- return addr;
-}
-
-func (fd *netFD) remoteAddr() string {
- sa, err := syscall.Getpeername(fd.fd);
- if err != 0 {
- return "";
- }
- // TODO(rsc): woud like to say err not err1 but 6g complains
- addr, err1 := sockaddrToString(sa);
- return addr;
-}
"syscall";
)
-var kqueuePhaseError = &Error{"kqueue phase error"}
-
type pollster struct {
kq int;
eventbuf [10]syscall.Kevent_t;
p = new(pollster);
var e int;
if p.kq, e = syscall.Kqueue(); e != 0 {
- return nil, os.ErrnoToError(e)
+ return nil, os.NewSyscallError("kqueue", e)
}
p.events = p.eventbuf[0:0];
return p, nil
n, e := syscall.Kevent(p.kq, &events, &events, nil);
if e != 0 {
- return os.ErrnoToError(e)
+ return os.NewSyscallError("kevent", e);
}
if n != 1 || (ev.Flags & syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode {
- return kqueuePhaseError
+ return os.ErrorString("kqueue phase error");
}
if ev.Data != 0 {
- return os.ErrnoToError(int(ev.Data))
+ return os.Errno(int(ev.Data))
}
return nil
}
if e == syscall.EINTR {
continue
}
- return -1, 0, os.ErrnoToError(e)
+ return -1, 0, os.NewSyscallError("kevent", e)
}
if nn == 0 {
return -1, 0, nil;
}
func (p *pollster) Close() os.Error {
- return os.ErrnoToError(syscall.Close(p.kq))
+ return os.NewSyscallError("close", syscall.Close(p.kq))
}
// about the number of FDs we will care about.
// We don't know.
if p.epfd, e = syscall.EpollCreate(16); e != 0 {
- return nil, os.ErrnoToError(e)
+ return nil, os.NewSyscallError("epoll_create", e)
}
p.events = make(map[int] uint32);
return p, nil
op = syscall.EPOLL_CTL_ADD;
}
if e := syscall.EpollCtl(p.epfd, op, fd, &ev); e != 0 {
- return os.ErrnoToError(e)
+ return os.NewSyscallError("epoll_ctl", e)
}
p.events[fd] = ev.Events;
return nil
ev.Fd = int32(fd);
ev.Events = events;
if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_MOD, fd, &ev); e != 0 {
- print("Epoll modify fd=", fd, ": ", os.ErrnoToError(e).String(), "\n");
+ print("Epoll modify fd=", fd, ": ", os.Errno(e).String(), "\n");
}
p.events[fd] = events;
} else {
if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_DEL, fd, nil); e != 0 {
- print("Epoll delete fd=", fd, ": ", os.ErrnoToError(e).String(), "\n");
+ print("Epoll delete fd=", fd, ": ", os.Errno(e).String(), "\n");
}
p.events[fd] = 0, false;
}
n, e = syscall.EpollWait(p.epfd, &evarray, msec);
}
if e != 0 {
- return -1, 0, os.ErrnoToError(e);
+ return -1, 0, os.NewSyscallError("epoll_wait", e);
}
if n == 0 {
return -1, 0, nil;
}
func (p *pollster) Close() os.Error {
- return os.ErrnoToError(syscall.Close(p.epfd));
+ return os.NewSyscallError("close", syscall.Close(p.epfd));
}
import (
"net";
"os";
+ "reflect";
"strconv";
+ "strings";
"syscall";
)
-var (
- BadAddress os.Error = &Error{"malformed address"};
- MissingAddress os.Error = &Error{"missing address"};
- UnknownNetwork os.Error = &Error{"unknown network"};
- UnknownHost os.Error = &Error{"unknown host"};
- UnknownSocketFamily os.Error = &Error{"unknown socket family"};
-)
+var errMissingAddress = os.ErrorString("missing address")
+
+type OpError struct {
+ Op string;
+ Net string;
+ Addr string;
+ Error os.Error;
+}
+
+func (e *OpError) String() string {
+ s := e.Op;
+ if e.Net != "" {
+ s += " " + e.Net;
+ }
+ if e.Addr != "" {
+ s += " " + e.Addr;
+ }
+ s += ": " + e.Error.String();
+ return s;
+}
+
+type AddrError struct {
+ Error string;
+ Addr string;
+}
+func (e *AddrError) String() string {
+ s := e.Error;
+ if e.Addr != "" {
+ s += " " + e.Addr;
+ }
+ return s;
+}
+
+type UnknownNetworkError string
+func (e UnknownNetworkError) String() string {
+ return "unknown network " + string(e);
+}
// Conn is a generic network connection.
type Conn interface {
}
func LookupHost(name string) (cname string, addrs []string, err os.Error)
+func LookupPort(network, service string) (port int, err os.Error)
// Split "host:port" into "host" and "port".
// Host cannot contain colons unless it is bracketed.
func splitHostPort(hostport string) (host, port string, err os.Error) {
// The port starts after the last colon.
- var i int;
- for i = len(hostport)-1; i >= 0; i-- {
- if hostport[i] == ':' {
- break
- }
- }
+ i := strings.LastIndex(hostport, ":");
if i < 0 {
- return "", "", BadAddress
+ err = &AddrError{"missing port in address", hostport};
+ return;
}
- host = hostport[0:i];
- port = hostport[i+1:len(hostport)];
+ host, port = hostport[0:i], hostport[i+1:len(hostport)];
// Can put brackets around host ...
if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' {
} else {
// ... but if there are no brackets, no colons.
if byteIndex(host, ':') >= 0 {
- return "", "", BadAddress
+ err = &AddrError{"too many colons in address", hostport};
+ return;
}
}
- return host, port, nil
+ return;
}
// Join "host" and "port" into "host:port".
// For now, host and port must be numeric literals.
// Eventually, we'll have name resolution.
func hostPortToIP(net, hostport, mode string) (ip IP, iport int, err os.Error) {
- var host, port string;
- host, port, err = splitHostPort(hostport);
+ host, port, err := splitHostPort(hostport);
if err != nil {
- return nil, 0, err
+ goto Error;
}
var addr IP;
if host == "" {
- if mode == "listen" {
- if preferIPv4 {
- addr = IPv4zero;
- } else {
- addr = IPzero; // wildcard - listen to all
- }
+ if mode != "listen" {
+ err = &AddrError{"no host in address", hostport};
+ goto Error;
+ }
+ if preferIPv4 {
+ addr = IPv4zero;
} else {
- return nil, 0, MissingAddress;
+ addr = IPzero; // wildcard - listen to all
}
}
}
if addr == nil {
// Not an IP address. Try as a DNS name.
- hostname, addrs, err := LookupHost(host);
- if err != nil {
- return nil, 0, err
- }
- if len(addrs) == 0 {
- return nil, 0, UnknownHost
+ hostname, addrs, err1 := LookupHost(host);
+ if err1 != nil {
+ err = err1;
+ goto Error;
}
addr = ParseIP(addrs[0]);
if addr == nil {
// should not happen
- return nil, 0, BadAddress
+ err = &AddrError{"LookupHost returned invalid address", addrs[0]};
+ goto Error;
}
}
if !ok || i != len(port) {
p, err = LookupPort(net, port);
if err != nil {
- return nil, 0, err
+ goto Error;
}
}
if p < 0 || p > 0xFFFF {
- return nil, 0, BadAddress
+ err = &AddrError{"invalid port", port};
+ goto Error;
}
- return addr, p, nil
+ return addr, p, nil;
+
+Error:
+ return nil, 0, err;
+}
+
+type UnknownSocketError struct {
+ sa syscall.Sockaddr;
+}
+func (e *UnknownSocketError) String() string {
+ return "unknown socket address type " + reflect.Typeof(e.sa).String()
}
func sockaddrToString(sa syscall.Sockaddr) (name string, err os.Error) {
case *syscall.SockaddrUnix:
return a.Name, nil;
}
- return "", UnknownSocketFamily
+
+ return "", &UnknownSocketError{sa};
}
func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, os.Error) {
s, e := syscall.Socket(f, p, t);
if e != 0 {
syscall.ForkLock.RUnlock();
- return nil, os.ErrnoToError(e)
+ return nil, os.Errno(e)
}
syscall.CloseOnExec(s);
syscall.ForkLock.RUnlock();
e = syscall.Bind(s, la);
if e != 0 {
syscall.Close(s);
- return nil, os.ErrnoToError(e)
+ return nil, os.Errno(e)
}
}
e = syscall.Connect(s, ra);
if e != 0 {
syscall.Close(s);
- return nil, os.ErrnoToError(e)
+ return nil, os.Errno(e)
}
}
func setsockoptInt(fd, level, opt int, value int) os.Error {
- return os.ErrnoToError(syscall.SetsockoptInt(fd, level, opt, value));
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, opt, value));
}
func setsockoptNsec(fd, level, opt int, nsec int64) os.Error {
var tv = syscall.NsecToTimeval(nsec);
- return os.ErrnoToError(syscall.SetsockoptTimeval(fd, level, opt, &tv));
+ return os.NewSyscallError("setsockopt", syscall.SetsockoptTimeval(fd, level, opt, &tv));
}
func (c *connBase) SetReadBuffer(bytes int) os.Error {
l.Linger = 0;
}
e := syscall.SetsockoptLinger(c.sysFD(), syscall.SOL_SOCKET, syscall.SO_LINGER, &l);
- return os.ErrnoToError(e);
+ return os.NewSyscallError("setsockopt", e);
}
if laddr != "" {
if lip, lport, err = hostPortToIP(net, laddr, mode); err != nil {
- return
+ goto Error;
}
}
if raddr != "" {
if rip, rport, err = hostPortToIP(net, raddr, mode); err != nil {
- return
+ goto Error;
}
}
var la, ra syscall.Sockaddr;
if lip != nil {
if la, err = ipToSockaddr(family, lip, lport); err != nil {
- return
+ goto Error;
}
}
if rip != nil {
if ra, err = ipToSockaddr(family, rip, rport); err != nil {
- return
+ goto Error;
}
}
fd, err = socket(net, laddr, raddr, family, proto, 0, la, ra);
- return fd, err
+ if err != nil {
+ goto Error;
+ }
+ return fd, nil;
+
+Error:
+ addr := raddr;
+ if mode == "listen" {
+ addr = laddr;
+ }
+ return nil, &OpError{mode, net, addr, err};
}
// and returns a ConnTCP structure.
func DialTCP(net, laddr, raddr string) (c *ConnTCP, err os.Error) {
if raddr == "" {
- return nil, MissingAddress
+ return nil, &OpError{"dial", "tcp", "", errMissingAddress}
}
fd, e := internetSocket(net, laddr, raddr, syscall.SOCK_STREAM, "dial");
if e != nil {
// and returns a ConnUDP structure.
func DialUDP(net, laddr, raddr string) (c *ConnUDP, err os.Error) {
if raddr == "" {
- return nil, MissingAddress
+ return nil, &OpError{"dial", "udp", "", errMissingAddress}
}
fd, e := internetSocket(net, laddr, raddr, syscall.SOCK_DGRAM, "dial");
if e != nil {
var proto int;
switch net {
default:
- return nil, UnknownNetwork;
+ return nil, UnknownNetworkError(net);
case "unix":
proto = syscall.SOCK_STREAM;
case "unix-dgram":
var la, ra syscall.Sockaddr;
switch mode {
+ default:
+ panic("unixSocket", mode);
+
case "dial":
if laddr != "" {
- return nil, BadAddress;
+ return nil, &OpError{mode, net, raddr, &AddrError{"unexpected local address", laddr}}
}
if raddr == "" {
- return nil, MissingAddress;
+ return nil, &OpError{mode, net, "", errMissingAddress}
}
ra = &syscall.SockaddrUnix{Name: raddr};
case "listen":
if laddr == "" {
- return nil, MissingAddress;
+ return nil, &OpError{mode, net, "", errMissingAddress}
}
la = &syscall.SockaddrUnix{Name: laddr};
if raddr != "" {
- return nil, BadAddress;
+ return nil, &OpError{mode, net, laddr, &AddrError{"unexpected remote address", raddr}}
}
}
fd, err = socket(net, laddr, raddr, syscall.AF_UNIX, proto, 0, la, ra);
- return fd, err
+ if err != nil {
+ goto Error;
+ }
+ return fd, nil;
+
+Error:
+ addr := raddr;
+ if mode == "listen" {
+ addr = laddr;
+ }
+ return nil, &OpError{mode, net, addr, err};
}
// ConnUnix is an implementation of the Conn interface
func ListenUnix(net, laddr string) (l *ListenerUnix, err os.Error) {
fd, e := unixSocket(net, laddr, "", "listen");
if e != nil {
+ if pe, ok := e.(*os.PathError); ok {
+ e = pe.Error;
+ }
// Check for socket ``in use'' but ``refusing connections,''
// which means some program created it and exited
// without unlinking it from the file system.
if e1 == nil {
fd1.Close();
}
+ if pe, ok := e1.(*os.PathError); ok {
+ e1 = pe.Error;
+ }
if e1 != os.ECONNREFUSED {
return nil, e;
}
e1 := syscall.Listen(fd.fd, 8); // listenBacklog());
if e1 != 0 {
syscall.Close(fd.fd);
- return nil, os.ErrnoToError(e1);
+ return nil, &OpError{"listen", "unix", laddr, os.Errno(e1)};
}
return &ListenerUnix{fd, laddr}, nil;
}
return c, err
*/
}
- return nil, UnknownNetwork
+ return nil, &OpError{"dial", net, raddr, UnknownNetworkError(net)};
}
// A Listener is a generic network listener.
e1 := syscall.Listen(fd.fd, listenBacklog());
if e1 != 0 {
syscall.Close(fd.fd);
- return nil, os.ErrnoToError(e1)
+ return nil, &OpError{"listen", "tcp", laddr, os.Errno(e1)};
}
l = new(ListenerTCP);
l.fd = fd;
*/
// BUG(rsc): Listen should support UDP.
}
- return nil, UnknownNetwork
+ return nil, UnknownNetworkError(net);
}
--- /dev/null
+// 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 net
+
+import (
+ "net";
+ "os";
+ "regexp";
+ "testing";
+)
+
+type DialErrorTest struct {
+ Net string;
+ Laddr string;
+ Raddr string;
+ Pattern string;
+}
+
+var dialErrorTests = []DialErrorTest {
+ DialErrorTest{
+ "datakit", "", "mh/astro/r70",
+ "dial datakit mh/astro/r70: unknown network datakit",
+ },
+ DialErrorTest{
+ "tcp", "", "127.0.0.1:☺",
+ "dial tcp 127.0.0.1:☺: unknown port tcp/☺",
+ },
+ DialErrorTest{
+ "tcp", "", "no-such-name.google.com.:80",
+ "dial tcp no-such-name.google.com.:80: lookup no-such-name.google.com.: no such host",
+ },
+ DialErrorTest{
+ "tcp", "", "no-such-name.no-such-top-level-domain.:80",
+ "dial tcp no-such-name.no-such-top-level-domain.:80: lookup no-such-name.no-such-top-level-domain.: no such host",
+ },
+ DialErrorTest{
+ "tcp", "", "no-such-name:80",
+ "dial tcp no-such-name:80: lookup no-such-name.google.com.: no such host",
+ },
+ DialErrorTest{
+ "tcp", "", "mh/astro/r70:http",
+ "dial tcp mh/astro/r70:http: lookup mh/astro/r70: invalid domain name",
+ },
+ DialErrorTest{
+ "unix", "", "/etc/file-not-found",
+ "dial unix /etc/file-not-found: no such file or directory",
+ },
+ DialErrorTest{
+ "unix", "", "/etc/",
+ "dial unix /etc/: (permission denied|socket operation on non-socket)",
+ },
+}
+
+func TestDialError(t *testing.T) {
+ for i, tt := range dialErrorTests {
+ c, e := net.Dial(tt.Net, tt.Laddr, tt.Raddr);
+ if c != nil {
+ c.Close();
+ }
+ if e == nil {
+ t.Errorf("#%d: nil error, want match for %#q", i, tt.Pattern);
+ continue;
+ }
+ s := e.String();
+ match, err := regexp.Match(tt.Pattern, s);
+ if !match {
+ t.Errorf("#%d: %q, want match for %#q", i, s, tt.Pattern);
+ }
+ }
+}
"os";
)
-type Error struct {
- os.ErrorString
-}
-
type file struct {
file *os.File;
data []byte;
"net";
"once";
"os";
- "strconv";
)
-// The error returned by LookupPort when a network service
-// is not listed in the database.
-var ErrNoService = &Error{"unknown network service"};
-
var services map[string] map[string] int
var servicesError os.Error
network = "udp";
}
- m, ok := services[network];
- if !ok {
- return 0, ErrNoService;
- }
- port, ok = m[service];
- if !ok {
- return 0, ErrNoService;
+ if m, ok := services[network]; ok {
+ if port, ok = m[service]; ok {
+ return;
+ }
}
- return port, nil;
+ return 0, &AddrError{"unknown port", network + "/" + service};
}
var b [100]byte;
n, err1 := fd.Read(&b);
t1 := time.Nanoseconds();
- if n != 0 || err1 != os.EAGAIN {
+ if n != 0 || !isEAGAIN(err1) {
t.Errorf("fd.Read on %s %s did not return 0, EAGAIN: %v, %v", network, addr, n, err1);
}
if t1 - t0 < 0.5e8 || t1 - t0 > 1.5e8 {
d.nbuf, errno = syscall.Getdirentries(file.fd, d.buf, new(uintptr));
if errno != 0 {
d.nbuf = 0;
- return names, ErrnoToError(errno)
+ return names, NewSyscallError("getdirentries", errno);
}
- if d.nbuf == 0 {
+ if d.nbuf <= 0 {
break // EOF
}
}
d.nbuf, errno = syscall.Getdirentries(file.fd, d.buf, new(uintptr));
if errno != 0 {
d.nbuf = 0;
- return names, ErrnoToError(errno)
+ return names, NewSyscallError("getdirentries", errno);
}
- if d.nbuf == 0 {
+ if d.nbuf <= 0 {
break // EOF
}
}
if d.bufp >= d.nbuf {
var errno int;
d.nbuf, errno = syscall.Getdents(file.fd, d.buf);
- if d.nbuf < 0 {
- return names, ErrnoToError(errno)
+ if errno != 0 {
+ return names, NewSyscallError("getdents", errno)
}
- if d.nbuf == 0 {
+ if d.nbuf <= 0 {
break // EOF
}
d.bufp = 0;
if d.bufp >= d.nbuf {
var errno int;
d.nbuf, errno = syscall.Getdents(file.fd, d.buf);
- if d.nbuf < 0 {
- return names, ErrnoToError(errno)
+ if errno != 0 {
+ return names, NewSyscallError("getdents", errno)
}
- if d.nbuf == 0 {
+ if d.nbuf <= 0 {
break // EOF
}
d.bufp = 0;
return syscall.Errstr(int(e))
}
-// ErrnoToError converts errno to an Error (underneath, an Errno).
-// It returns nil for the "no error" errno.
-func ErrnoToError(errno int) Error {
- if errno == 0 {
- return nil
- }
- return Errno(errno)
-}
-
// Commonly known Unix errors.
var (
EPERM Error = Errno(syscall.EPERM);
ENAMETOOLONG Error = Errno(syscall.ENAMETOOLONG);
)
+// PathError records an error and the operation and file path that caused it.
+type PathError struct {
+ Op string;
+ Path string;
+ Error Error;
+}
+
+func (e *PathError) String() string {
+ return e.Op + " " + e.Path + ": " + e.Error.String();
+}
+
+// SyscallError records an error from a specific system call.
+type SyscallError struct {
+ Syscall string;
+ Errno Errno;
+}
+
+func (e *SyscallError) String() string {
+ return e.Syscall + ": " + e.Errno.String();
+}
+
+// NewSyscallError returns, as an os.Error, a new SyscallError
+// with the given system call name and error number.
+// As a convenience, if errno is 0, NewSyscallError returns nil.
+func NewSyscallError(syscall string, errno int) os.Error {
+ if errno == 0 {
+ return nil;
+ }
+ return &SyscallError{syscall, Errno(errno)}
+}
}
p, e := syscall.ForkExec(argv0, argv, envv, dir, intfd);
- return int(p), ErrnoToError(e);
+ if e != 0 {
+ return 0, &PathError{"fork/exec", argv0, Errno(e)};
+ }
+ return p, nil;
}
// Exec replaces the current process with an execution of the program
envv = Environ();
}
e := syscall.Exec(argv0, argv, envv);
- return ErrnoToError(e);
+ if e != 0 {
+ return &PathError{"exec", argv0, Errno(e)};
+ }
+ return nil;
}
// TODO(rsc): Should os implement its own syscall.WaitStatus
}
pid1, e := syscall.Wait4(pid, &status, options, rusage);
if e != 0 {
- return nil, ErrnoToError(e);
+ return nil, NewSyscallError("wait", e);
}
w = new(Waitmsg);
- w.Pid = pid;
+ w.Pid = pid1;
w.WaitStatus = status;
w.Rusage = rusage;
return w, nil;
}
// NewFile returns a new File with the given file descriptor and name.
-func NewFile(file int, name string) *File {
- if file < 0 {
+func NewFile(fd int, name string) *File {
+ if fd < 0 {
return nil
}
- return &File{file, name, nil, 0}
+ return &File{fd, name, nil, 0}
}
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
func Open(name string, flag int, perm int) (file *File, err Error) {
r, e := syscall.Open(name, flag | syscall.O_CLOEXEC, perm);
if e != 0 {
- return nil, ErrnoToError(e);
+ return nil, &PathError{"open", name, Errno(e)};
}
// There's a race here with fork/exec, which we are
syscall.CloseOnExec(r);
}
- return NewFile(r, name), ErrnoToError(e)
+ return NewFile(r, name), nil;
}
// Close closes the File, rendering it unusable for I/O.
if file == nil {
return EINVAL
}
- err := ErrnoToError(syscall.Close(file.fd));
+ var err os.Error;
+ if e := syscall.Close(file.fd); e != 0 {
+ err = &PathError{"close", file.name, Errno(e)};
+ }
file.fd = -1; // so it can't be closed again
return err;
}
if n == 0 && e == 0 {
return 0, EOF
}
- return n, ErrnoToError(e);
+ if e != 0 {
+ err = &PathError{"read", file.name, Errno(e)};
+ }
+ return n, err
}
// Write writes len(b) bytes to the File.
} else {
file.nepipe = 0;
}
- return n, ErrnoToError(e)
+ if e != 0 {
+ err = &PathError{"write", file.name, Errno(e)};
+ }
+ return n, err
}
// Seek sets the offset for the next Read or Write on file to offset, interpreted
// It returns the new offset and an Error, if any.
func (file *File) Seek(offset int64, whence int) (ret int64, err Error) {
r, e := syscall.Seek(file.fd, offset, whence);
- if e != 0 {
- return -1, ErrnoToError(e)
+ if e == 0 && file.dirinfo != nil && r != 0 {
+ e = syscall.EISDIR;
}
- if file.dirinfo != nil && r != 0 {
- return -1, ErrnoToError(syscall.EISDIR)
+ if e != 0 {
+ return 0, &PathError{"seek", file.name, Errno(e)};
}
return r, nil
}
}
b := syscall.StringByteSlice(s);
b = b[0:len(b)-1];
- r, e := syscall.Write(file.fd, b);
- if r < 0 {
- r = 0
- }
- return int(r), ErrnoToError(e)
+ return file.Write(b);
}
// Pipe returns a connected pair of Files; reads from r return bytes written to w.
e := syscall.Pipe(&p);
if e != 0 {
syscall.ForkLock.RUnlock();
- return nil, nil, ErrnoToError(e)
+ return nil, nil, NewSyscallError("pipe", e);
}
syscall.CloseOnExec(p[0]);
syscall.CloseOnExec(p[1]);
// Mkdir creates a new directory with the specified name and permission bits.
// It returns an error, if any.
func Mkdir(name string, perm int) Error {
- return ErrnoToError(syscall.Mkdir(name, perm));
+ e := syscall.Mkdir(name, perm);
+ if e != 0 {
+ return &PathError{"mkdir", name, Errno(e)};
+ }
+ return nil;
}
// Stat returns a Dir structure describing the named file and an error, if any.
var lstat, stat syscall.Stat_t;
e := syscall.Lstat(name, &lstat);
if e != 0 {
- return nil, ErrnoToError(e);
+ return nil, &PathError{"stat", name, Errno(e)};
}
statp := &lstat;
if lstat.Mode & syscall.S_IFMT == syscall.S_IFLNK {
var stat syscall.Stat_t;
e := syscall.Fstat(file.fd, &stat);
if e != 0 {
- return nil, ErrnoToError(e)
+ return nil, &PathError{"stat", file.name, Errno(e)};
}
return dirFromStat(file.name, new(Dir), &stat, &stat), nil
}
var stat syscall.Stat_t;
e := syscall.Lstat(name, &stat);
if e != 0 {
- return nil, ErrnoToError(e)
+ return nil, &PathError{"lstat", name, Errno(e)};
}
return dirFromStat(name, new(Dir), &stat, &stat), nil
}
// Chdir changes the current working directory to the named directory.
func Chdir(dir string) Error {
- return ErrnoToError(syscall.Chdir(dir));
+ if e := syscall.Chdir(dir); e != 0 {
+ return &PathError{"chdir", dir, Errno(e)};
+ }
+ return nil;
}
// Chdir changes the current working directory to the file,
// which must be a directory.
func (f *File) Chdir() Error {
- return ErrnoToError(syscall.Fchdir(f.fd));
+ if e := syscall.Fchdir(f.fd); e != 0 {
+ return &PathError{"chdir", f.name, Errno(e)};
+ }
+ return nil;
}
// Remove removes the named file or directory.
if e1 != syscall.ENOTDIR {
e = e1;
}
- return ErrnoToError(e);
+ return &PathError{"remove", name, Errno(e)};
+}
+
+// LinkError records an error during a link or symlink
+// system call and the paths that caused it.
+type LinkError struct {
+ Op string;
+ Old string;
+ New string;
+ Error Error;
+}
+
+func (e *LinkError) String() string {
+ return e.Op + " " + e.Old + " " + e.New + ": " + e.Error.String();
}
// Link creates a hard link.
func Link(oldname, newname string) Error {
- return ErrnoToError(syscall.Link(oldname, newname));
+ e := syscall.Link(oldname, newname);
+ if e != 0 {
+ return &LinkError{"link", oldname, newname, Errno(e)};
+ }
+ return nil;
}
// Symlink creates a symbolic link.
func Symlink(oldname, newname string) Error {
- return ErrnoToError(syscall.Symlink(oldname, newname));
+ e := syscall.Symlink(oldname, newname);
+ if e != 0 {
+ return &LinkError{"symlink", oldname, newname, Errno(e)};
+ }
+ return nil;
}
// Readlink reads the contents of a symbolic link: the destination of
b := make([]byte, len);
n, e := syscall.Readlink(name, b);
if e != 0 {
- return "", ErrnoToError(e);
+ return "", &PathError{"readlink", name, Errno(e)};
}
if n < len {
return string(b[0:n]), nil;
// Chmod changes the mode of the named file to mode.
// If the file is a symbolic link, it changes the uid and gid of the link's target.
func Chmod(name string, mode int) Error {
- return ErrnoToError(syscall.Chmod(name, mode));
+ if e := syscall.Chmod(name, mode); e != 0 {
+ return &PathError{"chmod", name, Errno(e)};
+ }
+ return nil;
}
// Chmod changes the mode of the file to mode.
func (f *File) Chmod(mode int) Error {
- return ErrnoToError(syscall.Fchmod(f.fd, mode));
+ if e := syscall.Fchmod(f.fd, mode); e != 0 {
+ return &PathError{"chmod", f.name, Errno(e)};
+ }
+ return nil;
}
// Chown changes the numeric uid and gid of the named file.
// If the file is a symbolic link, it changes the uid and gid of the link's target.
func Chown(name string, uid, gid int) Error {
- return ErrnoToError(syscall.Chown(name, uid, gid));
+ if e := syscall.Chown(name, uid, gid); e != 0 {
+ return &PathError{"chown", name, Errno(e)};
+ }
+ return nil;
}
// Lchown changes the numeric uid and gid of the named file.
// If the file is a symbolic link, it changes the uid and gid of the link itself.
func Lchown(name string, uid, gid int) Error {
- return ErrnoToError(syscall.Lchown(name, uid, gid));
+ if e := syscall.Lchown(name, uid, gid); e != 0 {
+ return &PathError{"lchown", name, Errno(e)};
+ }
+ return nil;
}
// Chown changes the numeric uid and gid of the named file.
func (f *File) Chown(uid, gid int) Error {
- return ErrnoToError(syscall.Fchown(f.fd, uid, gid));
+ if e := syscall.Fchown(f.fd, uid, gid); e != 0 {
+ return &PathError{"chown", f.name, Errno(e)};
+ }
+ return nil;
}
// Truncate changes the size of the named file.
// If the file is a symbolic link, it changes the size of the link's target.
func Truncate(name string, size int64) Error {
- return ErrnoToError(syscall.Truncate(name, size));
+ if e := syscall.Truncate(name, size); e != 0 {
+ return &PathError{"truncate", name, Errno(e)};
+ }
+ return nil;
}
// Truncate changes the size of the file.
// It does not change the I/O offset.
func (f *File) Truncate(size int64) Error {
- return ErrnoToError(syscall.Ftruncate(f.fd, size));
+ if e := syscall.Ftruncate(f.fd, size); e != 0 {
+ return &PathError{"truncate", f.name, Errno(e)};
+ }
+ return nil;
}
// If the operating system provides a Getwd call, use it.
if syscall.ImplementsGetwd {
s, e := syscall.Getwd();
- return s, ErrnoToError(e);
+ return s, NewSyscallError("getwd", e);
}
// Otherwise, we're trying to find our way back to ".".
}
f.Close();
}
+
+type openErrorTest struct {
+ path string;
+ mode int;
+ error string;
+}
+
+var openErrorTests = []openErrorTest {
+ openErrorTest {
+ "/etc/no-such-file",
+ O_RDONLY,
+ "open /etc/no-such-file: no such file or directory",
+ },
+ openErrorTest {
+ "/etc",
+ O_WRONLY,
+ "open /etc: is a directory",
+ },
+ openErrorTest {
+ "/etc/passwd/group",
+ O_WRONLY,
+ "open /etc/passwd/group: not a directory",
+ },
+}
+
+func TestOpenError(t *testing.T) {
+ for i, tt := range openErrorTests {
+ f, err := Open(tt.path, tt.mode, 0);
+ if err == nil {
+ t.Errorf("Open(%q, %d) succeeded", tt.path, tt.mode);
+ f.Close();
+ continue;
+ }
+ if s := err.String(); s != tt.error {
+ t.Errorf("Open(%q, %d) = _, %q; want %q", tt.path, tt.mode, s, tt.error);
+ }
+ }
+}
import "os"
-// PathError reports an error and the file path where it occurred.
-type PathError struct {
- Path string;
- Error Error;
-}
-
-func (p *PathError) String() string {
- return p.Path + ": " + p.Error.String();
-}
-
// MkdirAll creates a directory named path,
// along with any necessary parents, and returns nil,
// or else returns an error.
if dir.IsDirectory() {
return nil;
}
- return &PathError{path, ENOTDIR};
+ return &PathError{"mkdir", path, ENOTDIR};
}
// Doesn't already exist; make sure parent does.
if err1 == nil && dir.IsDirectory() {
return nil;
}
- return &PathError{path, err};
+ return err;
}
return nil;
}
}
// Otherwise, is this a directory we need to recurse into?
- dir, err1 := os.Lstat(path);
- if err1 != nil {
- return &PathError{path, err1};
+ dir, err := os.Lstat(path);
+ if err != nil {
+ return err;
}
if !dir.IsDirectory() {
// Not a directory; return the error from Remove.
- return &PathError{path, err};
+ return err;
}
// Directory.
fd, err := Open(path, os.O_RDONLY, 0);
if err != nil {
- return &PathError{path, err};
+ return err;
}
defer fd.Close();
names, err1 := fd.Readdirnames(100);
for i, name := range names {
err1 := RemoveAll(path + "/" + name);
- if err1 != nil && err == nil {
+ if err == nil {
err = err1;
}
}
// If Readdirnames returned an error, use it.
- if err1 != nil && err == nil {
- err = &PathError{path, err1};
+ if err == nil {
+ err = err1;
}
if len(names) == 0 {
break;
}
// Remove directory.
- err1 = Remove(path);
- if err1 != nil && err == nil {
- err = &PathError{path, err1};
+ err1 := Remove(path);
+ if err == nil {
+ err = err1;
}
return err;
}
// Getgroups returns a list of the numeric ids of groups that the caller belongs to.
func Getgroups() ([]int, os.Error) {
gids, errno := syscall.Getgroups();
- return gids, ErrnoToError(errno);
+ return gids, NewSyscallError("getgroups", errno);
}
// Exit causes the current program to exit with the given status code.
func Time() (sec int64, nsec int64, err Error) {
var tv syscall.Timeval;
if errno := syscall.Gettimeofday(&tv); errno != 0 {
- return 0, 0, ErrnoToError(errno)
+ return 0, 0, NewSyscallError("gettimeofday", errno);
}
return int64(tv.Sec), int64(tv.Usec)*1000, err;
}
all: $(LIB) runtime.acid
-install: $(LIB) runtime.acid
+TARG=$(GOROOT)/pkg/$(GOOS)_$(GOARCH)/$(LIB)
+
+install: $(TARG)
+
+$(TARG): $(LIB) runtime.acid
test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)
- cp $(LIB) $(GOROOT)/pkg/$(GOOS)_$(GOARCH)/$(LIB)
+ cp $(LIB) $(TARG)
cp runtime.acid $(GOROOT)/acid/runtime.acid
$(LIB): $(OFILES)
$(OFILES): $(HFILES)
nuke:
- rm -f *.[568] *.a $(GOROOT)/lib/$(LIB)
+ rm -f *.[568] *.a $(TARG)
clean:
rm -f *.[568] *.a runtime.acid cgo2c */asm.h
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
+
# DO NOT EDIT. Automatically generated by gobuild.
# gobuild -m >Makefile
coverage: packages
gotest
- 6cov -g `pwd` | grep -v '_test\.go:'
+ 6cov -g $$(pwd) | grep -v '_test\.go:'
%.$O: %.go
$(GC) -I_obj $*.go
$(AS) $*.s
O1=\
- atoi.$O\
decimal.$O\
itoa.$O\
quote.$O\
O2=\
+ atoi.$O\
ftoa.$O\
O3=\
_obj$D/strconv.a: phases
a1: $(O1)
- $(AR) grc _obj$D/strconv.a atoi.$O decimal.$O itoa.$O quote.$O
+ $(AR) grc _obj$D/strconv.a decimal.$O itoa.$O quote.$O
rm -f $(O1)
a2: $(O2)
- $(AR) grc _obj$D/strconv.a ftoa.$O
+ $(AR) grc _obj$D/strconv.a atoi.$O ftoa.$O
rm -f $(O2)
a3: $(O3)
// Atof32 returns the nearest floating point number rounded
// using IEEE754 unbiased rounding.
//
-// If s is not syntactically well-formed, Atof32 returns err = os.EINVAL.
+// The errors that Atof32 returns have concrete type *NumError
+// and include err.Num = s.
+//
+// If s is not syntactically well-formed, Atof32 returns err.Error = os.EINVAL.
//
// If s is syntactically well-formed but is more than 1/2 ULP
// away from the largest floating point number of the given size,
-// Atof32 returns f = ±Inf, err = os.ERANGE.
+// Atof32 returns f = ±Inf, err.Error = os.ERANGE.
func Atof32(s string) (f float32, err os.Error) {
neg, d, trunc, ok := stringToDecimal(s);
if !ok {
- return 0, os.EINVAL;
+ return 0, &NumError{s, os.EINVAL};
}
if optimize {
if f, ok := decimalAtof32(neg, d, trunc); ok {
b, ovf := decimalToFloatBits(neg, d, trunc, &float32info);
f = math.Float32frombits(uint32(b));
if ovf {
- err = os.ERANGE;
+ err = &NumError{s, os.ERANGE};
}
return f, err
}
func Atof64(s string) (f float64, err os.Error) {
neg, d, trunc, ok := stringToDecimal(s);
if !ok {
- return 0, os.EINVAL;
+ return 0, &NumError{s, os.EINVAL};
}
if optimize {
if f, ok := decimalAtof64(neg, d, trunc); ok {
b, ovf := decimalToFloatBits(neg, d, trunc, &float64info);
f = math.Float64frombits(b);
if ovf {
- err = os.ERANGE;
+ err = &NumError{s, os.ERANGE};
}
return f, err
}
import (
"fmt";
"os";
+ "reflect";
"strconv";
"testing"
)
atofTest{ ".e-1", "0", os.EINVAL },
}
+func init() {
+ // The atof routines return NumErrors wrapping
+ // the error and the string. Convert the table above.
+ for i := range atoftests {
+ test := &atoftests[i];
+ if test.err != nil {
+ test.err = &NumError{test.in, test.err}
+ }
+ }
+}
+
func testAtof(t *testing.T, opt bool) {
oldopt := strconv.optimize;
strconv.optimize = opt;
test := &atoftests[i];
out, err := strconv.Atof64(test.in);
outs := strconv.Ftoa64(out, 'g', -1);
- if outs != test.out || err != test.err {
+ if outs != test.out || !reflect.DeepEqual(err, test.err) {
t.Errorf("strconv.Atof64(%v) = %v, %v want %v, %v\n",
test.in, out, err, test.out, test.err);
}
if float64(float32(out)) == out {
out32, err := strconv.Atof32(test.in);
outs := strconv.Ftoa32(out32, 'g', -1);
- if outs != test.out || err != test.err {
+ if outs != test.out || !reflect.DeepEqual(err, test.err) {
t.Errorf("strconv.Atof32(%v) = %v, %v want %v, %v # %v\n",
test.in, out32, err, test.out, test.err, out);
}
if FloatSize == 64 || float64(float32(out)) == out {
outf, err := strconv.Atof(test.in);
outs := strconv.Ftoa(outf, 'g', -1);
- if outs != test.out || err != test.err {
+ if outs != test.out || !reflect.DeepEqual(err, test.err) {
t.Errorf("strconv.Ftoa(%v) = %v, %v want %v, %v # %v\n",
test.in, outf, err, test.out, test.err, out);
}
// license that can be found in the LICENSE file.
package strconv
-import "os"
+import (
+ "os";
+ "strconv"
+)
+
+type NumError struct {
+ Num string;
+ Error os.Error;
+}
+
+func (e *NumError) String() string {
+ return "parsing " + e.Num + ": " + e.Error.String();
+}
+
func computeIntsize() uint {
siz := uint(8);
// Btoui64 interprets a string s in an arbitrary base b (2 to 36)
// and returns the corresponding value n.
//
-// Btoui64 returns err == os.EINVAL if b is out of
-// range or s is empty or contains invalid digits.
-// It returns err == os.ERANGE if the value corresponding
-// to s cannot be represented by a uint64.
+// The errors that Btoui64 returns have concrete type *NumError
+// and include err.Num = s. If s is empty or contains invalid
+// 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) {
- if b < 2 || b > 36 || len(s) < 1 {
- return 0, os.EINVAL;
+ if b < 2 || b > 36 {
+ err = os.ErrorString("invalid base " + Itoa(b));
+ goto Error;
+ }
+ if len(s) < 1 {
+ err = os.EINVAL;
+ goto Error;
}
n = 0;
case 'A' <= s[i] && s[i] <= 'Z':
v = s[i] - 'A' + 10;
default:
- return 0, os.EINVAL;
+ n = 0;
+ err = os.EINVAL;
+ goto Error;
}
if int(v) >= b {
- return 0, os.EINVAL;
+ n = 0;
+ err = os.EINVAL;
+ goto Error;
}
if n >= cutoff {
// n*b overflows
- return 1<<64-1, os.ERANGE;
+ n = 1<<64-1;
+ err = os.ERANGE;
+ goto Error;
}
n *= uint64(b);
n1 := n+uint64(v);
if n1 < n {
// n+v overflows
- return 1<<64-1, os.ERANGE;
+ n = 1<<64-1;
+ err = os.ERANGE;
+ goto Error;
}
n = n1;
}
return n, nil;
+
+Error:
+ return n, &NumError{s, err};
}
// Atoui64 interprets a string s as an unsigned decimal, octal, or
func Atoui64(s string) (n uint64, err os.Error) {
// Empty string bad.
if len(s) == 0 {
- return 0, os.EINVAL
+ return 0, &NumError{s, os.EINVAL}
}
// Look for octal, hex prefix.
func Atoi64(s string) (i int64, err os.Error) {
// Empty string bad.
if len(s) == 0 {
- return 0, os.EINVAL
+ return 0, &NumError{s, os.EINVAL}
}
// Pick off leading sign.
// Convert unsigned and check range.
var un uint64;
un, err = Atoui64(s);
- if err != nil && err != os.ERANGE {
+ if err != nil && err.(*NumError).Error != os.ERANGE {
return 0, err
}
if !neg && un >= 1<<63 {
- return 1<<63-1, os.ERANGE
+ return 1<<63-1, &NumError{s, os.ERANGE}
}
if neg && un > 1<<63 {
- return -1<<63, os.ERANGE
+ return -1<<63, &NumError{s, os.ERANGE}
}
n := int64(un);
if neg {
// Atoui is like Atoui64 but returns its result as a uint.
func Atoui(s string) (i uint, err os.Error) {
i1, e1 := Atoui64(s);
- if e1 != nil && e1 != os.ERANGE {
+ if e1 != nil && e1.(*NumError).Error != os.ERANGE {
return 0, e1
}
i = uint(i1);
if uint64(i) != i1 {
- // TODO: return uint(^0), os.ERANGE.
- i1 = 1<<64-1;
- return uint(i1), os.ERANGE
+ return ^uint(0), &NumError{s, os.ERANGE}
}
return i, nil
}
// Atoi is like Atoi64 but returns its result as an int.
func Atoi(s string) (i int, err os.Error) {
i1, e1 := Atoi64(s);
- if e1 != nil && e1 != os.ERANGE {
+ if e1 != nil && e1.(*NumError).Error != os.ERANGE {
return 0, e1
}
i = int(i1);
if int64(i) != i1 {
if i1 < 0 {
- return -1<<(intsize-1), os.ERANGE
+ return -1<<(intsize-1), &NumError{s, os.ERANGE}
}
- return 1<<(intsize-1) - 1, os.ERANGE
+ return 1<<(intsize-1) - 1, &NumError{s, os.ERANGE}
}
return i, nil
}
import (
"fmt";
"os";
+ "reflect";
"strconv";
"testing"
)
err os.Error;
}
-var atoi64test = []atoi64Test {
+var atoi64tests = []atoi64Test {
atoi64Test{"", 0, os.EINVAL},
atoi64Test{"0", 0, nil},
atoi64Test{"-0", 0, nil},
atoi32Test{"-2147483649", -1<<31, os.ERANGE},
}
+func init() {
+ // The atoi routines return NumErrors wrapping
+ // the error and the string. Convert the tables above.
+ for i := range atoui64tests {
+ test := &atoui64tests[i];
+ if test.err != nil {
+ test.err = &NumError{test.in, test.err}
+ }
+ }
+ for i := range atoi64tests {
+ test := &atoi64tests[i];
+ if test.err != nil {
+ test.err = &NumError{test.in, test.err}
+ }
+ }
+ for i := range atoui32tests {
+ test := &atoui32tests[i];
+ if test.err != nil {
+ test.err = &NumError{test.in, test.err}
+ }
+ }
+ for i := range atoi32tests {
+ test := &atoi32tests[i];
+ if test.err != nil {
+ test.err = &NumError{test.in, test.err}
+ }
+ }
+}
+
func TestAtoui64(t *testing.T) {
- for i := 0; i < len(atoui64tests); i++ {
+ for i := range atoui64tests {
test := &atoui64tests[i];
out, err := strconv.Atoui64(test.in);
- if test.out != out || test.err != err {
+ if test.out != out || !reflect.DeepEqual(test.err, err) {
t.Errorf("strconv.Atoui64(%v) = %v, %v want %v, %v\n",
test.in, out, err, test.out, test.err);
}
}
func TestAtoi64(t *testing.T) {
- for i := 0; i < len(atoi64test); i++ {
- test := &atoi64test[i];
+ for i := range atoi64tests {
+ test := &atoi64tests[i];
out, err := strconv.Atoi64(test.in);
- if test.out != out || test.err != err {
+ if test.out != out || !reflect.DeepEqual(test.err, err) {
t.Errorf("strconv.Atoi64(%v) = %v, %v want %v, %v\n",
test.in, out, err, test.out, test.err);
}
func TestAtoui(t *testing.T) {
switch intsize {
case 32:
- for i := 0; i < len(atoui32tests); i++ {
+ for i := range atoui32tests {
test := &atoui32tests[i];
out, err := strconv.Atoui(test.in);
- if test.out != uint32(out) || test.err != err {
+ if test.out != uint32(out) || !reflect.DeepEqual(test.err, err) {
t.Errorf("strconv.Atoui(%v) = %v, %v want %v, %v\n",
test.in, out, err, test.out, test.err);
}
}
case 64:
- for i := 0; i < len(atoui64tests); i++ {
+ for i := range atoui64tests {
test := &atoui64tests[i];
out, err := strconv.Atoui(test.in);
- if test.out != uint64(out) || test.err != err {
+ if test.out != uint64(out) || !reflect.DeepEqual(test.err, err) {
t.Errorf("strconv.Atoui(%v) = %v, %v want %v, %v\n",
test.in, out, err, test.out, test.err);
}
func TestAtoi(t *testing.T) {
switch intsize {
case 32:
- for i := 0; i < len(atoi32tests); i++ {
+ for i := range atoi32tests {
test := &atoi32tests[i];
out, err := strconv.Atoi(test.in);
- if test.out != int32(out) || test.err != err {
+ if test.out != int32(out) || !reflect.DeepEqual(test.err, err) {
t.Errorf("strconv.Atoi(%v) = %v, %v want %v, %v\n",
test.in, out, err, test.out, test.err);
}
}
case 64:
- for i := 0; i < len(atoi64test); i++ {
- test := &atoi64test[i];
+ for i := range atoi64tests {
+ test := &atoi64tests[i];
out, err := strconv.Atoi(test.in);
- if test.out != int64(out) || test.err != err {
+ if test.out != int64(out) || !reflect.DeepEqual(test.err, err) {
t.Errorf("strconv.Atoi(%v) = %v, %v want %v, %v\n",
test.in, out, err, test.out, test.err);
}
// Sleep pauses the current goroutine for ns nanoseconds.
// It returns os.EINTR if interrupted.
func Sleep(ns int64) os.Error {
- return os.ErrnoToError(syscall.Sleep(ns));
+ return os.NewSyscallError("sleep", syscall.Sleep(ns));
}
// SecondsToLocalTime converts sec, in number of seconds since the Unix epoch,
// into a parsed Time value in the local time zone.
func SecondsToLocalTime(sec int64) *Time {
- z, offset, err := time.lookupTimezone(sec);
- if err != nil {
- return SecondsToUTC(sec)
- }
+ z, offset := time.lookupTimezone(sec);
t := SecondsToUTC(sec+int64(offset));
t.Zone = z;
t.ZoneOffset = offset;
zoneDir = "/usr/share/zoneinfo/";
)
-// Errors that can be generated recovering time zone information.
-type TimeZoneError struct {
- os.ErrorString
-}
-
-var errShort = TimeZoneError{ "time: short zone file" }
-var errInvalid = TimeZoneError{ "time: invalid zone file" }
-
// Simple I/O interface to binary blob of data.
type data struct {
p []byte;
isstd, isutc bool; // ignored - no idea what these mean
}
-func parseinfo(bytes []byte) (zt []zonetime, err os.Error) {
+func parseinfo(bytes []byte) (zt []zonetime, ok bool) {
d := data{bytes, false};
// 4-byte magic "TZif"
if magic := d.read(4); string(magic) != "TZif" {
- return nil, TimeZoneError{ "time: bad zone magic" }
+ return nil, false
}
// 1-byte version, then 15 bytes of padding
var p []byte;
if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' {
- return nil, TimeZoneError { "time: bad zone file version" }
+ return nil, false
}
vers := p[0];
for i := 0; i < 6; i++ {
nn, ok := d.big4();
if !ok {
- return nil, errShort
+ return nil, false
}
n[i] = int(nn);
}
isutc := d.read(n[NUTCLocal]);
if d.error { // ran out of data
- return nil, errShort
+ return nil, false
}
// If version == 2, the entire file repeats, this time using
var ok bool;
var n uint32;
if n, ok = zonedata.big4(); !ok {
- return nil, errShort
+ return nil, false
}
z[i].utcoff = int(n);
var b byte;
if b, ok = zonedata.byte(); !ok {
- return nil, errShort
+ return nil, false
}
z[i].isdst = b != 0;
if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
- return nil, errInvalid
+ return nil, false
}
z[i].name = byteString(abbrev[b:len(abbrev)])
}
var ok bool;
var n uint32;
if n, ok = txtimes.big4(); !ok {
- return nil, errShort
+ return nil, false
}
zt[i].time = int32(n);
if int(txzones[i]) >= len(z) {
- return nil, errInvalid
+ return nil, false
}
zt[i].zone = &z[txzones[i]];
if i < len(isstd) {
zt[i].isutc = isutc[i] != 0
}
}
- return zt, nil
+ return zt, true
}
-func readinfofile(name string) ([]zonetime, os.Error) {
+func readinfofile(name string) ([]zonetime, bool) {
buf, err := io.ReadFile(name);
if err != nil {
- goto Error;
- }
- tx, err := parseinfo(buf);
- if err != nil {
- goto Error;
- }
- return tx, nil;
-
-Error:
- if tzerr, ok := err.(TimeZoneError); ok {
- tzerr.ErrorString = os.ErrorString(tzerr.String() + ": " + name)
+ return nil, false
}
- return nil, err
+ return parseinfo(buf);
}
var zones []zonetime
-var zoneerr os.Error
func setupZone() {
// consult $TZ to find the time zone to use.
// $TZ="foo" means use /usr/share/zoneinfo/foo.
tz, err := os.Getenv("TZ");
- var file string;
+ var ok bool;
switch {
case err == os.ENOENV:
- zones, zoneerr = readinfofile("/etc/localtime");
- case err != nil:
- zoneerr = err;
+ zones, ok = readinfofile("/etc/localtime");
case len(tz) > 0:
- zones, zoneerr = readinfofile(zoneDir + tz);
+ zones, ok = readinfofile(zoneDir + tz);
case len(tz) == 0:
// do nothing: use UTC
}
}
-func lookupTimezone(sec int64) (zone string, offset int, err os.Error) {
+func lookupTimezone(sec int64) (zone string, offset int) {
once.Do(setupZone);
- if zoneerr != nil || len(zones) == 0 {
- return "UTC", 0, zoneerr
+ if len(zones) == 0 {
+ return "UTC", 0
}
// Binary search for entry with largest time <= sec
}
}
z := tz[0].zone;
- return z.name, z.utcoff, nil
+ return z.name, z.utcoff
}