<commit_before>package weed_server

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"mime"
	"net/http"
	"net/url"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/images"
	"github.com/chrislusf/seaweedfs/weed/operation"
	"github.com/chrislusf/seaweedfs/weed/stats"
	"github.com/chrislusf/seaweedfs/weed/storage/needle"
	"github.com/chrislusf/seaweedfs/weed/util"
)

var fileNameEscaper = strings.NewReplacer("\\", "\\\\", "\"", "\\\"")

func (vs *VolumeServer) GetOrHeadHandler(w http.ResponseWriter, r *http.Request) {

	stats.VolumeServerRequestCounter.WithLabelValues("get").Inc()
	start := time.Now()
	defer func() { stats.VolumeServerRequestHistogram.WithLabelValues("get").Observe(time.Since(start).Seconds()) }()

	n := new(needle.Needle)
	vid, fid, filename, ext, _ := parseURLPath(r.URL.Path)

	if !vs.maybeCheckJwtAuthorization(r, vid, fid, false) {
		writeJsonError(w, r, http.StatusUnauthorized, errors.New("wrong jwt"))
		return
	}

	volumeId, err := needle.NewVolumeId(vid)
	if err != nil {
		glog.V(2).Infof("parsing vid %s: %v", r.URL.Path, err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	err = n.ParsePath(fid)
	if err != nil {
		glog.V(2).Infof("parsing fid %s: %v", r.URL.Path, err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	// glog.V(4).Infoln("volume", volumeId, "reading", n)
	hasVolume := vs.store.HasVolume(volumeId)
	_, hasEcVolume := vs.store.FindEcVolume(volumeId)
	if !hasVolume && !hasEcVolume {
		if !vs.ReadRedirect {
			glog.V(2).Infoln("volume is not local:", err, r.URL.Path)
			w.WriteHeader(http.StatusNotFound)
			return
		}
		lookupResult, err := operation.Lookup(vs.GetMaster(), volumeId.String())
		glog.V(2).Infoln("volume", volumeId, "found on", lookupResult, "error", err)
		if err == nil && len(lookupResult.Locations) > 0 {
			u, _ := url.Parse(util.NormalizeUrl(lookupResult.Locations[0].PublicUrl))
			u.Path = fmt.Sprintf("%s/%s,%s", u.Path, vid, fid)
			arg := url.Values{}
			if c := r.FormValue("collection"); c != "" {
				arg.Set("collection", c)
			}
			u.RawQuery = arg.Encode()
			http.Redirect(w, r, u.String(), http.StatusMovedPermanently)

		} else {
			glog.V(2).Infoln("lookup error:", err, r.URL.Path)
			w.WriteHeader(http.StatusNotFound)
		}
		return
	}
	cookie := n.Cookie
	var count int
	if hasVolume {
		count, err = vs.store.ReadVolumeNeedle(volumeId, n)
	} else if hasEcVolume {
		count, err = vs.store.ReadEcShardNeedle(volumeId, n)
	}
	// glog.V(4).Infoln("read bytes", count, "error", err)
	if err != nil || count < 0 {
		glog.V(0).Infof("read %s isNormalVolume %v error: %v", r.URL.Path, hasVolume, err)
		w.WriteHeader(http.StatusNotFound)
		return
	}
	if n.Cookie != cookie {
		glog.V(0).Infof("request %s with cookie:%x expected:%x from %s agent %s", r.URL.Path, cookie, n.Cookie, r.RemoteAddr, r.UserAgent())
		w.WriteHeader(http.StatusNotFound)
		return
	}
	if n.LastModified != 0 {
		w.Header().Set("Last-Modified", time.Unix(int64(n.LastModified), 0).UTC().Format(http.TimeFormat))
		if r.Header.Get("If-Modified-Since") != "" {
			if t, parseError := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); parseError == nil {
				if t.Unix() >= int64(n.LastModified) {
					w.WriteHeader(http.StatusNotModified)
					return
				}
			}
		}
	}
	if inm := r.Header.Get("If-None-Match"); inm == "\""+n.Etag()+"\"" {
		w.WriteHeader(http.StatusNotModified)
		return
	}
	setEtag(w, n.Etag())

	if n.HasPairs() {
		pairMap := make(map[string]string)
		err = json.Unmarshal(n.Pairs, &pairMap)
		if err != nil {
			glog.V(0).Infoln("Unmarshal pairs error:", err)
		}
		for k, v := range pairMap {
			w.Header().Set(k, v)
		}
	}

	if vs.tryHandleChunkedFile(n, filename, ext, w, r) {
		return
	}

	if n.NameSize > 0 && filename == "" {
		filename = string(n.Name)
		if ext == "" {
			ext = filepath.Ext(filename)
		}
	}
	mtype := ""
	if n.MimeSize > 0 {
		mt := string(n.Mime)
		if !strings.HasPrefix(mt, "application/octet-stream") {
			mtype = mt
		}
	}

	if ext != ".gz" && ext != ".zst" {
		if n.IsCompressed() {
			if _, _, _, shouldResize := shouldResizeImages(ext, r); shouldResize {
				if n.Data, err = util.DecompressData(n.Data); err != nil {
					glog.V(0).Infoln("ungzip error:", err, r.URL.Path)
				}
			} else if strings.Contains(r.Header.Get("Accept-Encoding"), "zstd") && util.IsZstdContent(n.Data) {
				w.Header().Set("Content-Encoding", "zstd")
			} else if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") && util.IsGzippedContent(n.Data) {
				w.Header().Set("Content-Encoding", "gzip")
			} else {
				if n.Data, err = util.DecompressData(n.Data); err != nil {
					glog.V(0).Infoln("uncompress error:", err, r.URL.Path)
				}
			}
		}
	}

	rs := conditionallyResizeImages(bytes.NewReader(n.Data), ext, r)

	if e := writeResponseContent(filename, mtype, rs, w, r); e != nil {
		glog.V(2).Infoln("response write error:", e)
	}
}

func (vs *VolumeServer) tryHandleChunkedFile(n *needle.Needle, fileName string, ext string, w http.ResponseWriter, r *http.Request) (processed bool) {
	if !n.IsChunkedManifest() || r.URL.Query().Get("cm") == "false" {
		return false
	}

	chunkManifest, e := operation.LoadChunkManifest(n.Data, n.IsCompressed())
	if e != nil {
		glog.V(0).Infof("load chunked manifest (%s) error: %v", r.URL.Path, e)
		return false
	}
	if fileName == "" && chunkManifest.Name != "" {
		fileName = chunkManifest.Name
	}

	if ext == "" {
		ext = filepath.Ext(fileName)
	}

	mType := ""
	if chunkManifest.Mime != "" {
		mt := chunkManifest.Mime
		if !strings.HasPrefix(mt, "application/octet-stream") {
			mType = mt
		}
	}

	w.Header().Set("X-File-Store", "chunked")

	chunkedFileReader := operation.NewChunkedFileReader(chunkManifest.Chunks, vs.GetMaster())
	defer chunkedFileReader.Close()

	rs := conditionallyResizeImages(chunkedFileReader, ext, r)

	if e := writeResponseContent(fileName, mType, rs, w, r); e != nil {
		glog.V(2).Infoln("response write error:", e)
	}
	return true
}

func conditionallyResizeImages(originalDataReaderSeeker io.ReadSeeker, ext string, r *http.Request) io.ReadSeeker {
	rs := originalDataReaderSeeker
	if len(ext) > 0 {
		ext = strings.ToLower(ext)
	}
	width, height, mode, shouldResize := shouldResizeImages(ext, r)
	if shouldResize {
		rs, _, _ = images.Resized(ext, originalDataReaderSeeker, width, height, mode)
	}
	return rs
}

func shouldResizeImages(ext string, r *http.Request) (width, height int, mode string, shouldResize bool) {
	if ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".gif" {
		if r.FormValue("width") != "" {
			width, _ = strconv.Atoi(r.FormValue("width"))
		}
		if r.FormValue("height") != "" {
			height, _ = strconv.Atoi(r.FormValue("height"))
		}
	}
	mode = r.FormValue("mode")
	shouldResize = width > 0 || height > 0
	return
}

func writeResponseContent(filename, mimeType string, rs io.ReadSeeker, w http.ResponseWriter, r *http.Request) error {
	totalSize, e := rs.Seek(0, 2)
	if mimeType == "" {
		if ext := filepath.Ext(filename); ext != "" {
			mimeType = mime.TypeByExtension(ext)
		}
	}
	if mimeType != "" {
		w.Header().Set("Content-Type", mimeType)
	}
	w.Header().Set("Accept-Ranges", "bytes")

	adjustHeaderContentDisposition(w, r, filename)

	if r.Method == "HEAD" {
		w.Header().Set("Content-Length", strconv.FormatInt(totalSize, 10))
		return nil
	}

	processRangeRequest(r, w, totalSize, mimeType, func(writer io.Writer, offset int64, size int64) error {
		if _, e = rs.Seek(offset, 0); e != nil {
			return e
		}
		_, e = io.CopyN(writer, rs, size)
		return e
	})
	return nil
}
<commit_msg>volume: avoid special logic for .gz files<commit_after>package weed_server

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"mime"
	"net/http"
	"net/url"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/images"
	"github.com/chrislusf/seaweedfs/weed/operation"
	"github.com/chrislusf/seaweedfs/weed/stats"
	"github.com/chrislusf/seaweedfs/weed/storage/needle"
	"github.com/chrislusf/seaweedfs/weed/util"
)

var fileNameEscaper = strings.NewReplacer("\\", "\\\\", "\"", "\\\"")

func (vs *VolumeServer) GetOrHeadHandler(w http.ResponseWriter, r *http.Request) {

	// println(r.Method + " " + r.URL.Path)

	stats.VolumeServerRequestCounter.WithLabelValues("get").Inc()
	start := time.Now()
	defer func() { stats.VolumeServerRequestHistogram.WithLabelValues("get").Observe(time.Since(start).Seconds()) }()

	n := new(needle.Needle)
	vid, fid, filename, ext, _ := parseURLPath(r.URL.Path)

	if !vs.maybeCheckJwtAuthorization(r, vid, fid, false) {
		writeJsonError(w, r, http.StatusUnauthorized, errors.New("wrong jwt"))
		return
	}

	volumeId, err := needle.NewVolumeId(vid)
	if err != nil {
		glog.V(2).Infof("parsing vid %s: %v", r.URL.Path, err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	err = n.ParsePath(fid)
	if err != nil {
		glog.V(2).Infof("parsing fid %s: %v", r.URL.Path, err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	// glog.V(4).Infoln("volume", volumeId, "reading", n)
	hasVolume := vs.store.HasVolume(volumeId)
	_, hasEcVolume := vs.store.FindEcVolume(volumeId)
	if !hasVolume && !hasEcVolume {
		if !vs.ReadRedirect {
			glog.V(2).Infoln("volume is not local:", err, r.URL.Path)
			w.WriteHeader(http.StatusNotFound)
			return
		}
		lookupResult, err := operation.Lookup(vs.GetMaster(), volumeId.String())
		glog.V(2).Infoln("volume", volumeId, "found on", lookupResult, "error", err)
		if err == nil && len(lookupResult.Locations) > 0 {
			u, _ := url.Parse(util.NormalizeUrl(lookupResult.Locations[0].PublicUrl))
			u.Path = fmt.Sprintf("%s/%s,%s", u.Path, vid, fid)
			arg := url.Values{}
			if c := r.FormValue("collection"); c != "" {
				arg.Set("collection", c)
			}
			u.RawQuery = arg.Encode()
			http.Redirect(w, r, u.String(), http.StatusMovedPermanently)

		} else {
			glog.V(2).Infoln("lookup error:", err, r.URL.Path)
			w.WriteHeader(http.StatusNotFound)
		}
		return
	}
	cookie := n.Cookie
	var count int
	if hasVolume {
		count, err = vs.store.ReadVolumeNeedle(volumeId, n)
	} else if hasEcVolume {
		count, err = vs.store.ReadEcShardNeedle(volumeId, n)
	}
	// glog.V(4).Infoln("read bytes", count, "error", err)
	if err != nil || count < 0 {
		glog.V(0).Infof("read %s isNormalVolume %v error: %v", r.URL.Path, hasVolume, err)
		w.WriteHeader(http.StatusNotFound)
		return
	}
	if n.Cookie != cookie {
		glog.V(0).Infof("request %s with cookie:%x expected:%x from %s agent %s", r.URL.Path, cookie, n.Cookie, r.RemoteAddr, r.UserAgent())
		w.WriteHeader(http.StatusNotFound)
		return
	}
	if n.LastModified != 0 {
		w.Header().Set("Last-Modified", time.Unix(int64(n.LastModified), 0).UTC().Format(http.TimeFormat))
		if r.Header.Get("If-Modified-Since") != "" {
			if t, parseError := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); parseError == nil {
				if t.Unix() >= int64(n.LastModified) {
					w.WriteHeader(http.StatusNotModified)
					return
				}
			}
		}
	}
	if inm := r.Header.Get("If-None-Match"); inm == "\""+n.Etag()+"\"" {
		w.WriteHeader(http.StatusNotModified)
		return
	}
	setEtag(w, n.Etag())

	if n.HasPairs() {
		pairMap := make(map[string]string)
		err = json.Unmarshal(n.Pairs, &pairMap)
		if err != nil {
			glog.V(0).Infoln("Unmarshal pairs error:", err)
		}
		for k, v := range pairMap {
			w.Header().Set(k, v)
		}
	}

	if vs.tryHandleChunkedFile(n, filename, ext, w, r) {
		return
	}

	if n.NameSize > 0 && filename == "" {
		filename = string(n.Name)
		if ext == "" {
			ext = filepath.Ext(filename)
		}
	}
	mtype := ""
	if n.MimeSize > 0 {
		mt := string(n.Mime)
		if !strings.HasPrefix(mt, "application/octet-stream") {
			mtype = mt
		}
	}

	if n.IsCompressed() {
		if _, _, _, shouldResize := shouldResizeImages(ext, r); shouldResize {
			if n.Data, err = util.DecompressData(n.Data); err != nil {
				glog.V(0).Infoln("ungzip error:", err, r.URL.Path)
			}
		} else if strings.Contains(r.Header.Get("Accept-Encoding"), "zstd") && util.IsZstdContent(n.Data) {
			w.Header().Set("Content-Encoding", "zstd")
		} else if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") && util.IsGzippedContent(n.Data) {
			w.Header().Set("Content-Encoding", "gzip")
		} else {
			if n.Data, err = util.DecompressData(n.Data); err != nil {
				glog.V(0).Infoln("uncompress error:", err, r.URL.Path)
			}
		}
	}

	rs := conditionallyResizeImages(bytes.NewReader(n.Data), ext, r)

	if e := writeResponseContent(filename, mtype, rs, w, r); e != nil {
		glog.V(2).Infoln("response write error:", e)
	}
}

func (vs *VolumeServer) tryHandleChunkedFile(n *needle.Needle, fileName string, ext string, w http.ResponseWriter, r *http.Request) (processed bool) {
	if !n.IsChunkedManifest() || r.URL.Query().Get("cm") == "false" {
		return false
	}

	chunkManifest, e := operation.LoadChunkManifest(n.Data, n.IsCompressed())
	if e != nil {
		glog.V(0).Infof("load chunked manifest (%s) error: %v", r.URL.Path, e)
		return false
	}
	if fileName == "" && chunkManifest.Name != "" {
		fileName = chunkManifest.Name
	}

	if ext == "" {
		ext = filepath.Ext(fileName)
	}

	mType := ""
	if chunkManifest.Mime != "" {
		mt := chunkManifest.Mime
		if !strings.HasPrefix(mt, "application/octet-stream") {
			mType = mt
		}
	}

	w.Header().Set("X-File-Store", "chunked")

	chunkedFileReader := operation.NewChunkedFileReader(chunkManifest.Chunks, vs.GetMaster())
	defer chunkedFileReader.Close()

	rs := conditionallyResizeImages(chunkedFileReader, ext, r)

	if e := writeResponseContent(fileName, mType, rs, w, r); e != nil {
		glog.V(2).Infoln("response write error:", e)
	}
	return true
}

func conditionallyResizeImages(originalDataReaderSeeker io.ReadSeeker, ext string, r *http.Request) io.ReadSeeker {
	rs := originalDataReaderSeeker
	if len(ext) > 0 {
		ext = strings.ToLower(ext)
	}
	width, height, mode, shouldResize := shouldResizeImages(ext, r)
	if shouldResize {
		rs, _, _ = images.Resized(ext, originalDataReaderSeeker, width, height, mode)
	}
	return rs
}

func shouldResizeImages(ext string, r *http.Request) (width, height int, mode string, shouldResize bool) {
	if ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".gif" {
		if r.FormValue("width") != "" {
			width, _ = strconv.Atoi(r.FormValue("width"))
		}
		if r.FormValue("height") != "" {
			height, _ = strconv.Atoi(r.FormValue("height"))
		}
	}
	mode = r.FormValue("mode")
	shouldResize = width > 0 || height > 0
	return
}

func writeResponseContent(filename, mimeType string, rs io.ReadSeeker, w http.ResponseWriter, r *http.Request) error {
	totalSize, e := rs.Seek(0, 2)
	if mimeType == "" {
		if ext := filepath.Ext(filename); ext != "" {
			mimeType = mime.TypeByExtension(ext)
		}
	}
	if mimeType != "" {
		w.Header().Set("Content-Type", mimeType)
	}
	w.Header().Set("Accept-Ranges", "bytes")

	adjustHeaderContentDisposition(w, r, filename)

	if r.Method == "HEAD" {
		w.Header().Set("Content-Length", strconv.FormatInt(totalSize, 10))
		return nil
	}

	processRangeRequest(r, w, totalSize, mimeType, func(writer io.Writer, offset int64, size int64) error {
		if _, e = rs.Seek(offset, 0); e != nil {
			return e
		}
		_, e = io.CopyN(writer, rs, size)
		return e
	})
	return nil
}
<|endoftext|>


<commit_before>package collectors

import (
	"regexp"
	"strconv"
	"strings"

	"github.com/StackExchange/scollector/opentsdb"
	"github.com/StackExchange/slog"
)

func init() {
	collectors = append(collectors, &IntervalCollector{F: c_procstats_linux})
}

var uptimeRE = regexp.MustCompile(`(\S+)\s+(\S+)`)
var meminfoRE = regexp.MustCompile(`(\w+):\s+(\d+)\s+(\w+)`)
var vmstatRE = regexp.MustCompile(`(\w+)\s+(\d+)`)
var statRE = regexp.MustCompile(`(\w+)\s+(.*)`)
var statCpuRE = regexp.MustCompile(`cpu(\d+)`)
var loadavgRE = regexp.MustCompile(`(\S+)\s+(\S+)\s+(\S+)\s+(\d+)/(\d+)\s+`)

var CPU_FIELDS = []string{
	"user",
	"nice",
	"system",
	"idle",
	"iowait",
	"irq",
	"softirq",
	"guest",
	"guest_nice",
}

func c_procstats_linux() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readLine("/proc/uptime", func(s string) {
		m := uptimeRE.FindStringSubmatch(s)
		if m == nil {
			return
		}
		Add(&md, "linux.uptime_total", m[1], nil)
		Add(&md, "linux.uptime_now", m[2], nil)
	})
	mem := make(map[string]float64)
	readLine("/proc/meminfo", func(s string) {
		m := meminfoRE.FindStringSubmatch(s)
		if m == nil {
			return
		}
		i, err := strconv.ParseFloat(m[2], 64)
		if err != nil {
			slog.Errorln(err)
		}
		mem[m[1]] = i
		Add(&md, "linux.mem."+strings.ToLower(m[1]), m[2], nil)
	})
	Add(&md, osMemTotal, int(mem["MemTotal"])*1024, nil)
	Add(&md, osMemFree, int(mem["MemFree"])*1024, nil)
	Add(&md, osMemUsed, (int(mem["MemTotal"])-(int(mem["MemFree"])+int(mem["Buffers"])+int(mem["Cached"])))*1024, nil)
	if mem["MemTotal"] != 0 {
		Add(&md, osMemPctFree, (mem["MemFree"]+mem["Buffers"]+mem["Cached"])/mem["MemTotal"]*100, nil)
	}

	readLine("/proc/vmstat", func(s string) {
		m := vmstatRE.FindStringSubmatch(s)
		if m == nil {
			return
		}

		switch m[1] {
		case "pgpgin", "pgpgout", "pswpin", "pswpout", "pgfault", "pgmajfault":
			Add(&md, "linux.mem."+m[1], m[2], nil)
		}
	})
	num_cores := 0
	readLine("/proc/stat", func(s string) {
		m := statRE.FindStringSubmatch(s)
		if m == nil {
			return
		}
		if strings.HasPrefix(m[1], "cpu") {
			metric_percpu := ""
			tag_cpu := ""
			cpu_m := statCpuRE.FindStringSubmatch(m[1])
			if cpu_m != nil {
				num_cores += 1
				metric_percpu = ".percpu"
				tag_cpu = cpu_m[1]
			}
			fields := strings.Fields(m[2])
			for i, value := range fields {
				if i >= len(CPU_FIELDS) {
					break
				}
				tags := opentsdb.TagSet{
					"type": CPU_FIELDS[i],
				}
				if tag_cpu != "" {
					tags["cpu"] = tag_cpu
				}
				Add(&md, "linux.cpu"+metric_percpu, value, tags)
			}
		} else if m[1] == "intr" {
			Add(&md, "linux.intr", strings.Fields(m[2])[0], nil)
		} else if m[1] == "ctxt" {
			Add(&md, "linux.ctxt", m[2], nil)
		} else if m[1] == "processes" {
			Add(&md, "linux.processes", m[2], nil)
		} else if m[1] == "procs_blocked" {
			Add(&md, "linux.procs_blocked", m[2], nil)
		}
	})
	readLine("/proc/loadavg", func(s string) {
		m := loadavgRE.FindStringSubmatch(s)
		if m == nil {
			return
		}
		Add(&md, "linux.loadavg_1_min", m[1], nil)
		Add(&md, "linux.loadavg_5_min", m[2], nil)
		Add(&md, "linux.loadavg_15_min", m[3], nil)
		Add(&md, "linux.loadavg_runnable", m[4], nil)
		Add(&md, "linux.loadavg_total_threads", m[5], nil)
	})
	readLine("/proc/sys/kernel/random/entropy_avail", func(s string) {
		Add(&md, "linux.entropy_avail", strings.TrimSpace(s), nil)
	})
	num_cpus := 0
	readLine("/proc/interrupts", func(s string) {
		cols := strings.Fields(s)
		if num_cpus == 0 {
			num_cpus = len(cols)
			return
		} else if len(cols) < 2 {
			return
		}
		irq_type := strings.TrimRight(cols[0], ":")
		if !IsAlNum(irq_type) {
			return
		}
		if IsDigit(irq_type) {
			if cols[len(cols)-2] == "PCI-MSI-edge" && strings.Contains(cols[len(cols)-1], "eth") {
				irq_type = cols[len(cols)-1]
			} else {
				// Interrupt type is just a number, ignore.
				return
			}
		}
		for i, val := range cols[1:] {
			if i >= num_cpus {
				// All values read, remaining cols contain textual description.
				break
			}
			if !IsDigit(val) {
				// Something is weird, there should only be digit values.
				slog.Infoln("interrupts: unexpected value", val)
				break
			}
			Add(&md, "linux.interrupts", val, opentsdb.TagSet{"type": irq_type, "cpu": strconv.Itoa(i)})
		}
	})
	readLine("/proc/uptime", func(s string) {
		cols := strings.Fields(s)
		if len(cols) < 2 {
			return
		}
		total_time, err := strconv.ParseFloat(cols[0], 64)
		idle_time, err := strconv.ParseFloat(cols[1], 64)
		if err != nil {
			return
		}
		if num_cores != 0 {
			Add(&md, osCPU, (total_time-(idle_time/float64(num_cores)))*100, nil)
		}
	})
	return md
}
<commit_msg>cmd/scollector: Make the direction of linux paging and swaping a tag<commit_after>package collectors

import (
	"regexp"
	"strconv"
	"strings"

	"github.com/StackExchange/scollector/opentsdb"
	"github.com/StackExchange/slog"
)

func init() {
	collectors = append(collectors, &IntervalCollector{F: c_procstats_linux})
}

var uptimeRE = regexp.MustCompile(`(\S+)\s+(\S+)`)
var meminfoRE = regexp.MustCompile(`(\w+):\s+(\d+)\s+(\w+)`)
var vmstatRE = regexp.MustCompile(`(\w+)\s+(\d+)`)
var statRE = regexp.MustCompile(`(\w+)\s+(.*)`)
var statCpuRE = regexp.MustCompile(`cpu(\d+)`)
var loadavgRE = regexp.MustCompile(`(\S+)\s+(\S+)\s+(\S+)\s+(\d+)/(\d+)\s+`)
var inoutRE = regexp.MustCompile(`(.*)(in|out)`)

var CPU_FIELDS = []string{
	"user",
	"nice",
	"system",
	"idle",
	"iowait",
	"irq",
	"softirq",
	"guest",
	"guest_nice",
}

func c_procstats_linux() opentsdb.MultiDataPoint {
	var md opentsdb.MultiDataPoint
	readLine("/proc/uptime", func(s string) {
		m := uptimeRE.FindStringSubmatch(s)
		if m == nil {
			return
		}
		Add(&md, "linux.uptime_total", m[1], nil)
		Add(&md, "linux.uptime_now", m[2], nil)
	})
	mem := make(map[string]float64)
	readLine("/proc/meminfo", func(s string) {
		m := meminfoRE.FindStringSubmatch(s)
		if m == nil {
			return
		}
		i, err := strconv.ParseFloat(m[2], 64)
		if err != nil {
			slog.Errorln(err)
		}
		mem[m[1]] = i
		Add(&md, "linux.mem."+strings.ToLower(m[1]), m[2], nil)
	})
	Add(&md, osMemTotal, int(mem["MemTotal"])*1024, nil)
	Add(&md, osMemFree, int(mem["MemFree"])*1024, nil)
	Add(&md, osMemUsed, (int(mem["MemTotal"])-(int(mem["MemFree"])+int(mem["Buffers"])+int(mem["Cached"])))*1024, nil)
	if mem["MemTotal"] != 0 {
		Add(&md, osMemPctFree, (mem["MemFree"]+mem["Buffers"]+mem["Cached"])/mem["MemTotal"]*100, nil)
	}

	readLine("/proc/vmstat", func(s string) {
		m := vmstatRE.FindStringSubmatch(s)
		if m == nil {
			return
		}

		switch m[1] {
		case "pgpgin", "pgpgout", "pswpin", "pswpout", "pgfault", "pgmajfault":
			mio := inoutRE.FindStringSubmatch(m[1])
			if mio != nil {
				Add(&md, "linux.mem."+mio[1], m[2], opentsdb.TagSet{"direction": mio[2]})
			} else {
				Add(&md, "linux.mem."+m[1], m[2], nil)
			}
		}
	})
	num_cores := 0
	readLine("/proc/stat", func(s string) {
		m := statRE.FindStringSubmatch(s)
		if m == nil {
			return
		}
		if strings.HasPrefix(m[1], "cpu") {
			metric_percpu := ""
			tag_cpu := ""
			cpu_m := statCpuRE.FindStringSubmatch(m[1])
			if cpu_m != nil {
				num_cores += 1
				metric_percpu = ".percpu"
				tag_cpu = cpu_m[1]
			}
			fields := strings.Fields(m[2])
			for i, value := range fields {
				if i >= len(CPU_FIELDS) {
					break
				}
				tags := opentsdb.TagSet{
					"type": CPU_FIELDS[i],
				}
				if tag_cpu != "" {
					tags["cpu"] = tag_cpu
				}
				Add(&md, "linux.cpu"+metric_percpu, value, tags)
			}
		} else if m[1] == "intr" {
			Add(&md, "linux.intr", strings.Fields(m[2])[0], nil)
		} else if m[1] == "ctxt" {
			Add(&md, "linux.ctxt", m[2], nil)
		} else if m[1] == "processes" {
			Add(&md, "linux.processes", m[2], nil)
		} else if m[1] == "procs_blocked" {
			Add(&md, "linux.procs_blocked", m[2], nil)
		}
	})
	readLine("/proc/loadavg", func(s string) {
		m := loadavgRE.FindStringSubmatch(s)
		if m == nil {
			return
		}
		Add(&md, "linux.loadavg_1_min", m[1], nil)
		Add(&md, "linux.loadavg_5_min", m[2], nil)
		Add(&md, "linux.loadavg_15_min", m[3], nil)
		Add(&md, "linux.loadavg_runnable", m[4], nil)
		Add(&md, "linux.loadavg_total_threads", m[5], nil)
	})
	readLine("/proc/sys/kernel/random/entropy_avail", func(s string) {
		Add(&md, "linux.entropy_avail", strings.TrimSpace(s), nil)
	})
	num_cpus := 0
	readLine("/proc/interrupts", func(s string) {
		cols := strings.Fields(s)
		if num_cpus == 0 {
			num_cpus = len(cols)
			return
		} else if len(cols) < 2 {
			return
		}
		irq_type := strings.TrimRight(cols[0], ":")
		if !IsAlNum(irq_type) {
			return
		}
		if IsDigit(irq_type) {
			if cols[len(cols)-2] == "PCI-MSI-edge" && strings.Contains(cols[len(cols)-1], "eth") {
				irq_type = cols[len(cols)-1]
			} else {
				// Interrupt type is just a number, ignore.
				return
			}
		}
		for i, val := range cols[1:] {
			if i >= num_cpus {
				// All values read, remaining cols contain textual description.
				break
			}
			if !IsDigit(val) {
				// Something is weird, there should only be digit values.
				slog.Infoln("interrupts: unexpected value", val)
				break
			}
			Add(&md, "linux.interrupts", val, opentsdb.TagSet{"type": irq_type, "cpu": strconv.Itoa(i)})
		}
	})
	readLine("/proc/uptime", func(s string) {
		cols := strings.Fields(s)
		if len(cols) < 2 {
			return
		}
		total_time, err := strconv.ParseFloat(cols[0], 64)
		idle_time, err := strconv.ParseFloat(cols[1], 64)
		if err != nil {
			return
		}
		if num_cores != 0 {
			Add(&md, osCPU, (total_time-(idle_time/float64(num_cores)))*100, nil)
		}
	})
	return md
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// verify that dependencies are up-to-date across different files
package main

import (
	"flag"
	"log"
	"strings"

	"bufio"
	"io/ioutil"
	"os"
	"regexp"

	"gopkg.in/yaml.v2"
)

type dependencies struct {
	Dependencies []*dependency `yaml:"dependencies"`
}

type dependency struct {
	Name     string     `yaml:"name"`
	Version  string     `yaml:"version"`
	RefPaths []*refPath `yaml:"refPaths"`
}

type refPath struct {
	Path  string `yaml:"path"`
	Match string `yaml:"match"`
}

func main() {

	flag.Parse()

	args := flag.Args()

	if len(args) == 0 {
		log.Fatalf("usage: verifydependency <file>")
	}
	externalDepsFilePath := args[0]
	externalDepsFile, err := ioutil.ReadFile(externalDepsFilePath)
	if err != nil {
		panic(err)
	}

	externalDeps := &dependencies{}

	err = yaml.Unmarshal(externalDepsFile, externalDeps)
	if err != nil {
		panic(err)
	}

	for _, dep := range externalDeps.Dependencies {
		for _, refPath := range dep.RefPaths {
			file, err := os.Open(refPath.Path)
			if err != nil {
				log.Fatalf("error opening file %v : %v", refPath.Path, err)
			}
			matcher := regexp.MustCompile(refPath.Match)
			depFileScanner := bufio.NewScanner(file)
			var found bool
			for depFileScanner.Scan() {
				line := depFileScanner.Text()
				if matcher.MatchString(line) && strings.Contains(line, dep.Version) {
					found = true
					break
				}
			}
			if !found {
				log.Fatalf("%v dependency: file %v doesn't contain the expected version %v or matcher %v did change. check the %v file", dep.Name, refPath.Path, dep.Version, refPath.Match, externalDepsFilePath)
			}
		}
	}

}
<commit_msg>improve error messages for verifydependency<commit_after>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// verify that dependencies are up-to-date across different files
package main

import (
	"flag"
	"log"
	"strings"

	"bufio"
	"io/ioutil"
	"os"
	"regexp"

	"gopkg.in/yaml.v2"
)

type dependencies struct {
	Dependencies []*dependency `yaml:"dependencies"`
}

type dependency struct {
	Name     string     `yaml:"name"`
	Version  string     `yaml:"version"`
	RefPaths []*refPath `yaml:"refPaths"`
}

type refPath struct {
	Path  string `yaml:"path"`
	Match string `yaml:"match"`
}

func main() {

	flag.Parse()

	args := flag.Args()

	if len(args) == 0 {
		log.Fatalf("usage: verifydependency <file>")
	}
	externalDepsFilePath := args[0]
	externalDepsFile, err := ioutil.ReadFile(externalDepsFilePath)
	if err != nil {
		panic(err)
	}

	mismatchErrorMessage := "ERROR: %v indicates that %v should be at version %v, but the following files didn't match:\n\n" +
		"%v\n\nif you are changing the version of %v, make sure all of the following files are updated with the newest version including %v\n" +
		"then run ./hack/verify-external-dependencies-version.sh\n\n"
	externalDeps := &dependencies{}
	var pathsToUpdate []string
	err = yaml.Unmarshal(externalDepsFile, externalDeps)
	if err != nil {
		panic(err)
	}

	for _, dep := range externalDeps.Dependencies {
		for _, refPath := range dep.RefPaths {
			file, err := os.Open(refPath.Path)
			if err != nil {
				log.Fatalf("error opening file %v : %v", refPath.Path, err)
			}
			matcher := regexp.MustCompile(refPath.Match)
			depFileScanner := bufio.NewScanner(file)
			var found bool
			for depFileScanner.Scan() {
				line := depFileScanner.Text()
				if matcher.MatchString(line) && strings.Contains(line, dep.Version) {
					found = true
					break
				}
			}
			if !found {
				pathsToUpdate = append(pathsToUpdate, refPath.Path)
			}
		}
		if len(pathsToUpdate) > 0 {
			log.Fatalf(mismatchErrorMessage, externalDepsFilePath, dep.Name, dep.Version, strings.Join(pathsToUpdate, "\n"), dep.Name, externalDepsFilePath)
		}
	}

}
<|endoftext|>


<commit_before>/*
 * Minimal multicast DNS server.
 *
 * Copyright (c) 2014, Alessandro Ghedini
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package main

import "log"
import "os"
import "strings"
import "sync"

import "github.com/docopt/docopt-go"

import "mdns"

func main() {
	log.SetFlags(0);

	usage := `Usage: moodns [options]

Options:
  -H <hostname>, --host <hostname>      Name of the local host.
  -l <addr:port>, --listen <addr:port>  Listen on this local address and port [default: 0.0.0.0:5353].
  -r, --enable-multicast-forward        Enable forwarding of unicast requests to multicast.
  -s, --silent                          Print fatal errors only.
  -h, --help                            Show the program's help message and exit.`

	var wg sync.WaitGroup;

	args, err := docopt.Parse(usage, nil, true, "", false)
	if err != nil {
		log.Fatal("Invalid arguments: ", err);
	}

	listen := args["--listen"].(string);

	hostname, _ := os.Hostname();
	if args["--host"] != nil {
		hostname = args["--host"].(string);
	}

	localname := hostname + ".local.";
	silent    := args["--silent"].(bool);
	forward   := args["--enable-multicast-forward"].(bool);

	for _, addr := range strings.Split(listen, ",") {
		maddr, server, err := mdns.NewServer(addr, mdns.MDNSAddr);
		if err != nil {
			log.Fatal("Error starting server: ", err);
		}

		wg.Add(1);
		go func() {
			mdns.Serve(server, maddr, localname, silent, forward);
			wg.Done();
		}()
	}

	wg.Wait();
}
<commit_msg>moodns: no need to actually wait for goroutines termination<commit_after>/*
 * Minimal multicast DNS server.
 *
 * Copyright (c) 2014, Alessandro Ghedini
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package main

import "log"
import "os"
import "strings"

import "github.com/docopt/docopt-go"

import "mdns"

func main() {
	log.SetFlags(0);

	usage := `Usage: moodns [options]

Options:
  -H <hostname>, --host <hostname>      Name of the local host.
  -l <addr:port>, --listen <addr:port>  Listen on this local address and port [default: 0.0.0.0:5353].
  -r, --enable-multicast-forward        Enable forwarding of unicast requests to multicast.
  -s, --silent                          Print fatal errors only.
  -h, --help                            Show the program's help message and exit.`

	args, err := docopt.Parse(usage, nil, true, "", false)
	if err != nil {
		log.Fatal("Invalid arguments: ", err);
	}

	listen := args["--listen"].(string);

	hostname, _ := os.Hostname();
	if args["--host"] != nil {
		hostname = args["--host"].(string);
	}

	localname := hostname + ".local.";
	silent    := args["--silent"].(bool);
	forward   := args["--enable-multicast-forward"].(bool);

	for _, addr := range strings.Split(listen, ",") {
		maddr, server, err := mdns.NewServer(addr, mdns.MDNSAddr);
		if err != nil {
			log.Fatal("Error starting server: ", err);
		}

		go mdns.Serve(server, maddr, localname, silent, forward);
	}

	select {}
}
<|endoftext|>


<commit_before>package compressing

import (
	"errors"
	"fmt"
	"github.com/balzaczyy/golucene/core/codec"
	"github.com/balzaczyy/golucene/core/codec/lucene40"
	. "github.com/balzaczyy/golucene/core/codec/spi"
	"github.com/balzaczyy/golucene/core/index/model"
	"github.com/balzaczyy/golucene/core/store"
	"github.com/balzaczyy/golucene/core/util"
	"github.com/balzaczyy/golucene/core/util/packed"
)

// codec/compressing/CompressingStoredFieldsReader.java

// Do not reuse the decompression buffer when there is more than 32kb to decompress
const BUFFER_REUSE_THRESHOLD = 1 << 15

// StoredFieldsReader impl for CompressingStoredFieldsFormat
type CompressingStoredFieldsReader struct {
	version           int
	fieldInfos        model.FieldInfos
	indexReader       *CompressingStoredFieldsIndexReader
	maxPointer        int64
	fieldsStream      store.IndexInput
	chunkSize         int
	packedIntsVersion int
	compressionMode   CompressionMode
	decompressor      Decompressor
	bytes             []byte
	numDocs           int
	closed            bool
}

// used by clone
func newCompressingStoredFieldsReaderFrom(reader *CompressingStoredFieldsReader) *CompressingStoredFieldsReader {
	return &CompressingStoredFieldsReader{
		version:           reader.version,
		fieldInfos:        reader.fieldInfos,
		fieldsStream:      reader.fieldsStream.Clone(),
		indexReader:       reader.indexReader.Clone(),
		maxPointer:        reader.maxPointer,
		chunkSize:         reader.chunkSize,
		packedIntsVersion: reader.packedIntsVersion,
		compressionMode:   reader.compressionMode,
		decompressor:      reader.compressionMode.NewDecompressor(),
		numDocs:           reader.numDocs,
		bytes:             make([]byte, len(reader.bytes)),
		closed:            false,
	}
}

// Sole constructor
func newCompressingStoredFieldsReader(d store.Directory,
	si *model.SegmentInfo, segmentSuffix string,
	fn model.FieldInfos, ctx store.IOContext, formatName string,
	compressionMode CompressionMode) (r *CompressingStoredFieldsReader, err error) {

	r = &CompressingStoredFieldsReader{}
	r.compressionMode = compressionMode
	segment := si.Name
	r.fieldInfos = fn
	r.numDocs = si.DocCount()

	var indexStream store.ChecksumIndexInput
	success := false
	defer func() {
		if !success {
			util.CloseWhileSuppressingError(r, indexStream)
		}
	}()

	indexStreamFN := util.SegmentFileName(segment, segmentSuffix, lucene40.FIELDS_INDEX_EXTENSION)
	fieldsStreamFN := util.SegmentFileName(segment, segmentSuffix, lucene40.FIELDS_EXTENSION)
	// Load the index into memory
	if indexStream, err = d.OpenChecksumInput(indexStreamFN, ctx); err != nil {
		return nil, err
	}
	codecNameIdx := formatName + CODEC_SFX_IDX
	if r.version, err = int32AsInt(codec.CheckHeader(indexStream, codecNameIdx,
		VERSION_START, VERSION_CURRENT)); err != nil {
		return nil, err
	}
	assert(int64(codec.HeaderLength(codecNameIdx)) == indexStream.FilePointer())
	if r.indexReader, err = newCompressingStoredFieldsIndexReader(indexStream, si); err != nil {
		return nil, err
	}

	var maxPointer int64 = -1

	if r.version >= VERSION_CHECKSUM {
		if maxPointer, err = indexStream.ReadVLong(); err != nil {
			return nil, err
		}
		if _, err = codec.CheckFooter(indexStream); err != nil {
			return nil, err
		}
	} else {
		if err = codec.CheckEOF(indexStream); err != nil {
			return nil, err
		}
	}

	if err = indexStream.Close(); err != nil {
		return nil, err
	}
	indexStream = nil

	// Open the data file and read metadata
	if r.fieldsStream, err = d.OpenInput(fieldsStreamFN, ctx); err != nil {
		return nil, err
	}
	if r.version >= VERSION_CHECKSUM {
		if maxPointer+codec.FOOTER_LENGTH != r.fieldsStream.Length() {
			return nil, errors.New(fmt.Sprintf(
				"Invalid fieldsStream maxPointer (file truncated?): maxPointer=%v, length=%v",
				maxPointer, r.fieldsStream.Length()))
		}
	} else {
		maxPointer = r.fieldsStream.Length()
	}
	r.maxPointer = maxPointer
	codecNameDat := formatName + CODEC_SFX_DAT
	var fieldsVersion int
	if fieldsVersion, err = int32AsInt(codec.CheckHeader(r.fieldsStream,
		codecNameDat, VERSION_START, VERSION_CURRENT)); err != nil {
		return nil, err
	}
	assert2(r.version == fieldsVersion,
		"Version mismatch between stored fields index and data: %v != %v",
		r.version, fieldsVersion)
	assert(int64(codec.HeaderLength(codecNameDat)) == r.fieldsStream.FilePointer())

	r.chunkSize = -1
	if r.version >= VERSION_BIG_CHUNKS {
		if r.chunkSize, err = int32AsInt(r.fieldsStream.ReadVInt()); err != nil {
			return nil, err
		}
	}

	if r.packedIntsVersion, err = int32AsInt(r.fieldsStream.ReadVInt()); err != nil {
		return nil, err
	}
	r.decompressor = compressionMode.NewDecompressor()
	r.bytes = make([]byte, 0)

	success = true
	return r, nil
}

func int32AsInt(n int32, err error) (int, error) {
	return int(n), err
}

func (r *CompressingStoredFieldsReader) ensureOpen() {
	assert2(!r.closed, "this FieldsReader is closed")
}

// Close the underlying IndexInputs
func (r *CompressingStoredFieldsReader) Close() (err error) {
	if !r.closed {
		if err = util.Close(r.fieldsStream); err == nil {
			r.closed = true
		}
	}
	return
}

func (r *CompressingStoredFieldsReader) readField(in util.DataInput,
	visitor StoredFieldVisitor, info *model.FieldInfo, bits int) (err error) {
	switch bits & TYPE_MASK {
	case BYTE_ARR:
		panic("not implemented yet")
	case STRING:
		var length int
		if length, err = int32AsInt(in.ReadVInt()); err != nil {
			return err
		}
		data := make([]byte, length)
		if err = in.ReadBytes(data); err != nil {
			return err
		}
		visitor.StringField(info, string(data))
	case NUMERIC_INT:
		panic("not implemented yet")
	case NUMERIC_FLOAT:
		panic("not implemented yet")
	case NUMERIC_LONG:
		panic("not implemented yet")
	case NUMERIC_DOUBLE:
		panic("not implemented yet")
	default:
		panic(fmt.Sprintf("Unknown type flag: %x", bits))
	}
	return nil
}

func (r *CompressingStoredFieldsReader) VisitDocument(docID int, visitor StoredFieldVisitor) error {
	err := r.fieldsStream.Seek(r.indexReader.startPointer(docID))
	if err != nil {
		return err
	}

	docBase, err := int32AsInt(r.fieldsStream.ReadVInt())
	if err != nil {
		return err
	}
	chunkDocs, err := int32AsInt(r.fieldsStream.ReadVInt())
	if err != nil {
		return err
	}
	if docID < docBase ||
		docID >= docBase+chunkDocs ||
		docBase+chunkDocs > r.numDocs {
		return errors.New(fmt.Sprintf(
			"Corrupted: docID=%v, docBase=%v, chunkDocs=%v, numDocs=%v (resource=%v)",
			docID, docBase, chunkDocs, r.numDocs, r.fieldsStream))
	}

	var numStoredFields, offset, length, totalLength int
	if chunkDocs == 1 {
		if numStoredFields, err = int32AsInt(r.fieldsStream.ReadVInt()); err != nil {
			return err
		}
		offset = 0
		if length, err = int32AsInt(r.fieldsStream.ReadVInt()); err != nil {
			return err
		}
		totalLength = length
	} else {
		bitsPerStoredFields, err := int32AsInt(r.fieldsStream.ReadVInt())
		if err != nil {
			return err
		}
		if bitsPerStoredFields == 0 {
			numStoredFields, err = int32AsInt(r.fieldsStream.ReadVInt())
			if err != nil {
				return err
			}
		} else if bitsPerStoredFields > 31 {
			return errors.New(fmt.Sprintf("bitsPerStoredFields=%v (resource=%v)",
				bitsPerStoredFields, r.fieldsStream))
		} else {
			panic("not implemented yet")
		}

		bitsPerLength, err := int32AsInt(r.fieldsStream.ReadVInt())
		if err != nil {
			return err
		}
		if bitsPerLength == 0 {
			if length, err = int32AsInt(r.fieldsStream.ReadVInt()); err != nil {
				return err
			}
			offset = (docID - docBase) * length
			totalLength = chunkDocs * length
		} else if bitsPerLength > 31 {
			return errors.New(fmt.Sprintf("bitsPerLength=%v (resource=%v)",
				bitsPerLength, r.fieldsStream))
		} else {
			it := packed.ReaderIteratorNoHeader(
				r.fieldsStream, packed.PackedFormat(packed.PACKED), r.packedIntsVersion,
				chunkDocs, bitsPerLength, 1)
			var n int64
			off := 0
			for i := 0; i < docID-docBase; i++ {
				if n, err = it.Next(); err != nil {
					return err
				}
				off += int(n)
			}
			offset = off
			if n, err = it.Next(); err != nil {
				return err
			}
			length = int(n)
			off += length
			for i := docID - docBase + 1; i < chunkDocs; i++ {
				if n, err = it.Next(); err != nil {
					return err
				}
				off += int(n)
			}
			totalLength = off
		}
	}

	if (length == 0) != (numStoredFields == 0) {
		return errors.New(fmt.Sprintf(
			"length=%v, numStoredFields=%v (resource=%v)",
			length, numStoredFields, r.fieldsStream))
	}
	if numStoredFields == 0 {
		// nothing to do
		return nil
	}

	var documentInput util.DataInput
	if r.version >= VERSION_BIG_CHUNKS && totalLength >= 2*r.chunkSize {
		panic("not implemented yet")
	} else {
		var bytes []byte
		if totalLength <= BUFFER_REUSE_THRESHOLD {
			bytes = r.bytes
		} else {
			bytes = make([]byte, 0)
		}
		bytes, err = r.decompressor(r.fieldsStream, totalLength, offset, length, bytes)
		if err != nil {
			return err
		}
		assert(len(bytes) == length)
		documentInput = store.NewByteArrayDataInput(bytes)
	}

	for fieldIDX := 0; fieldIDX < numStoredFields; fieldIDX++ {
		infoAndBits, err := documentInput.ReadVLong()
		if err != nil {
			return err
		}
		fieldNumber := int(uint64(infoAndBits) >> uint64(TYPE_BITS))
		fieldInfo := r.fieldInfos.FieldInfoByNumber(fieldNumber)

		bits := int(infoAndBits & int64(TYPE_MASK))
		assertWithMessage(bits <= NUMERIC_DOUBLE, fmt.Sprintf("bits=%x", bits))

		status, err := visitor.NeedsField(fieldInfo)
		if err != nil {
			return err
		}
		switch status {
		case STORED_FIELD_VISITOR_STATUS_YES:
			r.readField(documentInput, visitor, fieldInfo, bits)
		case STORED_FIELD_VISITOR_STATUS_NO:
			panic("not implemented yet")
		case STORED_FIELD_VISITOR_STATUS_STOP:
			return nil
		}
	}

	return nil
}

func assertWithMessage(ok bool, msg string) {
	if !ok {
		panic(msg)
	}
}

func (r *CompressingStoredFieldsReader) Clone() StoredFieldsReader {
	r.ensureOpen()
	return newCompressingStoredFieldsReaderFrom(r)
}
<commit_msg>sync package compressing<commit_after>package compressing

import (
	"errors"
	"fmt"
	"github.com/balzaczyy/golucene/core/codec"
	"github.com/balzaczyy/golucene/core/codec/lucene40"
	. "github.com/balzaczyy/golucene/core/codec/spi"
	"github.com/balzaczyy/golucene/core/index/model"
	"github.com/balzaczyy/golucene/core/store"
	"github.com/balzaczyy/golucene/core/util"
	"github.com/balzaczyy/golucene/core/util/packed"
)

// codec/compressing/CompressingStoredFieldsReader.java

// Do not reuse the decompression buffer when there is more than 32kb to decompress
const BUFFER_REUSE_THRESHOLD = 1 << 15

// StoredFieldsReader impl for CompressingStoredFieldsFormat
type CompressingStoredFieldsReader struct {
	version           int
	fieldInfos        model.FieldInfos
	indexReader       *CompressingStoredFieldsIndexReader
	maxPointer        int64
	fieldsStream      store.IndexInput
	chunkSize         int
	packedIntsVersion int
	compressionMode   CompressionMode
	decompressor      Decompressor
	bytes             []byte
	numDocs           int
	closed            bool
}

// used by clone
func newCompressingStoredFieldsReaderFrom(reader *CompressingStoredFieldsReader) *CompressingStoredFieldsReader {
	return &CompressingStoredFieldsReader{
		version:           reader.version,
		fieldInfos:        reader.fieldInfos,
		fieldsStream:      reader.fieldsStream.Clone(),
		indexReader:       reader.indexReader.Clone(),
		maxPointer:        reader.maxPointer,
		chunkSize:         reader.chunkSize,
		packedIntsVersion: reader.packedIntsVersion,
		compressionMode:   reader.compressionMode,
		decompressor:      reader.compressionMode.NewDecompressor(),
		numDocs:           reader.numDocs,
		bytes:             make([]byte, len(reader.bytes)),
		closed:            false,
	}
}

// Sole constructor
func newCompressingStoredFieldsReader(d store.Directory,
	si *model.SegmentInfo, segmentSuffix string,
	fn model.FieldInfos, ctx store.IOContext, formatName string,
	compressionMode CompressionMode) (r *CompressingStoredFieldsReader, err error) {

	r = &CompressingStoredFieldsReader{}
	r.compressionMode = compressionMode
	segment := si.Name
	r.fieldInfos = fn
	r.numDocs = si.DocCount()

	var indexStream store.ChecksumIndexInput
	success := false
	defer func() {
		if !success {
			util.CloseWhileSuppressingError(r, indexStream)
		}
	}()

	indexStreamFN := util.SegmentFileName(segment, segmentSuffix, lucene40.FIELDS_INDEX_EXTENSION)
	fieldsStreamFN := util.SegmentFileName(segment, segmentSuffix, lucene40.FIELDS_EXTENSION)
	// Load the index into memory
	if indexStream, err = d.OpenChecksumInput(indexStreamFN, ctx); err != nil {
		return nil, err
	}
	codecNameIdx := formatName + CODEC_SFX_IDX
	if r.version, err = int32AsInt(codec.CheckHeader(indexStream, codecNameIdx,
		VERSION_START, VERSION_CURRENT)); err != nil {
		return nil, err
	}
	assert(int64(codec.HeaderLength(codecNameIdx)) == indexStream.FilePointer())
	if r.indexReader, err = newCompressingStoredFieldsIndexReader(indexStream, si); err != nil {
		return nil, err
	}

	var maxPointer int64 = -1

	if r.version >= VERSION_CHECKSUM {
		if maxPointer, err = indexStream.ReadVLong(); err != nil {
			return nil, err
		}
		if _, err = codec.CheckFooter(indexStream); err != nil {
			return nil, err
		}
	} else {
		if err = codec.CheckEOF(indexStream); err != nil {
			return nil, err
		}
	}

	if err = indexStream.Close(); err != nil {
		return nil, err
	}
	indexStream = nil

	// Open the data file and read metadata
	if r.fieldsStream, err = d.OpenInput(fieldsStreamFN, ctx); err != nil {
		return nil, err
	}
	if r.version >= VERSION_CHECKSUM {
		if maxPointer+codec.FOOTER_LENGTH != r.fieldsStream.Length() {
			return nil, errors.New(fmt.Sprintf(
				"Invalid fieldsStream maxPointer (file truncated?): maxPointer=%v, length=%v",
				maxPointer, r.fieldsStream.Length()))
		}
	} else {
		maxPointer = r.fieldsStream.Length()
	}
	r.maxPointer = maxPointer
	codecNameDat := formatName + CODEC_SFX_DAT
	var fieldsVersion int
	if fieldsVersion, err = int32AsInt(codec.CheckHeader(r.fieldsStream,
		codecNameDat, VERSION_START, VERSION_CURRENT)); err != nil {
		return nil, err
	}
	assert2(r.version == fieldsVersion,
		"Version mismatch between stored fields index and data: %v != %v",
		r.version, fieldsVersion)
	assert(int64(codec.HeaderLength(codecNameDat)) == r.fieldsStream.FilePointer())

	r.chunkSize = -1
	if r.version >= VERSION_BIG_CHUNKS {
		if r.chunkSize, err = int32AsInt(r.fieldsStream.ReadVInt()); err != nil {
			return nil, err
		}
	}

	if r.packedIntsVersion, err = int32AsInt(r.fieldsStream.ReadVInt()); err != nil {
		return nil, err
	}
	r.decompressor = compressionMode.NewDecompressor()
	r.bytes = make([]byte, 0)

	if r.version >= VERSION_CHECKSUM {
		panic("niy")
	}

	success = true
	return r, nil
}

func int32AsInt(n int32, err error) (int, error) {
	return int(n), err
}

func (r *CompressingStoredFieldsReader) ensureOpen() {
	assert2(!r.closed, "this FieldsReader is closed")
}

// Close the underlying IndexInputs
func (r *CompressingStoredFieldsReader) Close() (err error) {
	if !r.closed {
		if err = util.Close(r.fieldsStream); err == nil {
			r.closed = true
		}
	}
	return
}

func (r *CompressingStoredFieldsReader) readField(in util.DataInput,
	visitor StoredFieldVisitor, info *model.FieldInfo, bits int) (err error) {
	switch bits & TYPE_MASK {
	case BYTE_ARR:
		panic("not implemented yet")
	case STRING:
		var length int
		if length, err = int32AsInt(in.ReadVInt()); err != nil {
			return err
		}
		data := make([]byte, length)
		if err = in.ReadBytes(data); err != nil {
			return err
		}
		visitor.StringField(info, string(data))
	case NUMERIC_INT:
		panic("not implemented yet")
	case NUMERIC_FLOAT:
		panic("not implemented yet")
	case NUMERIC_LONG:
		panic("not implemented yet")
	case NUMERIC_DOUBLE:
		panic("not implemented yet")
	default:
		panic(fmt.Sprintf("Unknown type flag: %x", bits))
	}
	return nil
}

func (r *CompressingStoredFieldsReader) VisitDocument(docID int, visitor StoredFieldVisitor) error {
	err := r.fieldsStream.Seek(r.indexReader.startPointer(docID))
	if err != nil {
		return err
	}

	docBase, err := int32AsInt(r.fieldsStream.ReadVInt())
	if err != nil {
		return err
	}
	chunkDocs, err := int32AsInt(r.fieldsStream.ReadVInt())
	if err != nil {
		return err
	}
	if docID < docBase ||
		docID >= docBase+chunkDocs ||
		docBase+chunkDocs > r.numDocs {
		return errors.New(fmt.Sprintf(
			"Corrupted: docID=%v, docBase=%v, chunkDocs=%v, numDocs=%v (resource=%v)",
			docID, docBase, chunkDocs, r.numDocs, r.fieldsStream))
	}

	var numStoredFields, offset, length, totalLength int
	if chunkDocs == 1 {
		if numStoredFields, err = int32AsInt(r.fieldsStream.ReadVInt()); err != nil {
			return err
		}
		offset = 0
		if length, err = int32AsInt(r.fieldsStream.ReadVInt()); err != nil {
			return err
		}
		totalLength = length
	} else {
		bitsPerStoredFields, err := int32AsInt(r.fieldsStream.ReadVInt())
		if err != nil {
			return err
		}
		if bitsPerStoredFields == 0 {
			numStoredFields, err = int32AsInt(r.fieldsStream.ReadVInt())
			if err != nil {
				return err
			}
		} else if bitsPerStoredFields > 31 {
			return errors.New(fmt.Sprintf("bitsPerStoredFields=%v (resource=%v)",
				bitsPerStoredFields, r.fieldsStream))
		} else {
			panic("not implemented yet")
		}

		bitsPerLength, err := int32AsInt(r.fieldsStream.ReadVInt())
		if err != nil {
			return err
		}
		if bitsPerLength == 0 {
			if length, err = int32AsInt(r.fieldsStream.ReadVInt()); err != nil {
				return err
			}
			offset = (docID - docBase) * length
			totalLength = chunkDocs * length
		} else if bitsPerLength > 31 {
			return errors.New(fmt.Sprintf("bitsPerLength=%v (resource=%v)",
				bitsPerLength, r.fieldsStream))
		} else {
			it := packed.ReaderIteratorNoHeader(
				r.fieldsStream, packed.PackedFormat(packed.PACKED), r.packedIntsVersion,
				chunkDocs, bitsPerLength, 1)
			var n int64
			off := 0
			for i := 0; i < docID-docBase; i++ {
				if n, err = it.Next(); err != nil {
					return err
				}
				off += int(n)
			}
			offset = off
			if n, err = it.Next(); err != nil {
				return err
			}
			length = int(n)
			off += length
			for i := docID - docBase + 1; i < chunkDocs; i++ {
				if n, err = it.Next(); err != nil {
					return err
				}
				off += int(n)
			}
			totalLength = off
		}
	}

	if (length == 0) != (numStoredFields == 0) {
		return errors.New(fmt.Sprintf(
			"length=%v, numStoredFields=%v (resource=%v)",
			length, numStoredFields, r.fieldsStream))
	}
	if numStoredFields == 0 {
		// nothing to do
		return nil
	}

	var documentInput util.DataInput
	if r.version >= VERSION_BIG_CHUNKS && totalLength >= 2*r.chunkSize {
		panic("not implemented yet")
	} else {
		var bytes []byte
		if totalLength <= BUFFER_REUSE_THRESHOLD {
			bytes = r.bytes
		} else {
			bytes = make([]byte, 0)
		}
		bytes, err = r.decompressor(r.fieldsStream, totalLength, offset, length, bytes)
		if err != nil {
			return err
		}
		assert(len(bytes) == length)
		documentInput = store.NewByteArrayDataInput(bytes)
	}

	for fieldIDX := 0; fieldIDX < numStoredFields; fieldIDX++ {
		infoAndBits, err := documentInput.ReadVLong()
		if err != nil {
			return err
		}
		fieldNumber := int(uint64(infoAndBits) >> uint64(TYPE_BITS))
		fieldInfo := r.fieldInfos.FieldInfoByNumber(fieldNumber)

		bits := int(infoAndBits & int64(TYPE_MASK))
		assertWithMessage(bits <= NUMERIC_DOUBLE, fmt.Sprintf("bits=%x", bits))

		status, err := visitor.NeedsField(fieldInfo)
		if err != nil {
			return err
		}
		switch status {
		case STORED_FIELD_VISITOR_STATUS_YES:
			r.readField(documentInput, visitor, fieldInfo, bits)
		case STORED_FIELD_VISITOR_STATUS_NO:
			panic("not implemented yet")
		case STORED_FIELD_VISITOR_STATUS_STOP:
			return nil
		}
	}

	return nil
}

func assertWithMessage(ok bool, msg string) {
	if !ok {
		panic(msg)
	}
}

func (r *CompressingStoredFieldsReader) Clone() StoredFieldsReader {
	r.ensureOpen()
	return newCompressingStoredFieldsReaderFrom(r)
}
<|endoftext|>


<commit_before>package ast

import "monkey/token"

type IntegerLiteral struct {
	Token token.Token
	Value int64
}

func (il *IntegerLiteral) expressionNode() {}

func (il *IntegerLiteral) TokenLiteral() string {
	return il.Token.Literal
}

func (il *IntegerLiteral) String() string {
	return il.Token.Literal
}
<commit_msg>added integer literal doc comments<commit_after>package ast

import "monkey/token"

// IntegerLiteral : Expression node representing an integer literal
type IntegerLiteral struct {
	Token token.Token
	Value int64
}

func (il *IntegerLiteral) expressionNode() {}

// TokenLiteral : Returns the literal representation of the token
func (il *IntegerLiteral) TokenLiteral() string {
	return il.Token.Literal
}

func (il *IntegerLiteral) String() string {
	return il.Token.Literal
}
<|endoftext|>


<commit_before>package gform

import (
    "syscall"
    "unsafe"
    "w32"
    "w32/user32"
)

type ListView struct {
    W32Control

    onEndLabelEdit EventManager
    onDBLClick     EventManager
    onClick        EventManager
}

func NewListView(parent Controller) *ListView {
    lv := new(ListView)
    lv.init(parent)

    lv.SetFont(DefaultFont)
    lv.SetSize(100, 100)

    return lv
}

func AttachListView(parent Controller, id int) *ListView {
    lv := new(ListView)
    lv.attach(parent, id)
    RegMsgHandler(lv)

    user32.SendMessage(lv.Handle(), w32.LVM_SETUNICODEFORMAT, w32.TRUE, 0)
    return lv
}

func (this *ListView) init(parent Controller) {
    this.W32Control.init("SysListView32", parent, 0, w32.WS_CHILD|w32.WS_VISIBLE|w32.WS_BORDER|w32.LVS_REPORT|w32.LVS_EDITLABELS)
    RegMsgHandler(this)
}

func (this *ListView) EnableSingleSelect(enable bool) {
    ToggleStyle(this.hwnd, enable, w32.LVS_SINGLESEL)
}

func (this *ListView) EnableSortHeader(enable bool) {
    ToggleStyle(this.hwnd, enable, w32.LVS_NOSORTHEADER)
}

func (this *ListView) EnableSortAscending(enable bool) {
    ToggleStyle(this.hwnd, enable, w32.LVS_SORTASCENDING)
}

func (this *ListView) EnableEditLabels(enable bool) {
    ToggleStyle(this.hwnd, enable, w32.LVS_EDITLABELS)
}

func (this *ListView) EnableFullRowSelect(enable bool) {
    if enable {
        user32.SendMessage(this.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, 0, w32.LVS_EX_FULLROWSELECT)
    } else {
        user32.SendMessage(this.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, w32.LVS_EX_FULLROWSELECT, 0)
    }
}

func (this *ListView) EnableDoubleBuffer(enable bool) {
    if enable {
        user32.SendMessage(this.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, 0, w32.LVS_EX_DOUBLEBUFFER)
    } else {
        user32.SendMessage(this.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, w32.LVS_EX_DOUBLEBUFFER, 0)
    }
}

func (this *ListView) EnableHotTrack(enable bool) {
    if enable {
        user32.SendMessage(this.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, 0, w32.LVS_EX_TRACKSELECT)
    } else {
        user32.SendMessage(this.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, w32.LVS_EX_TRACKSELECT, 0)
    }
}

func (this *ListView) SetItemCount(count int) bool {
    return user32.SendMessage(this.hwnd, w32.LVM_SETITEMCOUNT, uintptr(count), 0) != 0
}

func (this *ListView) GetItemCount() int {
    return int(user32.SendMessage(this.hwnd, w32.LVM_GETITEMCOUNT, 0, 0))
}

func (this *ListView) InsertColumn(caption string, width int, iCol int) {
    var lc w32.LVCOLUMN
    lc.Mask = w32.LVCF_TEXT
    if width != 0 {
        lc.Mask = lc.Mask | w32.LVCF_WIDTH
        lc.Cx = width
    }
    lc.PszText = syscall.StringToUTF16Ptr(caption)

    this.InsertLvColumn(&lc, iCol)
}

func (this *ListView) InsertItem(caption string, index int) {
    var li w32.LVITEM
    li.Mask = w32.LVIF_TEXT
    li.PszText = syscall.StringToUTF16Ptr(caption)
    li.IItem = index

    this.InsertLvItem(&li)
}

func (this *ListView) AddItem(caption string) {
    this.InsertItem(caption, this.GetItemCount())
}

func (this *ListView) InsertLvColumn(lvColumn *w32.LVCOLUMN, iCol int) {
    user32.SendMessage(this.hwnd, w32.LVM_INSERTCOLUMN, uintptr(iCol), uintptr(unsafe.Pointer(lvColumn)))
}

func (this *ListView) InsertLvItem(lvItem *w32.LVITEM) {
    user32.SendMessage(this.hwnd, w32.LVM_INSERTITEM, 0, uintptr(unsafe.Pointer(lvItem)))
}

func (this *ListView) DeleteAllItems() bool {
    return user32.SendMessage(this.hwnd, w32.LVM_DELETEALLITEMS, 0, 0) == w32.TRUE
}

func (this *ListView) GetItem(item *w32.LVITEM) bool {
    return user32.SendMessage(this.hwnd, w32.LVM_GETITEM, 0, uintptr(unsafe.Pointer(item))) == w32.TRUE
}

func (this *ListView) GetItemAtIndex(i int) *w32.LVITEM {
    var item w32.LVITEM
    item.Mask = w32.LVIF_PARAM | w32.LVIF_TEXT
    item.IItem = i

    this.GetItem(&item)
    return &item
}

// mask is used to set the LVITEM.Mask for ListView.GetItem which indicates which attributes you'd like to receive
// of LVITEM.
func (this *ListView) GetSelectedItems(mask uint) []*w32.LVITEM {
    items := make([]*w32.LVITEM, 0)

    var i int = -1
    for {
        if i = int(user32.SendMessage(this.hwnd, w32.LVM_GETNEXTITEM, uintptr(i), uintptr(w32.LVNI_SELECTED))); i == -1 {
            break
        }

        var item w32.LVITEM
        item.Mask = mask
        item.IItem = i
        if this.GetItem(&item) {
            items = append(items, &item)
        }
    }
    return items
}

func (this *ListView) GetSelectedCount() uint {
    return uint(user32.SendMessage(this.hwnd, w32.LVM_GETSELECTEDCOUNT, 0, 0))
}

func (this *ListView) SetImageList(imageList *ImageList, imageListType int) *ImageList {
    h := user32.SendMessage(this.hwnd, w32.LVM_SETIMAGELIST, uintptr(imageListType), uintptr(imageList.Handle()))
    if h == 0 {
        return nil
    }

    return &ImageList{w32.HIMAGELIST(h)}
}

func (this *ListView) GetImageList(imageListType int) *ImageList {
    h := user32.SendMessage(this.hwnd, w32.LVM_GETIMAGELIST, uintptr(imageListType), 0)
    if h == 0 {
        return nil
    }

    return &ImageList{w32.HIMAGELIST(h)}
}

// Event publishers
func (this *ListView) OnEndLabelEdit() *EventManager {
    return &this.onEndLabelEdit
}

func (this *ListView) OnDBLClick() *EventManager {
    return &this.onDBLClick
}

func (this *ListView) OnClick() *EventManager {
    return &this.onClick
}

// Message processer
func (this *ListView) WndProc(msg uint, wparam, lparam uintptr) uintptr {
    switch msg {
    case w32.WM_NOTIFY:
        nm := (*w32.NMHDR)(unsafe.Pointer(lparam))
        switch int(nm.Code) {
        case w32.LVN_BEGINLABELEDITW:
            // println("Begin label edit")
        case w32.LVN_ENDLABELEDITW:
            nmdi := (*w32.NMLVDISPINFO)(unsafe.Pointer(lparam))
            if nmdi.Item.PszText != nil {
                this.onEndLabelEdit.Fire(NewEventArg(this, &LVEndLabelEditEventData{Item: &nmdi.Item}))
                return w32.TRUE
            }
        case w32.NM_DBLCLK:
            nmItem := (*w32.NMITEMACTIVATE)(unsafe.Pointer(lparam))
            this.onDBLClick.Fire(NewEventArg(this, &LVDBLClickEventData{NmItem: nmItem}))
        case w32.NM_CLICK:
            nmItem := (*w32.NMITEMACTIVATE)(unsafe.Pointer(lparam))
            this.onClick.Fire(NewEventArg(this, &LVDBLClickEventData{NmItem: nmItem}))
        }
    }

    return this.W32Control.WndProc(msg, wparam, lparam)
}
<commit_msg>Rename all getter methods from "GetXXX" to "XXX".<commit_after>package gform

import (
    "syscall"
    "unsafe"
    "w32"
    "w32/user32"
)

type ListView struct {
    W32Control

    onEndLabelEdit EventManager
    onDBLClick     EventManager
    onClick        EventManager
}

func NewListView(parent Controller) *ListView {
    lv := new(ListView)
    lv.init(parent)

    lv.SetFont(DefaultFont)
    lv.SetSize(100, 100)

    return lv
}

func AttachListView(parent Controller, id int) *ListView {
    lv := new(ListView)
    lv.attach(parent, id)
    RegMsgHandler(lv)

    user32.SendMessage(lv.Handle(), w32.LVM_SETUNICODEFORMAT, w32.TRUE, 0)
    return lv
}

func (this *ListView) init(parent Controller) {
    this.W32Control.init("SysListView32", parent, 0, w32.WS_CHILD|w32.WS_VISIBLE|w32.WS_BORDER|w32.LVS_REPORT|w32.LVS_EDITLABELS)
    RegMsgHandler(this)
}

// Changes the state of an item in a list-view control. Refer LVM_SETITEMSTATE message.
func (this *ListView) setItemState(i int, state, mask uint) {
    var item w32.LVITEM
    item.State, item.StateMask = state, mask

    user32.SendMessage(this.hwnd, w32.LVM_SETITEMSTATE, uintptr(i), uintptr(unsafe.Pointer(&item)))
}

func (this *ListView) EnableSingleSelect(enable bool) {
    ToggleStyle(this.hwnd, enable, w32.LVS_SINGLESEL)
}

func (this *ListView) EnableSortHeader(enable bool) {
    ToggleStyle(this.hwnd, enable, w32.LVS_NOSORTHEADER)
}

func (this *ListView) EnableSortAscending(enable bool) {
    ToggleStyle(this.hwnd, enable, w32.LVS_SORTASCENDING)
}

func (this *ListView) EnableEditLabels(enable bool) {
    ToggleStyle(this.hwnd, enable, w32.LVS_EDITLABELS)
}

func (this *ListView) EnableFullRowSelect(enable bool) {
    if enable {
        user32.SendMessage(this.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, 0, w32.LVS_EX_FULLROWSELECT)
    } else {
        user32.SendMessage(this.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, w32.LVS_EX_FULLROWSELECT, 0)
    }
}

func (this *ListView) EnableDoubleBuffer(enable bool) {
    if enable {
        user32.SendMessage(this.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, 0, w32.LVS_EX_DOUBLEBUFFER)
    } else {
        user32.SendMessage(this.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, w32.LVS_EX_DOUBLEBUFFER, 0)
    }
}

func (this *ListView) EnableHotTrack(enable bool) {
    if enable {
        user32.SendMessage(this.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, 0, w32.LVS_EX_TRACKSELECT)
    } else {
        user32.SendMessage(this.hwnd, w32.LVM_SETEXTENDEDLISTVIEWSTYLE, w32.LVS_EX_TRACKSELECT, 0)
    }
}

func (this *ListView) SetItemCount(count int) bool {
    return user32.SendMessage(this.hwnd, w32.LVM_SETITEMCOUNT, uintptr(count), 0) != 0
}

func (this *ListView) ItemCount() int {
    return int(user32.SendMessage(this.hwnd, w32.LVM_GETITEMCOUNT, 0, 0))
}

func (this *ListView) InsertColumn(caption string, width int, iCol int) {
    var lc w32.LVCOLUMN
    lc.Mask = w32.LVCF_TEXT
    if width != 0 {
        lc.Mask = lc.Mask | w32.LVCF_WIDTH
        lc.Cx = width
    }
    lc.PszText = syscall.StringToUTF16Ptr(caption)

    this.InsertLvColumn(&lc, iCol)
}

func (this *ListView) InsertItem(caption string, index int) {
    var li w32.LVITEM
    li.Mask = w32.LVIF_TEXT
    li.PszText = syscall.StringToUTF16Ptr(caption)
    li.IItem = index

    this.InsertLvItem(&li)
}

func (this *ListView) AddItem(caption string) {
    this.InsertItem(caption, this.ItemCount())
}

func (this *ListView) InsertLvColumn(lvColumn *w32.LVCOLUMN, iCol int) {
    user32.SendMessage(this.hwnd, w32.LVM_INSERTCOLUMN, uintptr(iCol), uintptr(unsafe.Pointer(lvColumn)))
}

func (this *ListView) InsertLvItem(lvItem *w32.LVITEM) {
    user32.SendMessage(this.hwnd, w32.LVM_INSERTITEM, 0, uintptr(unsafe.Pointer(lvItem)))
}

func (this *ListView) DeleteAllItems() bool {
    return user32.SendMessage(this.hwnd, w32.LVM_DELETEALLITEMS, 0, 0) == w32.TRUE
}

func (this *ListView) Item(item *w32.LVITEM) bool {
    return user32.SendMessage(this.hwnd, w32.LVM_GETITEM, 0, uintptr(unsafe.Pointer(item))) == w32.TRUE
}

func (this *ListView) ItemAtIndex(i int) *w32.LVITEM {
    var item w32.LVITEM
    item.Mask = w32.LVIF_PARAM | w32.LVIF_TEXT
    item.IItem = i

    this.Item(&item)
    return &item
}

// mask is used to set the LVITEM.Mask for ListView.GetItem which indicates which attributes you'd like to receive
// of LVITEM.
func (this *ListView) SelectedItems(mask uint) []*w32.LVITEM {
    items := make([]*w32.LVITEM, 0)

    var i int = -1
    for {
        if i = int(user32.SendMessage(this.hwnd, w32.LVM_GETNEXTITEM, uintptr(i), uintptr(w32.LVNI_SELECTED))); i == -1 {
            break
        }

        var item w32.LVITEM
        item.Mask = mask
        item.IItem = i
        if this.Item(&item) {
            items = append(items, &item)
        }
    }
    return items
}

func (this *ListView) SelectedCount() uint {
    return uint(user32.SendMessage(this.hwnd, w32.LVM_GETSELECTEDCOUNT, 0, 0))
}

// Set i to -1 to select all items.
func (this *ListView) SetSelectedItem(i int) {
    this.setItemState(i, w32.LVIS_SELECTED, w32.LVIS_SELECTED)
}

func (this *ListView) SetImageList(imageList *ImageList, imageListType int) *ImageList {
    h := user32.SendMessage(this.hwnd, w32.LVM_SETIMAGELIST, uintptr(imageListType), uintptr(imageList.Handle()))
    if h == 0 {
        return nil
    }

    return &ImageList{w32.HIMAGELIST(h)}
}

func (this *ListView) ImageList(imageListType int) *ImageList {
    h := user32.SendMessage(this.hwnd, w32.LVM_GETIMAGELIST, uintptr(imageListType), 0)
    if h == 0 {
        return nil
    }

    return &ImageList{w32.HIMAGELIST(h)}
}

// Event publishers
func (this *ListView) OnEndLabelEdit() *EventManager {
    return &this.onEndLabelEdit
}

func (this *ListView) OnDBLClick() *EventManager {
    return &this.onDBLClick
}

func (this *ListView) OnClick() *EventManager {
    return &this.onClick
}

// Message processer
func (this *ListView) WndProc(msg uint, wparam, lparam uintptr) uintptr {
    switch msg {
    case w32.WM_NOTIFY:
        nm := (*w32.NMHDR)(unsafe.Pointer(lparam))
        switch int(nm.Code) {
        case w32.LVN_BEGINLABELEDITW:
            // println("Begin label edit")
        case w32.LVN_ENDLABELEDITW:
            nmdi := (*w32.NMLVDISPINFO)(unsafe.Pointer(lparam))
            if nmdi.Item.PszText != nil {
                this.onEndLabelEdit.Fire(NewEventArg(this, &LVEndLabelEditEventData{Item: &nmdi.Item}))
                return w32.TRUE
            }
        case w32.NM_DBLCLK:
            nmItem := (*w32.NMITEMACTIVATE)(unsafe.Pointer(lparam))
            this.onDBLClick.Fire(NewEventArg(this, &LVDBLClickEventData{NmItem: nmItem}))
        case w32.NM_CLICK:
            nmItem := (*w32.NMITEMACTIVATE)(unsafe.Pointer(lparam))
            this.onClick.Fire(NewEventArg(this, &LVDBLClickEventData{NmItem: nmItem}))
        }
    }

    return this.W32Control.WndProc(msg, wparam, lparam)
}
<|endoftext|>


<commit_before>// 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 build provides tools for building Go packages.
package build

import (
	"bytes"
	"exec"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
)

// 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)

	b.goarch = runtime.GOARCH
	if g := os.Getenv("GOARCH"); g != "" {
		b.goarch = g
	}
	var err os.Error
	b.arch, err = ArchChar(b.goarch)
	if err != nil {
		return nil, err
	}

	// .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(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
	if len(gofiles) > 0 {
		ofile := b.obj + "_go_." + b.arch
		b.gc(ofile, gofiles...)
		ofiles = append(ofiles, ofile)
		s.addIntermediate(ofile)
	}

	// assemble
	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")
	}

	// 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
}

// 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
}

func (c *Cmd) String() string {
	return strings.Join(c.Args, " ")
}

// 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 = c.Dir
	cmd.Env = c.Env
	cmd.Stdout = out
	cmd.Stderr = out
	if c.Stdout != "" {
		f, err := os.Create(c.Stdout)
		if err != nil {
			return err
		}
		defer f.Close()
		cmd.Stdout = f
	}
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("command %q: %v\n%v", c, err, out)
	}
	return nil
}

// ArchChar returns the architecture character for the given goarch.
// For example, ArchChar("amd64") returns "6".
func ArchChar(goarch string) (string, os.Error) {
	switch goarch {
	case "386":
		return "8", nil
	case "amd64":
		return "6", nil
	case "arm":
		return "5", nil
	}
	return "", os.NewError("unsupported GOARCH " + goarch)
}

type build struct {
	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.script.Cmd = append(b.script.Cmd, &c)
}

func (b *build) mkdir(name string) {
	b.add(Cmd{
		Args:   []string{"mkdir", "-p", name},
		Output: []string{name},
	})
}

func (b *build) gc(ofile string, gofiles ...string) {
	gc := b.arch + "g"
	args := append([]string{gc, "-o", ofile}, gcImportArgs...)
	args = append(args, gofiles...)
	b.add(Cmd{
		Args:   args,
		Input:  gofiles,
		Output: []string{ofile},
	})
}

func (b *build) asm(ofile string, sfile string) {
	asm := b.arch + "a"
	b.add(Cmd{
		Args:   []string{asm, "-o", ofile, sfile},
		Input:  []string{sfile},
		Output: []string{ofile},
	})
}

func (b *build) ld(targ string, ofiles ...string) {
	ld := b.arch + "l"
	args := append([]string{ld, "-o", targ}, ldImportArgs...)
	args = append(args, ofiles...)
	b.add(Cmd{
		Args:   args,
		Input:  ofiles,
		Output: []string{targ},
	})
}

func (b *build) gopack(targ string, ofiles ...string) {
	b.add(Cmd{
		Args:   append([]string{"gopack", "grc", targ}, ofiles...),
		Input:  ofiles,
		Output: []string{targ},
	})
}

func (b *build) cc(ofile string, cfiles ...string) {
	cc := b.arch + "c"
	dir := fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH)
	inc := filepath.Join(runtime.GOROOT(), "pkg", dir)
	args := []string{cc, "-FVw", "-I", inc, "-o", ofile}
	b.add(Cmd{
		Args:   append(args, cfiles...),
		Input:  cfiles,
		Output: []string{ofile},
	})
}

func (b *build) gccCompile(ofile, cfile string) {
	b.add(Cmd{
		Args:   b.gccArgs("-o", ofile, "-c", cfile),
		Input:  []string{cfile},
		Output: []string{ofile},
	})
}

func (b *build) gccLink(ofile string, ofiles ...string) {
	b.add(Cmd{
		Args:   append(b.gccArgs("-o", ofile), ofiles...),
		Input:  ofiles,
		Output: []string{ofile},
	})
}

func (b *build) gccArgs(args ...string) []string {
	// TODO(adg): HOST_CC
	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(a, args...)
}

var cgoRe = regexp.MustCompile("[/\\:]")

func (b *build) cgo(cgofiles []string) (outGo, outObj []string) {
	// cgo
	// TODO(adg): CGOPKGPATH
	// TODO(adg): CGO_FLAGS
	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 + 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}, 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
	b.cc(defunObj, defunC)
	outObj = append(outObj, defunObj)

	// gcc
	linkobj := make([]string, 0, len(cfiles))
	for _, cfile := range cfiles {
		ofile := cfile[:len(cfile)-1] + "o"
		b.gccCompile(ofile, cfile)
		linkobj = append(linkobj, ofile)
		if !strings.HasSuffix(ofile, "_cgo_main.o") {
			outObj = append(outObj, ofile)
		} else {
			b.script.addIntermediate(ofile)
		}
	}
	dynObj := b.obj + "_cgo_.o"
	b.gccLink(dynObj, linkobj...)
	b.script.addIntermediate(dynObj)

	// cgo -dynimport
	importC := b.obj + "_cgo_import.c"
	b.add(Cmd{
		Args:   []string{"cgo", "-dynimport", dynObj},
		Stdout: importC,
		Input:  []string{dynObj},
		Output: []string{importC},
	})
	b.script.addIntermediate(importC)

	// cc _cgo_import.ARCH
	importObj := b.obj + "_cgo_import." + b.arch
	b.cc(importObj, importC)
	outObj = append(outObj, importObj)

	return
}
<commit_msg>go/build: use back quotes for regular expression<commit_after>// 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 build provides tools for building Go packages.
package build

import (
	"bytes"
	"exec"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
)

// 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)

	b.goarch = runtime.GOARCH
	if g := os.Getenv("GOARCH"); g != "" {
		b.goarch = g
	}
	var err os.Error
	b.arch, err = ArchChar(b.goarch)
	if err != nil {
		return nil, err
	}

	// .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(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
	if len(gofiles) > 0 {
		ofile := b.obj + "_go_." + b.arch
		b.gc(ofile, gofiles...)
		ofiles = append(ofiles, ofile)
		s.addIntermediate(ofile)
	}

	// assemble
	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")
	}

	// 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
}

// 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
}

func (c *Cmd) String() string {
	return strings.Join(c.Args, " ")
}

// 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 = c.Dir
	cmd.Env = c.Env
	cmd.Stdout = out
	cmd.Stderr = out
	if c.Stdout != "" {
		f, err := os.Create(c.Stdout)
		if err != nil {
			return err
		}
		defer f.Close()
		cmd.Stdout = f
	}
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("command %q: %v\n%v", c, err, out)
	}
	return nil
}

// ArchChar returns the architecture character for the given goarch.
// For example, ArchChar("amd64") returns "6".
func ArchChar(goarch string) (string, os.Error) {
	switch goarch {
	case "386":
		return "8", nil
	case "amd64":
		return "6", nil
	case "arm":
		return "5", nil
	}
	return "", os.NewError("unsupported GOARCH " + goarch)
}

type build struct {
	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.script.Cmd = append(b.script.Cmd, &c)
}

func (b *build) mkdir(name string) {
	b.add(Cmd{
		Args:   []string{"mkdir", "-p", name},
		Output: []string{name},
	})
}

func (b *build) gc(ofile string, gofiles ...string) {
	gc := b.arch + "g"
	args := append([]string{gc, "-o", ofile}, gcImportArgs...)
	args = append(args, gofiles...)
	b.add(Cmd{
		Args:   args,
		Input:  gofiles,
		Output: []string{ofile},
	})
}

func (b *build) asm(ofile string, sfile string) {
	asm := b.arch + "a"
	b.add(Cmd{
		Args:   []string{asm, "-o", ofile, sfile},
		Input:  []string{sfile},
		Output: []string{ofile},
	})
}

func (b *build) ld(targ string, ofiles ...string) {
	ld := b.arch + "l"
	args := append([]string{ld, "-o", targ}, ldImportArgs...)
	args = append(args, ofiles...)
	b.add(Cmd{
		Args:   args,
		Input:  ofiles,
		Output: []string{targ},
	})
}

func (b *build) gopack(targ string, ofiles ...string) {
	b.add(Cmd{
		Args:   append([]string{"gopack", "grc", targ}, ofiles...),
		Input:  ofiles,
		Output: []string{targ},
	})
}

func (b *build) cc(ofile string, cfiles ...string) {
	cc := b.arch + "c"
	dir := fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH)
	inc := filepath.Join(runtime.GOROOT(), "pkg", dir)
	args := []string{cc, "-FVw", "-I", inc, "-o", ofile}
	b.add(Cmd{
		Args:   append(args, cfiles...),
		Input:  cfiles,
		Output: []string{ofile},
	})
}

func (b *build) gccCompile(ofile, cfile string) {
	b.add(Cmd{
		Args:   b.gccArgs("-o", ofile, "-c", cfile),
		Input:  []string{cfile},
		Output: []string{ofile},
	})
}

func (b *build) gccLink(ofile string, ofiles ...string) {
	b.add(Cmd{
		Args:   append(b.gccArgs("-o", ofile), ofiles...),
		Input:  ofiles,
		Output: []string{ofile},
	})
}

func (b *build) gccArgs(args ...string) []string {
	// TODO(adg): HOST_CC
	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(a, args...)
}

var cgoRe = regexp.MustCompile(`[/\\:]`)

func (b *build) cgo(cgofiles []string) (outGo, outObj []string) {
	// cgo
	// TODO(adg): CGOPKGPATH
	// TODO(adg): CGO_FLAGS
	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 + 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}, 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
	b.cc(defunObj, defunC)
	outObj = append(outObj, defunObj)

	// gcc
	linkobj := make([]string, 0, len(cfiles))
	for _, cfile := range cfiles {
		ofile := cfile[:len(cfile)-1] + "o"
		b.gccCompile(ofile, cfile)
		linkobj = append(linkobj, ofile)
		if !strings.HasSuffix(ofile, "_cgo_main.o") {
			outObj = append(outObj, ofile)
		} else {
			b.script.addIntermediate(ofile)
		}
	}
	dynObj := b.obj + "_cgo_.o"
	b.gccLink(dynObj, linkobj...)
	b.script.addIntermediate(dynObj)

	// cgo -dynimport
	importC := b.obj + "_cgo_import.c"
	b.add(Cmd{
		Args:   []string{"cgo", "-dynimport", dynObj},
		Stdout: importC,
		Input:  []string{dynObj},
		Output: []string{importC},
	})
	b.script.addIntermediate(importC)

	// cc _cgo_import.ARCH
	importObj := b.obj + "_cgo_import." + b.arch
	b.cc(importObj, importC)
	outObj = append(outObj, importObj)

	return
}
<|endoftext|>