Home | History | Annotate | Download | only in dist
      1 // Copyright 2012 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // This file is forked from go/build/read.go.
      6 // (cmd/dist must not import go/build because we do not want it to be
      7 // sensitive to the specific version of go/build present in $GOROOT_BOOTSTRAP.)
      8 
      9 package main
     10 
     11 import (
     12 	"bufio"
     13 	"errors"
     14 	"io"
     15 	"strconv"
     16 	"strings"
     17 	"unicode/utf8"
     18 )
     19 
     20 type importReader struct {
     21 	b    *bufio.Reader
     22 	buf  []byte
     23 	peek byte
     24 	err  error
     25 	eof  bool
     26 	nerr int
     27 }
     28 
     29 func isIdent(c byte) bool {
     30 	return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf
     31 }
     32 
     33 var (
     34 	errSyntax = errors.New("syntax error")
     35 	errNUL    = errors.New("unexpected NUL in input")
     36 )
     37 
     38 // syntaxError records a syntax error, but only if an I/O error has not already been recorded.
     39 func (r *importReader) syntaxError() {
     40 	if r.err == nil {
     41 		r.err = errSyntax
     42 	}
     43 }
     44 
     45 // readByte reads the next byte from the input, saves it in buf, and returns it.
     46 // If an error occurs, readByte records the error in r.err and returns 0.
     47 func (r *importReader) readByte() byte {
     48 	c, err := r.b.ReadByte()
     49 	if err == nil {
     50 		r.buf = append(r.buf, c)
     51 		if c == 0 {
     52 			err = errNUL
     53 		}
     54 	}
     55 	if err != nil {
     56 		if err == io.EOF {
     57 			r.eof = true
     58 		} else if r.err == nil {
     59 			r.err = err
     60 		}
     61 		c = 0
     62 	}
     63 	return c
     64 }
     65 
     66 // peekByte returns the next byte from the input reader but does not advance beyond it.
     67 // If skipSpace is set, peekByte skips leading spaces and comments.
     68 func (r *importReader) peekByte(skipSpace bool) byte {
     69 	if r.err != nil {
     70 		if r.nerr++; r.nerr > 10000 {
     71 			panic("go/build: import reader looping")
     72 		}
     73 		return 0
     74 	}
     75 
     76 	// Use r.peek as first input byte.
     77 	// Don't just return r.peek here: it might have been left by peekByte(false)
     78 	// and this might be peekByte(true).
     79 	c := r.peek
     80 	if c == 0 {
     81 		c = r.readByte()
     82 	}
     83 	for r.err == nil && !r.eof {
     84 		if skipSpace {
     85 			// For the purposes of this reader, semicolons are never necessary to
     86 			// understand the input and are treated as spaces.
     87 			switch c {
     88 			case ' ', '\f', '\t', '\r', '\n', ';':
     89 				c = r.readByte()
     90 				continue
     91 
     92 			case '/':
     93 				c = r.readByte()
     94 				if c == '/' {
     95 					for c != '\n' && r.err == nil && !r.eof {
     96 						c = r.readByte()
     97 					}
     98 				} else if c == '*' {
     99 					var c1 byte
    100 					for (c != '*' || c1 != '/') && r.err == nil {
    101 						if r.eof {
    102 							r.syntaxError()
    103 						}
    104 						c, c1 = c1, r.readByte()
    105 					}
    106 				} else {
    107 					r.syntaxError()
    108 				}
    109 				c = r.readByte()
    110 				continue
    111 			}
    112 		}
    113 		break
    114 	}
    115 	r.peek = c
    116 	return r.peek
    117 }
    118 
    119 // nextByte is like peekByte but advances beyond the returned byte.
    120 func (r *importReader) nextByte(skipSpace bool) byte {
    121 	c := r.peekByte(skipSpace)
    122 	r.peek = 0
    123 	return c
    124 }
    125 
    126 // readKeyword reads the given keyword from the input.
    127 // If the keyword is not present, readKeyword records a syntax error.
    128 func (r *importReader) readKeyword(kw string) {
    129 	r.peekByte(true)
    130 	for i := 0; i < len(kw); i++ {
    131 		if r.nextByte(false) != kw[i] {
    132 			r.syntaxError()
    133 			return
    134 		}
    135 	}
    136 	if isIdent(r.peekByte(false)) {
    137 		r.syntaxError()
    138 	}
    139 }
    140 
    141 // readIdent reads an identifier from the input.
    142 // If an identifier is not present, readIdent records a syntax error.
    143 func (r *importReader) readIdent() {
    144 	c := r.peekByte(true)
    145 	if !isIdent(c) {
    146 		r.syntaxError()
    147 		return
    148 	}
    149 	for isIdent(r.peekByte(false)) {
    150 		r.peek = 0
    151 	}
    152 }
    153 
    154 // readString reads a quoted string literal from the input.
    155 // If an identifier is not present, readString records a syntax error.
    156 func (r *importReader) readString(save *[]string) {
    157 	switch r.nextByte(true) {
    158 	case '`':
    159 		start := len(r.buf) - 1
    160 		for r.err == nil {
    161 			if r.nextByte(false) == '`' {
    162 				if save != nil {
    163 					*save = append(*save, string(r.buf[start:]))
    164 				}
    165 				break
    166 			}
    167 			if r.eof {
    168 				r.syntaxError()
    169 			}
    170 		}
    171 	case '"':
    172 		start := len(r.buf) - 1
    173 		for r.err == nil {
    174 			c := r.nextByte(false)
    175 			if c == '"' {
    176 				if save != nil {
    177 					*save = append(*save, string(r.buf[start:]))
    178 				}
    179 				break
    180 			}
    181 			if r.eof || c == '\n' {
    182 				r.syntaxError()
    183 			}
    184 			if c == '\\' {
    185 				r.nextByte(false)
    186 			}
    187 		}
    188 	default:
    189 		r.syntaxError()
    190 	}
    191 }
    192 
    193 // readImport reads an import clause - optional identifier followed by quoted string -
    194 // from the input.
    195 func (r *importReader) readImport(imports *[]string) {
    196 	c := r.peekByte(true)
    197 	if c == '.' {
    198 		r.peek = 0
    199 	} else if isIdent(c) {
    200 		r.readIdent()
    201 	}
    202 	r.readString(imports)
    203 }
    204 
    205 // readComments is like ioutil.ReadAll, except that it only reads the leading
    206 // block of comments in the file.
    207 func readComments(f io.Reader) ([]byte, error) {
    208 	r := &importReader{b: bufio.NewReader(f)}
    209 	r.peekByte(true)
    210 	if r.err == nil && !r.eof {
    211 		// Didn't reach EOF, so must have found a non-space byte. Remove it.
    212 		r.buf = r.buf[:len(r.buf)-1]
    213 	}
    214 	return r.buf, r.err
    215 }
    216 
    217 // readimports returns the imports found in the named file.
    218 func readimports(file string) []string {
    219 	var imports []string
    220 	r := &importReader{b: bufio.NewReader(strings.NewReader(readfile(file)))}
    221 	r.readKeyword("package")
    222 	r.readIdent()
    223 	for r.peekByte(true) == 'i' {
    224 		r.readKeyword("import")
    225 		if r.peekByte(true) == '(' {
    226 			r.nextByte(false)
    227 			for r.peekByte(true) != ')' && r.err == nil {
    228 				r.readImport(&imports)
    229 			}
    230 			r.nextByte(false)
    231 		} else {
    232 			r.readImport(&imports)
    233 		}
    234 	}
    235 
    236 	for i := range imports {
    237 		unquoted, err := strconv.Unquote(imports[i])
    238 		if err != nil {
    239 			fatalf("reading imports from %s: %v", file, err)
    240 		}
    241 		imports[i] = unquoted
    242 	}
    243 
    244 	return imports
    245 }
    246