<commit_before>// Copyright 2015 Google Inc.
//
// 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.

// Program yang parses YANG files, displays errors, and possibly writes
// something related to the input on output.
//
// Usage: yang [--path PATH] [--format FORMAT] [MODULE] [FILE ...]
//
// If MODULE is specified (an argument that does not end in .yang), it is taken
// as the name of the module to display.  Any FILEs specified are read, and the
// tree for MODULE is displayed.  If MODULE was not defined in FILEs (or no
// files were specified), then the file MODULES.yang is read as well.  An error
// is displayed if no definition for MODULE was found.
//
// If MODULE is missing, then all base modules read from the FILEs are
// displayed.  If there are no arguments then standard input is parsed.
//
// If PATH is specified, it is considered a comma separated list of paths
// to append to the search directory.
//
// FORMAT, which defaults to "tree", specifes the format of output to produce:
//
//   tree   All modules in tree form
//   proto  All directory nodes as proto structures
//   types  All type definitions
//
// THIS PROGRAM IS STILL JUST A DEVELOPMENT TOOL.
package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"sort"
	"strings"

	"github.com/openconfig/goyang/pkg/indent"
	"github.com/openconfig/goyang/pkg/yang"
	"github.com/pborman/getopt"
)

// Types keeps track of all the YangTypes defined.
type Types map[*yang.YangType]struct{}

// AddEntry adds all types defined in e and its decendents to t.
func (t Types) AddEntry(e *yang.Entry) {
	if e == nil {
		return
	}
	if e.Type != nil {
		t[e.Type.Root] = struct{}{}
	}
	for _, d := range e.Dir {
		t.AddEntry(d)
	}
	t.AddEntry(e.Choice)
	t.AddEntry(e.Case)
}

// exitIfError writes errs to standard error and exits with an exit status of 1.
// If errs is empty then exitIfError does nothing and simply returns.
func exitIfError(errs []error) {
	if len(errs) > 0 {
		for _, err := range errs {
			fmt.Fprintln(os.Stderr, err)
		}
		os.Exit(1)
	}
}

func main() {
	format := "tree"
	getopt.CommandLine.ListVarLong(&yang.Path, "path", 0, "comma separated list of directories to add to PATH")
	getopt.CommandLine.StringVarLong(&format, "format", 0, "format to display: tree, proto, types")

	getopt.Parse()
	files := getopt.Args()

	if len(files) > 0 && !strings.HasSuffix(files[0], ".yang") {
		e, errs := yang.GetModule(files[0], files[1:]...)
		exitIfError(errs)
		Write(os.Stdout, e)
		return
	}

	// Okay, either there are no arguments and we read stdin, or there
	// is one or more file names listed.  Read them in and display them.

	ms := yang.NewModules()

	if len(files) == 0 {
		data, err := ioutil.ReadAll(os.Stdin)
		if err == nil {
			err = ms.Parse(string(data), "<STDIN>")
		}
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
	}

	for _, name := range files {
		if err := ms.Read(name); err != nil {
			fmt.Fprintln(os.Stderr, err)
			continue
		}
	}

	// Keep track of the top level modules we read in.
	// Those are the only modules we want to print below.
	mods := map[string]*yang.Module{}
	var names []string

	for _, m := range ms.Modules {
		if mods[m.Name] == nil {
			mods[m.Name] = m
			names = append(names, m.Name)
		}
	}
	sort.Strings(names)

	// Print any errors found in the tree.  This will return false if
	// there were no errors.
	exitIfError(ms.Process())

	switch format {
	case "tree":
		for _, n := range names {
			Write(os.Stdout, yang.ToEntry(mods[n]))
		}
	case "proto":
		for _, n := range names {
			for _, e := range flatten(yang.ToEntry(mods[n])) {
				FormatNode(os.Stdout, e)
			}
		}
	case "types":
		types := Types{}
		for _, n := range names {
			types.AddEntry(yang.ToEntry(mods[n]))
		}

		for t := range types {
			YTPrint(os.Stdout, t)
		}
	default:
		fmt.Fprintf(os.Stderr, "unknown format: %s\n", format)
		os.Exit(1)
	}
}

// fixedNames maps a fixed name back to its origial name.
var fixedNames = map[string]string{}

func fixName(s string) string {
	cc := yang.CamelCase(s)
	if cc != s {
		if o := fixedNames[cc]; o != "" && o != s {
			fmt.Printf("Collision on %s and %s\n", o, s)
		}
		fixedNames[cc] = s
	}
	return cc
}

func getTypeName(e *yang.Entry) string {
	if e == nil || e.Type == nil {
		return ""
	}
	// Return our root's type name.
	// This is should be the builtin type-name
	// for this entry.
	return e.Type.Root.Name
}

// Write writes e, formatted, and all of its children, to w.
func Write(w io.Writer, e *yang.Entry) {
	if e.Description != "" {
		fmt.Fprintln(w)
		fmt.Fprintln(indent.NewWriter(w, "// "), e.Description)
	}
	if e.ReadOnly() {
		fmt.Fprintf(w, "RO: ")
	} else {
		fmt.Fprintf(w, "rw: ")
	}
	if e.Type != nil {
		fmt.Fprintf(w, "%s ", getTypeName(e))
	}
	switch {
	case e.Dir == nil && e.IsList:
		fmt.Fprintf(w, "[]%s\n", e.Name)
		return
	case e.Dir == nil:
		fmt.Fprintf(w, "%s\n", e.Name)
		return
	case e.IsList:
		fmt.Fprintf(w, "[%s]%s {\n", e.Key, e.Name) //}
	default:
		fmt.Fprintf(w, "%s {\n", e.Name) //}
	}
	var names []string
	for k := range e.Dir {
		names = append(names, k)
	}
	sort.Strings(names)
	for _, k := range names {
		Write(indent.NewWriter(w, "  "), e.Dir[k])
	}
	// { to match the brace below to keep brace matching working
	fmt.Fprintln(w, "}")
}

// kind2proto maps base yang types to protocol buffer types.
// TODO(borman): do TODO types.
var kind2proto = map[yang.TypeKind]string{
	yang.Yint8:   "int32",  // int in range [-128, 127]
	yang.Yint16:  "int32",  // int in range [-32768, 32767]
	yang.Yint32:  "int32",  // int in range [-2147483648, 2147483647]
	yang.Yint64:  "int64",  // int in range [-9223372036854775808, 9223372036854775807]
	yang.Yuint8:  "uint32", // int in range [0, 255]
	yang.Yuint16: "uint32", // int in range [0, 65535]
	yang.Yuint32: "uint32", // int in range [0, 4294967295]
	yang.Yuint64: "uint64", // int in range [0, 18446744073709551615]

	yang.Ybinary:             "bytes",          // arbitrary data
	yang.Ybits:               "TODO-bits",      // set of bits or flags
	yang.Ybool:               "bool",           // true or false
	yang.Ydecimal64:          "TODO-decimal64", // signed decimal number
	yang.Yenum:               "TODO-enum",      // enumerated strings
	yang.Yidentityref:        "string",         // reference to abstrace identity
	yang.YinstanceIdentifier: "TODO-ii",        // reference of a data tree node
	yang.Yleafref:            "string",         // reference to a leaf instance
	yang.Ystring:             "string",         // human readable string
	yang.Yunion:              "TODO-union",     // choice of types
}

// FormatNode writes e, formatted almost like a protobuf message, to w.
func FormatNode(w io.Writer, e *yang.Entry) {
	fmt.Fprintln(w)
	if e.Description != "" {
		fmt.Fprintln(indent.NewWriter(w, "// "), e.Description)
	}
	fmt.Fprintf(w, "message %s {\n", fixName(e.Name))

	var names []string
	for k := range e.Dir {
		names = append(names, k)
	}
	sort.Strings(names)
	for x, k := range names {
		se := e.Dir[k]
		if se.Description != "" {
			fmt.Fprintln(indent.NewWriter(w, "  // "), se.Description)
		}
		if se.IsList {
			fmt.Fprint(w, "  repeated ")
		} else {
			fmt.Fprint(w, "  optional ")
		}
		if len(se.Dir) == 0 && se.Type != nil {
			// TODO(borman): this is probably an empty container.
			kind := "UNKNOWN TYPE"
			if se.Type != nil {
				kind = kind2proto[se.Type.Kind]
			}
			fmt.Fprintf(w, "%s %s = %d; // %s\n", kind, fixName(k), x+1, yang.Source(se.Node))
			continue
		}
		fmt.Fprintf(w, "%s %s = %d; // %s\n", fixName(se.Name), fixName(k), x+1, yang.Source(se.Node))
	}
	// { to match the brace below to keep brace matching working
	fmt.Fprintln(w, "}")
}

// flatten returns a slice of all directory entries in e and e's decendents.
func flatten(e *yang.Entry) []*yang.Entry {
	if e == nil || len(e.Dir) == 0 {
		return nil
	}
	f := []*yang.Entry{e}
	var names []string
	for n := range e.Dir {
		names = append(names, n)
	}
	sort.Strings(names)
	for _, n := range names {
		f = append(f, flatten(e.Dir[n])...)
	}
	f = append(f, flatten(e.Choice)...)
	f = append(f, flatten(e.Case)...)
	return f
}

// YTPrint prints type t in a moderately human readable format to w.
func YTPrint(w io.Writer, t *yang.YangType) {
	if t.Base != nil {
		fmt.Fprintf(w, "%s: ", yang.Source(t.Base))
	}
	fmt.Fprintf(w, "%s", t.Root.Name)
	if t.Kind.String() != t.Root.Name {
		fmt.Fprintf(w, "(%s)", t.Kind)
	}
	if t.Units != "" {
		fmt.Fprintf(w, " units=%s", t.Units)
	}
	if t.Default != "" {
		fmt.Fprintf(w, " default=%q", t.Default)
	}
	if t.FractionDigits != 0 {
		fmt.Fprintf(w, " fraction-digits=%d", t.FractionDigits)
	}
	if len(t.Length) > 0 {
		fmt.Fprintf(w, " length=%s", t.Length)
	}
	if t.Kind == yang.YinstanceIdentifier && !t.OptionalInstance {
		fmt.Fprintf(w, " required")
	}
	if t.Kind == yang.Yleafref && t.Path != "" {
		fmt.Fprintf(w, " path=%q", t.Path)
	}
	if len(t.Pattern) > 0 {
		fmt.Fprintf(w, " pattern=%s", strings.Join(t.Pattern, "|"))
	}
	if len(t.Type) > 0 {
		fmt.Fprintf(w, " union...")
	}

	b := yang.BaseTypedefs[t.Kind.String()].YangType
	if len(t.Range) > 0 && !t.Range.Equal(b.Range) {
		fmt.Fprintf(w, " range=%s", t.Range)
	}
	fmt.Fprintf(w, ";\n")
}
<commit_msg>Fix for IsList being removed as an attribute on yang.Entry<commit_after>// Copyright 2015 Google Inc.
//
// 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.

// Program yang parses YANG files, displays errors, and possibly writes
// something related to the input on output.
//
// Usage: yang [--path PATH] [--format FORMAT] [MODULE] [FILE ...]
//
// If MODULE is specified (an argument that does not end in .yang), it is taken
// as the name of the module to display.  Any FILEs specified are read, and the
// tree for MODULE is displayed.  If MODULE was not defined in FILEs (or no
// files were specified), then the file MODULES.yang is read as well.  An error
// is displayed if no definition for MODULE was found.
//
// If MODULE is missing, then all base modules read from the FILEs are
// displayed.  If there are no arguments then standard input is parsed.
//
// If PATH is specified, it is considered a comma separated list of paths
// to append to the search directory.
//
// FORMAT, which defaults to "tree", specifes the format of output to produce:
//
//   tree   All modules in tree form
//   proto  All directory nodes as proto structures
//   types  All type definitions
//
// THIS PROGRAM IS STILL JUST A DEVELOPMENT TOOL.
package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"sort"
	"strings"

	"github.com/openconfig/goyang/pkg/indent"
	"github.com/openconfig/goyang/pkg/yang"
	"github.com/pborman/getopt"
)

// Types keeps track of all the YangTypes defined.
type Types map[*yang.YangType]struct{}

// AddEntry adds all types defined in e and its decendents to t.
func (t Types) AddEntry(e *yang.Entry) {
	if e == nil {
		return
	}
	if e.Type != nil {
		t[e.Type.Root] = struct{}{}
	}
	for _, d := range e.Dir {
		t.AddEntry(d)
	}
	t.AddEntry(e.Choice)
	t.AddEntry(e.Case)
}

// exitIfError writes errs to standard error and exits with an exit status of 1.
// If errs is empty then exitIfError does nothing and simply returns.
func exitIfError(errs []error) {
	if len(errs) > 0 {
		for _, err := range errs {
			fmt.Fprintln(os.Stderr, err)
		}
		os.Exit(1)
	}
}

func main() {
	format := "tree"
	getopt.CommandLine.ListVarLong(&yang.Path, "path", 0, "comma separated list of directories to add to PATH")
	getopt.CommandLine.StringVarLong(&format, "format", 0, "format to display: tree, proto, types")

	getopt.Parse()
	files := getopt.Args()

	if len(files) > 0 && !strings.HasSuffix(files[0], ".yang") {
		e, errs := yang.GetModule(files[0], files[1:]...)
		exitIfError(errs)
		Write(os.Stdout, e)
		return
	}

	// Okay, either there are no arguments and we read stdin, or there
	// is one or more file names listed.  Read them in and display them.

	ms := yang.NewModules()

	if len(files) == 0 {
		data, err := ioutil.ReadAll(os.Stdin)
		if err == nil {
			err = ms.Parse(string(data), "<STDIN>")
		}
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(1)
		}
	}

	for _, name := range files {
		if err := ms.Read(name); err != nil {
			fmt.Fprintln(os.Stderr, err)
			continue
		}
	}

	// Keep track of the top level modules we read in.
	// Those are the only modules we want to print below.
	mods := map[string]*yang.Module{}
	var names []string

	for _, m := range ms.Modules {
		if mods[m.Name] == nil {
			mods[m.Name] = m
			names = append(names, m.Name)
		}
	}
	sort.Strings(names)

	// Print any errors found in the tree.  This will return false if
	// there were no errors.
	exitIfError(ms.Process())

	switch format {
	case "tree":
		for _, n := range names {
			Write(os.Stdout, yang.ToEntry(mods[n]))
		}
	case "proto":
		for _, n := range names {
			for _, e := range flatten(yang.ToEntry(mods[n])) {
				FormatNode(os.Stdout, e)
			}
		}
	case "types":
		types := Types{}
		for _, n := range names {
			types.AddEntry(yang.ToEntry(mods[n]))
		}

		for t := range types {
			YTPrint(os.Stdout, t)
		}
	default:
		fmt.Fprintf(os.Stderr, "unknown format: %s\n", format)
		os.Exit(1)
	}
}

// fixedNames maps a fixed name back to its origial name.
var fixedNames = map[string]string{}

func fixName(s string) string {
	cc := yang.CamelCase(s)
	if cc != s {
		if o := fixedNames[cc]; o != "" && o != s {
			fmt.Printf("Collision on %s and %s\n", o, s)
		}
		fixedNames[cc] = s
	}
	return cc
}

func getTypeName(e *yang.Entry) string {
	if e == nil || e.Type == nil {
		return ""
	}
	// Return our root's type name.
	// This is should be the builtin type-name
	// for this entry.
	return e.Type.Root.Name
}

// Write writes e, formatted, and all of its children, to w.
func Write(w io.Writer, e *yang.Entry) {
	if e.Description != "" {
		fmt.Fprintln(w)
		fmt.Fprintln(indent.NewWriter(w, "// "), e.Description)
	}
	if e.ReadOnly() {
		fmt.Fprintf(w, "RO: ")
	} else {
		fmt.Fprintf(w, "rw: ")
	}
	if e.Type != nil {
		fmt.Fprintf(w, "%s ", getTypeName(e))
	}
	switch {
	case e.Dir == nil && e.ListAttr != nil:
		fmt.Fprintf(w, "[]%s\n", e.Name)
		return
	case e.Dir == nil:
		fmt.Fprintf(w, "%s\n", e.Name)
		return
	case e.ListAttr != nil:
		fmt.Fprintf(w, "[%s]%s {\n", e.Key, e.Name) //}
	default:
		fmt.Fprintf(w, "%s {\n", e.Name) //}
	}
	var names []string
	for k := range e.Dir {
		names = append(names, k)
	}
	sort.Strings(names)
	for _, k := range names {
		Write(indent.NewWriter(w, "  "), e.Dir[k])
	}
	// { to match the brace below to keep brace matching working
	fmt.Fprintln(w, "}")
}

// kind2proto maps base yang types to protocol buffer types.
// TODO(borman): do TODO types.
var kind2proto = map[yang.TypeKind]string{
	yang.Yint8:   "int32",  // int in range [-128, 127]
	yang.Yint16:  "int32",  // int in range [-32768, 32767]
	yang.Yint32:  "int32",  // int in range [-2147483648, 2147483647]
	yang.Yint64:  "int64",  // int in range [-9223372036854775808, 9223372036854775807]
	yang.Yuint8:  "uint32", // int in range [0, 255]
	yang.Yuint16: "uint32", // int in range [0, 65535]
	yang.Yuint32: "uint32", // int in range [0, 4294967295]
	yang.Yuint64: "uint64", // int in range [0, 18446744073709551615]

	yang.Ybinary:             "bytes",          // arbitrary data
	yang.Ybits:               "TODO-bits",      // set of bits or flags
	yang.Ybool:               "bool",           // true or false
	yang.Ydecimal64:          "TODO-decimal64", // signed decimal number
	yang.Yenum:               "TODO-enum",      // enumerated strings
	yang.Yidentityref:        "string",         // reference to abstrace identity
	yang.YinstanceIdentifier: "TODO-ii",        // reference of a data tree node
	yang.Yleafref:            "string",         // reference to a leaf instance
	yang.Ystring:             "string",         // human readable string
	yang.Yunion:              "TODO-union",     // choice of types
}

// FormatNode writes e, formatted almost like a protobuf message, to w.
func FormatNode(w io.Writer, e *yang.Entry) {
	fmt.Fprintln(w)
	if e.Description != "" {
		fmt.Fprintln(indent.NewWriter(w, "// "), e.Description)
	}
	fmt.Fprintf(w, "message %s {\n", fixName(e.Name))

	var names []string
	for k := range e.Dir {
		names = append(names, k)
	}
	sort.Strings(names)
	for x, k := range names {
		se := e.Dir[k]
		if se.Description != "" {
			fmt.Fprintln(indent.NewWriter(w, "  // "), se.Description)
		}
		if se.ListAttr != nil {
			fmt.Fprint(w, "  repeated ")
		} else {
			fmt.Fprint(w, "  optional ")
		}
		if len(se.Dir) == 0 && se.Type != nil {
			// TODO(borman): this is probably an empty container.
			kind := "UNKNOWN TYPE"
			if se.Type != nil {
				kind = kind2proto[se.Type.Kind]
			}
			fmt.Fprintf(w, "%s %s = %d; // %s\n", kind, fixName(k), x+1, yang.Source(se.Node))
			continue
		}
		fmt.Fprintf(w, "%s %s = %d; // %s\n", fixName(se.Name), fixName(k), x+1, yang.Source(se.Node))
	}
	// { to match the brace below to keep brace matching working
	fmt.Fprintln(w, "}")
}

// flatten returns a slice of all directory entries in e and e's decendents.
func flatten(e *yang.Entry) []*yang.Entry {
	if e == nil || len(e.Dir) == 0 {
		return nil
	}
	f := []*yang.Entry{e}
	var names []string
	for n := range e.Dir {
		names = append(names, n)
	}
	sort.Strings(names)
	for _, n := range names {
		f = append(f, flatten(e.Dir[n])...)
	}
	f = append(f, flatten(e.Choice)...)
	f = append(f, flatten(e.Case)...)
	return f
}

// YTPrint prints type t in a moderately human readable format to w.
func YTPrint(w io.Writer, t *yang.YangType) {
	if t.Base != nil {
		fmt.Fprintf(w, "%s: ", yang.Source(t.Base))
	}
	fmt.Fprintf(w, "%s", t.Root.Name)
	if t.Kind.String() != t.Root.Name {
		fmt.Fprintf(w, "(%s)", t.Kind)
	}
	if t.Units != "" {
		fmt.Fprintf(w, " units=%s", t.Units)
	}
	if t.Default != "" {
		fmt.Fprintf(w, " default=%q", t.Default)
	}
	if t.FractionDigits != 0 {
		fmt.Fprintf(w, " fraction-digits=%d", t.FractionDigits)
	}
	if len(t.Length) > 0 {
		fmt.Fprintf(w, " length=%s", t.Length)
	}
	if t.Kind == yang.YinstanceIdentifier && !t.OptionalInstance {
		fmt.Fprintf(w, " required")
	}
	if t.Kind == yang.Yleafref && t.Path != "" {
		fmt.Fprintf(w, " path=%q", t.Path)
	}
	if len(t.Pattern) > 0 {
		fmt.Fprintf(w, " pattern=%s", strings.Join(t.Pattern, "|"))
	}
	if len(t.Type) > 0 {
		fmt.Fprintf(w, " union...")
	}

	b := yang.BaseTypedefs[t.Kind.String()].YangType
	if len(t.Range) > 0 && !t.Range.Equal(b.Range) {
		fmt.Fprintf(w, " range=%s", t.Range)
	}
	fmt.Fprintf(w, ";\n")
}
<|endoftext|>


<commit_before>// +build !plan9

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	. "github.com/candid82/joker/core"
	"github.com/candid82/liner"
)

var qualifiedSymbolRe *regexp.Regexp = regexp.MustCompile(`([0-9A-Za-z_\-\+\*\'\.]+)/([0-9A-Za-z_\-\+\*\']*$)`)
var callRe *regexp.Regexp = regexp.MustCompile(`\(\s*([0-9A-Za-z_\-\+\*\'\.]*$)`)

func completer(line string, pos int) (head string, c []string, tail string) {
	head = line[:pos]
	tail = line[pos:]
	var match []string
	var prefix string
	var ns *Namespace
	var addNamespaces bool
	if match = qualifiedSymbolRe.FindStringSubmatch(head); match != nil {
		nsName := match[1]
		prefix = match[2]
		ns = GLOBAL_ENV.NamespaceFor(GLOBAL_ENV.CurrentNamespace(), MakeSymbol(nsName+"/"+prefix))
	} else if match = callRe.FindStringSubmatch(head); match != nil {
		prefix = match[1]
		ns = GLOBAL_ENV.CurrentNamespace()
		addNamespaces = true
	}
	if ns == nil {
		return
	}
	for k, _ := range ns.Mappings() {
		if strings.HasPrefix(*k, prefix) {
			c = append(c, *k)
		}
	}
	if addNamespaces {
		for k, _ := range GLOBAL_ENV.Namespaces {
			if strings.HasPrefix(*k, prefix) {
				c = append(c, *k)
			}
		}
		for k, _ := range ns.Aliases() {
			if strings.HasPrefix(*k, prefix) {
				c = append(c, *k)
			}
		}
	}
	if len(c) > 0 {
		head = head[:len(head)-len(prefix)]
	}
	return
}

func saveReplHistory(rl *liner.State, filename string) {
	if filename == "" {
		return
	}
	if f, err := os.Create(filename); err == nil {
		rl.WriteHistory(f)
		f.Close()
	}
}

func repl(phase Phase) {
	ProcessReplData()
	GLOBAL_ENV.FindNamespace(MakeSymbol("user")).ReferAll(GLOBAL_ENV.FindNamespace(MakeSymbol("joker.repl")))
	fmt.Printf("Welcome to joker %s. Use '(exit)', EOF (Ctrl-D) or SIGINT (Ctrl-C) to exit.\n", VERSION)
	parseContext := &ParseContext{GlobalEnv: GLOBAL_ENV}
	replContext := NewReplContext(parseContext.GlobalEnv)

	var runeReader io.RuneReader
	var rl *liner.State
	var historyFilename string
	if noReadline {
		runeReader = bufio.NewReader(Stdin)
	} else {
		home := HomeDir()
		jokerd := filepath.Join(home, ".jokerd")
		if _, err := os.Stat(jokerd); os.IsNotExist(err) {
			if err := os.MkdirAll(jokerd, 0777); err != nil {
				fmt.Fprintf(Stderr, "WARNING: could not create %s \n", jokerd)
			}
		}
		if !noReplHistory {
			historyFilename = filepath.Join(jokerd, ".repl_history")
		}
		rl = liner.NewLiner()
		OnExit(func() {
			saveReplHistory(rl, historyFilename)
			rl.Close()
		})
		defer rl.Close()
		rl.SetCtrlCAborts(true)
		rl.SetWordCompleter(completer)

		if !noReplHistory {
			if f, err := os.Open(historyFilename); err == nil {
				rl.ReadHistory(f)
				f.Close()
			}
		}

		runeReader = NewLineRuneReader(rl)

		for _, line := range strings.Split(string(dataRead), "\n") {
			if strings.TrimSpace(line) != "" {
				rl.AppendHistory(line)
			}
		}
		dataRead = []rune{}
	}

	reader := NewReader(runeReader, "<repl>")

	for {
		if noReadline {
			print(GLOBAL_ENV.CurrentNamespace().Name.ToString(false) + "=> ")
		} else {
			runeReader.(*LineRuneReader).Prompt = (GLOBAL_ENV.CurrentNamespace().Name.ToString(false) + "=> ")
		}
		if processReplCommand(reader, phase, parseContext, replContext) {
			saveReplHistory(rl, historyFilename)
			return
		}
	}
}
<commit_msg>Use bash-like tab completion.<commit_after>// +build !plan9

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	. "github.com/candid82/joker/core"
	"github.com/candid82/liner"
)

var qualifiedSymbolRe *regexp.Regexp = regexp.MustCompile(`([0-9A-Za-z_\-\+\*\'\.]+)/([0-9A-Za-z_\-\+\*\']*$)`)
var callRe *regexp.Regexp = regexp.MustCompile(`\(\s*([0-9A-Za-z_\-\+\*\'\.]*$)`)

func completer(line string, pos int) (head string, c []string, tail string) {
	head = line[:pos]
	tail = line[pos:]
	var match []string
	var prefix string
	var ns *Namespace
	var addNamespaces bool
	if match = qualifiedSymbolRe.FindStringSubmatch(head); match != nil {
		nsName := match[1]
		prefix = match[2]
		ns = GLOBAL_ENV.NamespaceFor(GLOBAL_ENV.CurrentNamespace(), MakeSymbol(nsName+"/"+prefix))
	} else if match = callRe.FindStringSubmatch(head); match != nil {
		prefix = match[1]
		ns = GLOBAL_ENV.CurrentNamespace()
		addNamespaces = true
	}
	if ns == nil {
		return
	}
	for k, _ := range ns.Mappings() {
		if strings.HasPrefix(*k, prefix) {
			c = append(c, *k)
		}
	}
	if addNamespaces {
		for k, _ := range GLOBAL_ENV.Namespaces {
			if strings.HasPrefix(*k, prefix) {
				c = append(c, *k)
			}
		}
		for k, _ := range ns.Aliases() {
			if strings.HasPrefix(*k, prefix) {
				c = append(c, *k)
			}
		}
	}
	if len(c) > 0 {
		head = head[:len(head)-len(prefix)]
	}
	return
}

func saveReplHistory(rl *liner.State, filename string) {
	if filename == "" {
		return
	}
	if f, err := os.Create(filename); err == nil {
		rl.WriteHistory(f)
		f.Close()
	}
}

func repl(phase Phase) {
	ProcessReplData()
	GLOBAL_ENV.FindNamespace(MakeSymbol("user")).ReferAll(GLOBAL_ENV.FindNamespace(MakeSymbol("joker.repl")))
	fmt.Printf("Welcome to joker %s. Use '(exit)', EOF (Ctrl-D) or SIGINT (Ctrl-C) to exit.\n", VERSION)
	parseContext := &ParseContext{GlobalEnv: GLOBAL_ENV}
	replContext := NewReplContext(parseContext.GlobalEnv)

	var runeReader io.RuneReader
	var rl *liner.State
	var historyFilename string
	if noReadline {
		runeReader = bufio.NewReader(Stdin)
	} else {
		home := HomeDir()
		jokerd := filepath.Join(home, ".jokerd")
		if _, err := os.Stat(jokerd); os.IsNotExist(err) {
			if err := os.MkdirAll(jokerd, 0777); err != nil {
				fmt.Fprintf(Stderr, "WARNING: could not create %s \n", jokerd)
			}
		}
		if !noReplHistory {
			historyFilename = filepath.Join(jokerd, ".repl_history")
		}
		rl = liner.NewLiner()
		OnExit(func() {
			saveReplHistory(rl, historyFilename)
			rl.Close()
		})
		defer rl.Close()
		rl.SetCtrlCAborts(true)
		rl.SetWordCompleter(completer)
		rl.SetTabCompletionStyle(liner.TabPrints)

		if !noReplHistory {
			if f, err := os.Open(historyFilename); err == nil {
				rl.ReadHistory(f)
				f.Close()
			}
		}

		runeReader = NewLineRuneReader(rl)

		for _, line := range strings.Split(string(dataRead), "\n") {
			if strings.TrimSpace(line) != "" {
				rl.AppendHistory(line)
			}
		}
		dataRead = []rune{}
	}

	reader := NewReader(runeReader, "<repl>")

	for {
		if noReadline {
			print(GLOBAL_ENV.CurrentNamespace().Name.ToString(false) + "=> ")
		} else {
			runeReader.(*LineRuneReader).Prompt = (GLOBAL_ENV.CurrentNamespace().Name.ToString(false) + "=> ")
		}
		if processReplCommand(reader, phase, parseContext, replContext) {
			saveReplHistory(rl, historyFilename)
			return
		}
	}
}
<|endoftext|>


<commit_before>// package rest contains functions for sending http requests to a REST API, which
// can be used to create, read, update, and delete models from a server.
//
// TODO: add a really detailed package doc comment describing:
//   - The methods and urls that are used for each function
//   - The format in which models are encoded and what field types are supported
//   - What responses from the server should look like
//   - What happens if there is a non-200 response status code
package rest

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strings"
)

// ContentType is a string which can be set as the Content-Type header and
// will affect the encoding of the body of requests.
type ContentType string

const (
	ContentJSON       ContentType = "application/json"
	ContentURLEncoded ContentType = "application/x-www-form-urlencoded"
)

// contentType determines which Content-Type header and body encoding to
// use and can be set with the SetContentType function.
var contentType = ContentURLEncoded

// SetContentType can be set to globally affect the Content-Type header and body
// encoding used by the rest package when sending requests. By default the value
// is ContentURLEncoded, which corresponds to the Content-Type header
// "application/x-www-form-urlencoded". To send requests encoded as JSON, you can
// set this to ContentJSON, which corresponds to the Content-Type header
// "application/json".
func SetContentType(c ContentType) {
	contentType = c
}

// Model must be satisfied by all models. Satisfying this interface allows you to
// use the helper methods which send http requests to a REST API. They are used
// for e.g., creating a new model or getting an existing model from the server.
// Because of the way reflection is used to encode the data, a Model must have an
// underlying type of either a struct or a pointer to a struct.
type Model interface {
	// ModelId returns a unique identifier for the model. It is used for determining
	// which URL to send a request to.
	ModelId() string
	// RootURL returns the url for the REST resource corresponding to this model.
	// Typically it should look something like "http://examle.com/todos". Note that
	// the trailing slash should not be included.
	RootURL() string
}

// Create sends an http request to create the given model. It uses reflection to
// convert the fields of model to url-encoded data. Then it sends a POST request to
// model.RootURL() with the encoded data in the body and the Content-Type header set
// to "application/x-www-form-urlencoded" by default, or to "application/json" if you
// called rest.SetContentType(rest.ContentJSON). It expects a JSON response containing
// the created object from the server if the request was successful, in which case it
// will mutate model by setting the fields to the values in the JSON response. Since
// model may be mutated, it should be a poitner.
func Create(model Model) error {
	fullURL := model.RootURL()
	encodedModelData, err := encodeFields(model)
	if err != nil {
		return err
	}
	return sendRequestAndUnmarshal("POST", fullURL, encodedModelData, model)
}

// Read sends an http request to read (or fetch) the model with the given id
// from the server. It sends a GET request to model.RootURL() + "/" + model.ModelId().
// Read expects a JSON response containing the data for the requested model if the
// request was successful, in which case it will mutate model by setting the fields
// to the values in the JSON response. Since model may be mutated, it should be
// a pointer.
func Read(id string, model Model) error {
	fullURL := model.RootURL() + "/" + id
	return sendRequestAndUnmarshal("GET", fullURL, "", model)
}

// ReadAll sends an http request to read (or fetch) all the models of a particular
// type from the server (e.g. get all the todos). It sends a GET request to
// model.RootURL(). ReadAll expects a JSON response containing an array of objects,
// where each object contains data for one model. models must be a pointer to a slice
// of some type which implements Model. ReadAll will mutate models by growing or shrinking
// the slice as needed, and by setting the fields of each element to the values in the JSON
// response.
func ReadAll(models interface{}) error {
	rootURL, err := getURLFromModels(models)
	if err != nil {
		return err
	}
	return sendRequestAndUnmarshal("GET", rootURL, "", models)
}

// Update sends an http request to update the given model, i.e. to change some or all
// of the fields. It uses reflection to convert the fields of model to url-encoded data.
// Then it sends a PUT request to model.RootURL() with the encoded data in the body and
// the Content-Type header set to "application/x-www-form-urlencoded" by default, or to
// "application/json" if you called rest.SetContentType(rest.ContentJSON). Update expects
// a JSON response containing the data for the updated model if the request was successful,
// in which case it will mutate model by setting the fields to the values in the JSON
// response. Since model may be mutated, it should be a pointer.
func Update(model Model) error {
	fullURL := model.RootURL() + "/" + model.ModelId()
	encodedModelData, err := encodeFields(model)
	if err != nil {
		return err
	}
	return sendRequestAndUnmarshal("PUT", fullURL, encodedModelData, model)
}

// Delete sends an http request to delete the given model. It sends a DELETE request
// to model.RootURL() + "/" + model.ModelId(). DELETE expects an empty JSON response
// if the request was successful, and it will not mutate model.
func Delete(model Model) error {
	fullURL := model.RootURL() + "/" + model.ModelId()
	req, err := http.NewRequest("DELETE", fullURL, nil)
	if err != nil {
		return fmt.Errorf("Something went wrong building DELETE request to %s: %s", fullURL, err.Error())
	}
	if _, err := http.DefaultClient.Do(req); err != nil {
		return fmt.Errorf("Something went wrong with DELETE request to %s: %s", fullURL, err.Error())
	}
	return nil
}

// getURLFromModels returns the url that should be used for the type that corresponds
// to models. It does this by instantiating a new model of the correct type and then
// calling RootURL on it. models should be a pointer to a slice of models.
func getURLFromModels(models interface{}) (string, error) {
	// Check the type of models
	typ := reflect.TypeOf(models)
	switch {
	// Make sure its a pointer
	case typ.Kind() != reflect.Ptr:
		return "", fmt.Errorf("models must be a pointer to a slice of models. %T is not a pointer.", models)
	// Make sure its a pointer to a slice
	case typ.Elem().Kind() != reflect.Slice:
		return "", fmt.Errorf("models must be a pointer to a slice of models. %T is not a pointer to a slice", models)
	// Make sure the type of the elements of the slice implement Model
	case !typ.Elem().Elem().Implements(reflect.TypeOf([]Model{}).Elem()):
		return "", fmt.Errorf("models must be a pointer to a slice of models. The elem type %s does not implement model", typ.Elem().Elem().String())
	}
	// modelType is the type of the elements of models
	modelType := typ.Elem().Elem()
	// Ultimately, we need to be able to instantiate a new object of a type that
	// implements Model so that we can call RootURL on it. The trouble is that
	// reflect.New only works for things that are not pointers, and the type of
	// the elements of models could be pointers. To solve for this, we are going
	// to get the Elem of modelType if it is a pointer and keep track of the number
	// of times we get the Elem. So if modelType is *Todo, we'll call Elem once to
	// get the type Todo.
	numDeref := 0
	for modelType.Kind() == reflect.Ptr {
		modelType = modelType.Elem()
		numDeref += 1
	}
	// Now that we have the underlying type that is not a pointer, we can instantiate
	// a new object with reflect.New.
	newModelVal := reflect.New(modelType).Elem()
	// Now we need to iteratively get the address of the object we created exactly
	// numDeref times to get to a type that implements Model. Note that Addr is the
	// inverse of Elem.
	for i := 0; i < numDeref; i++ {
		newModelVal = newModelVal.Addr()
	}
	// Now we can use a type assertion to convert the object we instantiated to a Model
	newModel := newModelVal.Interface().(Model)
	// Finally, once we have a Model we can get what we wanted by calling RootURL
	return newModel.RootURL(), nil
}

// sendRequestAndUnmarshal constructs a request with the given method, url, and
// data. If data is an empty string, it will construct a request without any
// data in the body. If data is a non-empty string, it will send it as the body
// of the request and set the Content-Type header depending on what contentType has
// been set to. Then sendRequestAndUnmarshal sends the request using http.DefaultClient
// and marshals the response into v using the json package.
// TODO: do something if the response status code is non-200.
func sendRequestAndUnmarshal(method string, url string, data string, v interface{}) error {
	// Build the request
	req, err := http.NewRequest(method, url, strings.NewReader(data))
	if err != nil {
		return fmt.Errorf("Something went wrong building %s request to %s: %s", method, url, err.Error())
	}
	// Set the Content-Type header only if data was provided
	if data != "" {
		req.Header.Set("Content-Type", string(contentType))
	}
	// Specify that we want json as the response type. This is especially useful
	// for applications which share things between client and server
	req.Header.Set("Accept", "application/json")
	// Send the request using the default client
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return fmt.Errorf("Something went wrong with %s request to %s: %s", req.Method, req.URL.String(), err.Error())
	}
	// Unmarshal the response into v
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return fmt.Errorf("Couldn't read response to %s: %s", res.Request.URL.String(), err.Error())
	}
	return json.Unmarshal(body, v)
}

// encodeFields encodes the fields using either json encoding or url encoding, depending
// on the value of contentType.
func encodeFields(model Model) (string, error) {
	switch contentType {
	case ContentURLEncoded:
		return urlEncodeFields(model)
	case ContentJSON:
		data, err := json.Marshal(model)
		return string(data), err
	default:
		return "", fmt.Errorf("rest: don't know how to handle ContentType: %s", contentType)
	}
}

// urlEncodeFields returns the fields of model represented as a url-encoded string.
// Suitable for POST requests with a content type of application/x-www-form-urlencoded.
// It returns an error if model is a nil pointer or if it is not a struct or a pointer
// to a struct. Any fields that are nil will not be added to the url-encoded string.
func urlEncodeFields(model Model) (string, error) {
	modelVal := reflect.ValueOf(model)
	// dereference the pointer until we reach the underlying struct value.
	for modelVal.Kind() == reflect.Ptr {
		if modelVal.IsNil() {
			return "", errors.New("Error encoding model as url-encoded data: model was a nil pointer.")
		}
		modelVal = modelVal.Elem()
	}
	// Make sure the type of model after dereferencing is a struct.
	if modelVal.Kind() != reflect.Struct {
		return "", fmt.Errorf("Error encoding model as url-encoded data: model must be a struct or a pointer to a struct.")
	}
	encodedFields := []string{}
	for i := 0; i < modelVal.Type().NumField(); i++ {
		field := modelVal.Type().Field(i)
		fieldValue := modelVal.FieldByName(field.Name)
		encodedField, err := urlEncodeField(field, fieldValue)
		if err != nil {
			if _, ok := err.(nilFieldError); ok {
				// If there was a nil field, continue without adding the field
				// to the encoded data.
				continue
			}
			// We should return any other kind of error
			return "", err
		}
		encodedFields = append(encodedFields, field.Name+"="+encodedField)
	}
	return strings.Join(encodedFields, "&"), nil
}

type nilFieldError struct{}

func (nilFieldError) Error() string {
	return "field was nil"
}

// urlEncodeField converts a field with the given value to a string. It returns an error
// if field has a type which is unsupported. It returns a special error (nilFieldError)
// if a field has a value of nil. The supported types are int and its variants (int64,
// int32, etc.), uint and its variants (uint64, uint32, etc.), bool, string, and []byte.
func urlEncodeField(field reflect.StructField, value reflect.Value) (string, error) {
	for value.Kind() == reflect.Ptr {
		if value.IsNil() {
			// Skip nil fields
			return "", nilFieldError{}
		}
		value = value.Elem()
	}
	switch v := value.Interface().(type) {
	case int, int64, int32, int16, int8, uint, uint64, uint32, uint16, uint8, bool:
		return fmt.Sprint(v), nil
	case string:
		return url.QueryEscape(v), nil
	case []byte:
		return url.QueryEscape(string(v)), nil
	default:
		return "", fmt.Errorf("Error encoding model as url-encoded data: Don't know how to convert %v of type %T to a string.", v, v)
	}
}
<commit_msg>Make nilFieldError a var instead of a type<commit_after>// package rest contains functions for sending http requests to a REST API, which
// can be used to create, read, update, and delete models from a server.
//
// TODO: add a really detailed package doc comment describing:
//   - The methods and urls that are used for each function
//   - The format in which models are encoded and what field types are supported
//   - What responses from the server should look like
//   - What happens if there is a non-200 response status code
package rest

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strings"
)

// ContentType is a string which can be set as the Content-Type header and
// will affect the encoding of the body of requests.
type ContentType string

const (
	ContentJSON       ContentType = "application/json"
	ContentURLEncoded ContentType = "application/x-www-form-urlencoded"
)

// contentType determines which Content-Type header and body encoding to
// use and can be set with the SetContentType function.
var contentType = ContentURLEncoded

// SetContentType can be set to globally affect the Content-Type header and body
// encoding used by the rest package when sending requests. By default the value
// is ContentURLEncoded, which corresponds to the Content-Type header
// "application/x-www-form-urlencoded". To send requests encoded as JSON, you can
// set this to ContentJSON, which corresponds to the Content-Type header
// "application/json".
func SetContentType(c ContentType) {
	contentType = c
}

// Model must be satisfied by all models. Satisfying this interface allows you to
// use the helper methods which send http requests to a REST API. They are used
// for e.g., creating a new model or getting an existing model from the server.
// Because of the way reflection is used to encode the data, a Model must have an
// underlying type of either a struct or a pointer to a struct.
type Model interface {
	// ModelId returns a unique identifier for the model. It is used for determining
	// which URL to send a request to.
	ModelId() string
	// RootURL returns the url for the REST resource corresponding to this model.
	// Typically it should look something like "http://examle.com/todos". Note that
	// the trailing slash should not be included.
	RootURL() string
}

// Create sends an http request to create the given model. It uses reflection to
// convert the fields of model to url-encoded data. Then it sends a POST request to
// model.RootURL() with the encoded data in the body and the Content-Type header set
// to "application/x-www-form-urlencoded" by default, or to "application/json" if you
// called rest.SetContentType(rest.ContentJSON). It expects a JSON response containing
// the created object from the server if the request was successful, in which case it
// will mutate model by setting the fields to the values in the JSON response. Since
// model may be mutated, it should be a poitner.
func Create(model Model) error {
	fullURL := model.RootURL()
	encodedModelData, err := encodeFields(model)
	if err != nil {
		return err
	}
	return sendRequestAndUnmarshal("POST", fullURL, encodedModelData, model)
}

// Read sends an http request to read (or fetch) the model with the given id
// from the server. It sends a GET request to model.RootURL() + "/" + model.ModelId().
// Read expects a JSON response containing the data for the requested model if the
// request was successful, in which case it will mutate model by setting the fields
// to the values in the JSON response. Since model may be mutated, it should be
// a pointer.
func Read(id string, model Model) error {
	fullURL := model.RootURL() + "/" + id
	return sendRequestAndUnmarshal("GET", fullURL, "", model)
}

// ReadAll sends an http request to read (or fetch) all the models of a particular
// type from the server (e.g. get all the todos). It sends a GET request to
// model.RootURL(). ReadAll expects a JSON response containing an array of objects,
// where each object contains data for one model. models must be a pointer to a slice
// of some type which implements Model. ReadAll will mutate models by growing or shrinking
// the slice as needed, and by setting the fields of each element to the values in the JSON
// response.
func ReadAll(models interface{}) error {
	rootURL, err := getURLFromModels(models)
	if err != nil {
		return err
	}
	return sendRequestAndUnmarshal("GET", rootURL, "", models)
}

// Update sends an http request to update the given model, i.e. to change some or all
// of the fields. It uses reflection to convert the fields of model to url-encoded data.
// Then it sends a PUT request to model.RootURL() with the encoded data in the body and
// the Content-Type header set to "application/x-www-form-urlencoded" by default, or to
// "application/json" if you called rest.SetContentType(rest.ContentJSON). Update expects
// a JSON response containing the data for the updated model if the request was successful,
// in which case it will mutate model by setting the fields to the values in the JSON
// response. Since model may be mutated, it should be a pointer.
func Update(model Model) error {
	fullURL := model.RootURL() + "/" + model.ModelId()
	encodedModelData, err := encodeFields(model)
	if err != nil {
		return err
	}
	return sendRequestAndUnmarshal("PUT", fullURL, encodedModelData, model)
}

// Delete sends an http request to delete the given model. It sends a DELETE request
// to model.RootURL() + "/" + model.ModelId(). DELETE expects an empty JSON response
// if the request was successful, and it will not mutate model.
func Delete(model Model) error {
	fullURL := model.RootURL() + "/" + model.ModelId()
	req, err := http.NewRequest("DELETE", fullURL, nil)
	if err != nil {
		return fmt.Errorf("Something went wrong building DELETE request to %s: %s", fullURL, err.Error())
	}
	if _, err := http.DefaultClient.Do(req); err != nil {
		return fmt.Errorf("Something went wrong with DELETE request to %s: %s", fullURL, err.Error())
	}
	return nil
}

// getURLFromModels returns the url that should be used for the type that corresponds
// to models. It does this by instantiating a new model of the correct type and then
// calling RootURL on it. models should be a pointer to a slice of models.
func getURLFromModels(models interface{}) (string, error) {
	// Check the type of models
	typ := reflect.TypeOf(models)
	switch {
	// Make sure its a pointer
	case typ.Kind() != reflect.Ptr:
		return "", fmt.Errorf("models must be a pointer to a slice of models. %T is not a pointer.", models)
	// Make sure its a pointer to a slice
	case typ.Elem().Kind() != reflect.Slice:
		return "", fmt.Errorf("models must be a pointer to a slice of models. %T is not a pointer to a slice", models)
	// Make sure the type of the elements of the slice implement Model
	case !typ.Elem().Elem().Implements(reflect.TypeOf([]Model{}).Elem()):
		return "", fmt.Errorf("models must be a pointer to a slice of models. The elem type %s does not implement model", typ.Elem().Elem().String())
	}
	// modelType is the type of the elements of models
	modelType := typ.Elem().Elem()
	// Ultimately, we need to be able to instantiate a new object of a type that
	// implements Model so that we can call RootURL on it. The trouble is that
	// reflect.New only works for things that are not pointers, and the type of
	// the elements of models could be pointers. To solve for this, we are going
	// to get the Elem of modelType if it is a pointer and keep track of the number
	// of times we get the Elem. So if modelType is *Todo, we'll call Elem once to
	// get the type Todo.
	numDeref := 0
	for modelType.Kind() == reflect.Ptr {
		modelType = modelType.Elem()
		numDeref += 1
	}
	// Now that we have the underlying type that is not a pointer, we can instantiate
	// a new object with reflect.New.
	newModelVal := reflect.New(modelType).Elem()
	// Now we need to iteratively get the address of the object we created exactly
	// numDeref times to get to a type that implements Model. Note that Addr is the
	// inverse of Elem.
	for i := 0; i < numDeref; i++ {
		newModelVal = newModelVal.Addr()
	}
	// Now we can use a type assertion to convert the object we instantiated to a Model
	newModel := newModelVal.Interface().(Model)
	// Finally, once we have a Model we can get what we wanted by calling RootURL
	return newModel.RootURL(), nil
}

// sendRequestAndUnmarshal constructs a request with the given method, url, and
// data. If data is an empty string, it will construct a request without any
// data in the body. If data is a non-empty string, it will send it as the body
// of the request and set the Content-Type header depending on what contentType has
// been set to. Then sendRequestAndUnmarshal sends the request using http.DefaultClient
// and marshals the response into v using the json package.
// TODO: do something if the response status code is non-200.
func sendRequestAndUnmarshal(method string, url string, data string, v interface{}) error {
	// Build the request
	req, err := http.NewRequest(method, url, strings.NewReader(data))
	if err != nil {
		return fmt.Errorf("Something went wrong building %s request to %s: %s", method, url, err.Error())
	}
	// Set the Content-Type header only if data was provided
	if data != "" {
		req.Header.Set("Content-Type", string(contentType))
	}
	// Specify that we want json as the response type. This is especially useful
	// for applications which share things between client and server
	req.Header.Set("Accept", "application/json")
	// Send the request using the default client
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return fmt.Errorf("Something went wrong with %s request to %s: %s", req.Method, req.URL.String(), err.Error())
	}
	// Unmarshal the response into v
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return fmt.Errorf("Couldn't read response to %s: %s", res.Request.URL.String(), err.Error())
	}
	return json.Unmarshal(body, v)
}

// encodeFields encodes the fields using either json encoding or url encoding, depending
// on the value of contentType.
func encodeFields(model Model) (string, error) {
	switch contentType {
	case ContentURLEncoded:
		return urlEncodeFields(model)
	case ContentJSON:
		data, err := json.Marshal(model)
		return string(data), err
	default:
		return "", fmt.Errorf("rest: don't know how to handle ContentType: %s", contentType)
	}
}

// urlEncodeFields returns the fields of model represented as a url-encoded string.
// Suitable for POST requests with a content type of application/x-www-form-urlencoded.
// It returns an error if model is a nil pointer or if it is not a struct or a pointer
// to a struct. Any fields that are nil will not be added to the url-encoded string.
func urlEncodeFields(model Model) (string, error) {
	modelVal := reflect.ValueOf(model)
	// dereference the pointer until we reach the underlying struct value.
	for modelVal.Kind() == reflect.Ptr {
		if modelVal.IsNil() {
			return "", errors.New("Error encoding model as url-encoded data: model was a nil pointer.")
		}
		modelVal = modelVal.Elem()
	}
	// Make sure the type of model after dereferencing is a struct.
	if modelVal.Kind() != reflect.Struct {
		return "", fmt.Errorf("Error encoding model as url-encoded data: model must be a struct or a pointer to a struct.")
	}
	encodedFields := []string{}
	for i := 0; i < modelVal.Type().NumField(); i++ {
		field := modelVal.Type().Field(i)
		fieldValue := modelVal.FieldByName(field.Name)
		encodedField, err := urlEncodeField(field, fieldValue)
		if err != nil {
			if err == nilFieldError {
				// If there was a nil field, continue without adding the field
				// to the encoded data.
				continue
			}
			// We should return any other kind of error
			return "", err
		}
		encodedFields = append(encodedFields, field.Name+"="+encodedField)
	}
	return strings.Join(encodedFields, "&"), nil
}

var nilFieldError = errors.New("field was nil")

// urlEncodeField converts a field with the given value to a string. It returns an error
// if field has a type which is unsupported. It returns a special error (nilFieldError)
// if a field has a value of nil. The supported types are int and its variants (int64,
// int32, etc.), uint and its variants (uint64, uint32, etc.), bool, string, and []byte.
func urlEncodeField(field reflect.StructField, value reflect.Value) (string, error) {
	for value.Kind() == reflect.Ptr {
		if value.IsNil() {
			// Skip nil fields
			return "", nilFieldError
		}
		value = value.Elem()
	}
	switch v := value.Interface().(type) {
	case int, int64, int32, int16, int8, uint, uint64, uint32, uint16, uint8, bool:
		return fmt.Sprint(v), nil
	case string:
		return url.QueryEscape(v), nil
	case []byte:
		return url.QueryEscape(string(v)), nil
	default:
		return "", fmt.Errorf("Error encoding model as url-encoded data: Don't know how to convert %v of type %T to a string.", v, v)
	}
}
<|endoftext|>


<commit_before>/*
Package rest is a RESTful web-service framework. It make struct method to http.Hander automatically.

Define a service struct like this:

	type RESTService struct {
		Service `prefix:"/prefix"`

		Hello    Processor `path:"/hello/(.*?)/to/(.*?)" method:"GET"`
		PostConv Processor `path:"/conversation" func:"PostConversation" method:"POST"`
		Conv     Processor `path:"/conversation/([0-9]+)" func:"GetConversation" method:"GET"`
	}

	func (s RESTService) Hello_(host, guest string) string {
		return "hello from " + host + " to " + guest
	}

	func (s RESTService) PostConversation(post string) string {
		path, _ := s.Conv.Path(1)
		s.RedirectTo(path)
		return "just post: " + post
	}

	func (s RESTService) GetConversation(id int) string {
		return fmt.Sprintf("get post id %d", id)
	}

The field tag of RESTService configure the parameters of processor, like method, path, or function which
will process the request.

The path of processor can capture arguments, which will pass to process function by order in path. Arguments
type can be string or int, or any type which kind is string or int.

The default name of processor is the name of field postfix with "_", like Hello processor correspond Hello_ method.

Get the http.Handler from RESTService:

	handler, err := rest.New(new(RESTService))
	http.ListenAndServe("127.0.0.1:8080", handler)

Or use gorilla mux and work with other http handlers:

	// import "github.com/gorilla/mux"
	router := mux.NewRouter()
	handler, err := rest.New(new(RESTService))
	router.PathPrefix(handler.Prefix()).Handle(handler)
*/
package rest

import (
	"fmt"
	"net/http"
	"reflect"
)

// Rest handle the http request and call to correspond the handler(processor or streaming).
type Rest struct {
	prefix         string
	defaultCharset string
	defaultMime    string

	instance reflect.Value
	handlers []*node
}

// Create Rest instance from service instance
func New(i interface{}) (*Rest, error) {
	instance := reflect.ValueOf(i)
	if instance.Kind() != reflect.Struct && instance.Kind() != reflect.Ptr {
		return nil, fmt.Errorf("%s's kind must struct or point to struct")
	}
	if instance.Kind() == reflect.Ptr {
		instance = instance.Elem()
	}
	t := instance.Type()
	serviceType, ok := t.FieldByName("Service")
	if !ok {
		return nil, fmt.Errorf("can't find restful.Service field")
	}
	if serviceType.Index[0] != 0 {
		return nil, fmt.Errorf("%s's 1st field must be restful.Service", t.Name())
	}

	serviceTag := serviceType.Tag
	service := instance.Field(0)
	prefix, mime, charset, err := initService(service, serviceTag)
	if err != nil {
		return nil, err
	}

	var handlers []*node
	for i, n := 0, instance.NumField(); i < n; i++ {
		handlerType := t.Field(i)
		if capital := handlerType.Name[0]; !('A' <= capital && capital <= 'Z') {
			continue
		}
		handler := instance.Field(i)
		if _, ok := handler.Interface().(nodeInterface); !ok {
			continue
		}

		node, err := newNode(t, prefix, handler, handlerType)
		if err != nil {
			return nil, err
		}
		handlers = append(handlers, node)
	}

	return &Rest{
		prefix:         prefix,
		defaultMime:    mime,
		defaultCharset: charset,
		handlers:       handlers,
		instance:       instance,
	}, nil
}

// Get the prefix of service.
func (s Rest) Prefix() string {
	return s.prefix
}

// Serve the http request.
func (s Rest) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var err error
	var errorCode int
	defer func() {
		r := recover()
		if r != nil {
			errorCode = http.StatusInternalServerError
			err = fmt.Errorf("panic: %v", r)
		}
		if err != nil {
			http.Error(w, err.Error(), errorCode)
		}
	}()

	node := s.findNode(r)
	if node == nil {
		errorCode, err = http.StatusNotFound, fmt.Errorf("can't find node to process %s", r.URL.Path)
		return
	}

	args, e := node.match(r.Method, r.URL.Path)
	if e != nil {
		errorCode, err = http.StatusNotFound, e
		return
	}

	ctx, e := newContent(w, r, s.defaultMime, s.defaultCharset)
	if err != nil {
		errorCode, err = http.StatusBadRequest, e
		return
	}

	if req := node.request; req != nil {
		request := reflect.New(req)
		err = ctx.marshaller.Unmarshal(r.Body, request.Interface())
		if err != nil {
			errorCode, err = http.StatusBadRequest, fmt.Errorf("can't marshal request to type %s: %s", req, err)
			return
		}
		args = append(args, request.Elem())
	}

	service := s.instance.Field(0).Interface().(Service)
	service.ctx = ctx

	node.handle(s.instance, service.ctx, args)
}

func (s Rest) findNode(r *http.Request) *node {
	for _, h := range s.handlers {
		if h.method != r.Method {
			continue
		}
		if h.path.MatchString(r.URL.Path) {
			return h
		}
	}
	return nil
}
<commit_msg>update doc<commit_after>/*
Package rest is a RESTful web-service framework. It make struct method to http.Hander automatically.

Define a service struct like this:

	type RESTService struct {
		Service `prefix:"/prefix"`

		Hello    Processor `path:"/hello/(.*?)/to/(.*?)" method:"GET"`
		PostConv Processor `path:"/conversation/to/(.*?)" func:"PostConversation" method:"POST"`
		Conv     Processor `path:"/conversation/([0-9]+)" func:"GetConversation" method:"GET"`
		Update   Streaming `path:"/conversation/streaming" method:"GET"`
	}

	// call /hello/{host}/to/{guest} and get a string.
	func (s RESTService) Hello_(host, guest string) string {
		return "hello from " + host + " to " + guest
	}

	// call /conversation/to/{people}, post a string as text and return a conversation object.
	// the post content will unmarshal to the last parameter of processor.
	// when save the conversation to db, send new conv to people through streaming api.
	func (s RESTService) PostConversation(people, post string) Conversation {
		path, _ := s.Conv.Path(1)
		s.RedirectTo(path)
		conv := Conversation{
			Id: 1,
			To: people,
			Text: post,
		}
		// save conv to db
		s.Update.Feed(people)
		return conv
	}

	// call /conversation/{id} and get the conversation object.
	// rest will automatically convert id in url to int type. if convert failed, return bad request.
	func (s RESTService) GetConversation(id int) Conversation {
		conv := // load conversation with id
		return conv
	}

	// call /conversation/streaming, create a long connection and get the conversation update ASAP.
	// this function will be called when connecting to get a identity from request.
	// when feeding streaming, all connection with same identity will send the data.
	func (s RESTService) Update_() string {
		to := s.Request().URL.Query().Get("user")
		if to == "" {
			s.Error(http.StatusBadRequest, fmt.Errorf("need user."))
		}
		return to
	}

The field tag of RESTService configure the parameters of processor, like method, path, or function which
will process the request.

The path of processor can capture arguments, which will pass to process function by order in path. Arguments
type can be string or int, or any type which kind is string or int.

The default name of processor is the name of field postfix with "_", like Hello processor correspond Hello_ method.

Get the http.Handler from RESTService:

	handler, err := rest.New(new(RESTService))
	http.ListenAndServe("127.0.0.1:8080", handler)

Or use gorilla mux and work with other http handlers:

	// import "github.com/gorilla/mux"
	router := mux.NewRouter()
	handler, err := rest.New(new(RESTService))
	router.PathPrefix(handler.Prefix()).Handle(handler)
*/
package rest

import (
	"fmt"
	"net/http"
	"reflect"
)

// Rest handle the http request and call to correspond the handler(processor or streaming).
type Rest struct {
	prefix         string
	defaultCharset string
	defaultMime    string

	instance reflect.Value
	handlers []*node
}

// Create Rest instance from service instance
func New(i interface{}) (*Rest, error) {
	instance := reflect.ValueOf(i)
	if instance.Kind() != reflect.Struct && instance.Kind() != reflect.Ptr {
		return nil, fmt.Errorf("%s's kind must struct or point to struct")
	}
	if instance.Kind() == reflect.Ptr {
		instance = instance.Elem()
	}
	t := instance.Type()
	serviceType, ok := t.FieldByName("Service")
	if !ok {
		return nil, fmt.Errorf("can't find restful.Service field")
	}
	if serviceType.Index[0] != 0 {
		return nil, fmt.Errorf("%s's 1st field must be restful.Service", t.Name())
	}

	serviceTag := serviceType.Tag
	service := instance.Field(0)
	prefix, mime, charset, err := initService(service, serviceTag)
	if err != nil {
		return nil, err
	}

	var handlers []*node
	for i, n := 0, instance.NumField(); i < n; i++ {
		handlerType := t.Field(i)
		if capital := handlerType.Name[0]; !('A' <= capital && capital <= 'Z') {
			continue
		}
		handler := instance.Field(i)
		if _, ok := handler.Interface().(nodeInterface); !ok {
			continue
		}

		node, err := newNode(t, prefix, handler, handlerType)
		if err != nil {
			return nil, err
		}
		handlers = append(handlers, node)
	}

	return &Rest{
		prefix:         prefix,
		defaultMime:    mime,
		defaultCharset: charset,
		handlers:       handlers,
		instance:       instance,
	}, nil
}

// Get the prefix of service.
func (s Rest) Prefix() string {
	return s.prefix
}

// Serve the http request.
func (s Rest) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	var err error
	var errorCode int
	defer func() {
		r := recover()
		if r != nil {
			errorCode = http.StatusInternalServerError
			err = fmt.Errorf("panic: %v", r)
		}
		if err != nil {
			http.Error(w, err.Error(), errorCode)
		}
	}()

	node := s.findNode(r)
	if node == nil {
		errorCode, err = http.StatusNotFound, fmt.Errorf("can't find node to process %s", r.URL.Path)
		return
	}

	args, e := node.match(r.Method, r.URL.Path)
	if e != nil {
		errorCode, err = http.StatusNotFound, e
		return
	}

	ctx, e := newContent(w, r, s.defaultMime, s.defaultCharset)
	if err != nil {
		errorCode, err = http.StatusBadRequest, e
		return
	}

	if req := node.request; req != nil {
		request := reflect.New(req)
		err = ctx.marshaller.Unmarshal(r.Body, request.Interface())
		if err != nil {
			errorCode, err = http.StatusBadRequest, fmt.Errorf("can't marshal request to type %s: %s", req, err)
			return
		}
		args = append(args, request.Elem())
	}

	service := s.instance.Field(0).Interface().(Service)
	service.ctx = ctx

	node.handle(s.instance, service.ctx, args)
}

func (s Rest) findNode(r *http.Request) *node {
	for _, h := range s.handlers {
		if h.method != r.Method {
			continue
		}
		if h.path.MatchString(r.URL.Path) {
			return h
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package ring

// Ring stores the assignments of replicas of partitions to node indexes;
// Ring[replica][partition] = node index.
//
// All Ring[replica] slices will be the same length.
//
// A negative NodeIndexType value indicates no node assignment.
//
// Usually these are generated and maintained by the Builder.
type Ring [][]NodeIndexType

// ReplicaCount is a convenience method for len(r).
func (r Ring) ReplicaCount() int {
	return len(r)
}

// PartitionCount is a convenience method for len(r[0]) if len(r) > 0 else 0.
func (r Ring) PartitionCount() int {
	if len(r) > 0 {
		return len(r[0])
	}
	return 0
}

// RingEqual is true if the size and all the assignments of the given ring are the
// same as this ring.
//
// Note: This only can compare the node index value assignments. If the indexes
// point to different node information, that is outside the Ring itself. For
// example, it is possible you could remove a node from a builder, add another
// node, rebalance, and end up with the same index values assigned.
func (r Ring) RingEqual(r2 Ring) bool {
	if len(r) != len(r2) {
		return false
	}
	for replica := range r {
		partitionToNodeIndex := r[replica]
		partitionToNodeIndex2 := r2[replica]
		if len(partitionToNodeIndex) != len(partitionToNodeIndex2) {
			return false
		}
		for partition := range partitionToNodeIndex {
			if partitionToNodeIndex[partition] != partitionToNodeIndex2[partition] {
				return false
			}
		}
	}
	return true
}

// RingDuplicate returns a duplicate of the ring; a deep copy of all the
// replica assignments.
func (r Ring) RingDuplicate() Ring {
	r2 := make(Ring, len(r))
	for replica := range r {
		r2[replica] = make([]NodeIndexType, len(r[replica]))
		copy(r2[replica], r[replica])
	}
	return r2
}
<commit_msg>Correct docs due to previous change<commit_after>package ring

// Ring stores the assignments of replicas of partitions to node indexes;
// Ring[replica][partition] = node index.
//
// All Ring[replica] slices will be the same length.
//
// A NodeIndexNil value indicates no node assignment.
//
// Usually these are generated and maintained by the Builder.
type Ring [][]NodeIndexType

// ReplicaCount is a convenience method for len(r).
func (r Ring) ReplicaCount() int {
	return len(r)
}

// PartitionCount is a convenience method for len(r[0]) if len(r) > 0 else 0.
func (r Ring) PartitionCount() int {
	if len(r) > 0 {
		return len(r[0])
	}
	return 0
}

// RingEqual is true if the size and all the assignments of the given ring are the
// same as this ring.
//
// Note: This only can compare the node index value assignments. If the indexes
// point to different node information, that is outside the Ring itself. For
// example, it is possible you could remove a node from a builder, add another
// node, rebalance, and end up with the same index values assigned.
func (r Ring) RingEqual(r2 Ring) bool {
	if len(r) != len(r2) {
		return false
	}
	for replica := range r {
		partitionToNodeIndex := r[replica]
		partitionToNodeIndex2 := r2[replica]
		if len(partitionToNodeIndex) != len(partitionToNodeIndex2) {
			return false
		}
		for partition := range partitionToNodeIndex {
			if partitionToNodeIndex[partition] != partitionToNodeIndex2[partition] {
				return false
			}
		}
	}
	return true
}

// RingDuplicate returns a duplicate of the ring; a deep copy of all the
// replica assignments.
func (r Ring) RingDuplicate() Ring {
	r2 := make(Ring, len(r))
	for replica := range r {
		r2[replica] = make([]NodeIndexType, len(r[replica]))
		copy(r2[replica], r[replica])
	}
	return r2
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Pani Networks
// All Rights Reserved.
//
// 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.

package rlog

import (
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
)

const notATrace = -1

// The known log levels
const (
	levelNone = iota
	levelCrit
	levelErr
	levelWarn
	levelInfo
	levelDebug
	levelTrace
)

// Translation map from level to string representation
var levelStrings = map[int]string{
	levelTrace: "TRACE",
	levelDebug: "DEBUG",
	levelInfo:  "INFO",
	levelWarn:  "WARN",
	levelErr:   "ERROR",
	levelCrit:  "CRITICAL",
	levelNone:  "NONE",
}

// Translation from level string to number.
var levelNumbers = map[string]int{
	"TRACE":    levelTrace,
	"DEBUG":    levelDebug,
	"INFO":     levelInfo,
	"WARN":     levelWarn,
	"ERROR":    levelErr,
	"CRITICAL": levelCrit,
	"NONE":     levelNone,
}

// FilterSpec holds a list of filters. These are applied to the 'caller'
// information of a log message (calling module and file) to see if this
// message should be logged. Different log or trace levels per file can
// therefore be maintained. For log messages this is the log level, for trace
// messages this is going to be the trace level.
type FilterSpec struct {
	Filters []Filter
}

// FromString initializes FilterSpec from string.
//
// Use the isTraceLevel flag to indicate whether the levels are numeric (for
// trace messages) or are level strings (for log messages).
//
// Format "<filter>,<filter>,[<filter>]..."
//     filter:
//       <pattern=level> | <level>
//     pattern:
//       shell glob to match caller file name
//     level:
//       log or trace level of the logs to enable in matched files.
//
//     Example:
//     - "RLOG_TRACE_LEVEL=3"
//       Just a global trace level of 3 for all files and modules.
//     - "RLOG_TRACE_LEVEL=client.go=1,ip*=5,3"
//       This enables trace level 1 in client.go, level 5 in all files whose
//       names start with 'ip', and level 3 for everyone else.
//     - "RLOG_LOG_LEVEL=DEBUG"
//       Global log level DEBUG for all files and modules.
//     - "RLOG_LOG_LEVEL=client.go=ERROR,INFO,ip*=WARN"
//       ERROR and higher for client.go, WARN or higher for all files whose
//       name starts with 'ip', INFO for everyone else.
func (spec *FilterSpec) FromString(s string, isTraceLevels bool, globalLevelDefault int) {
	var globalLevel int = globalLevelDefault
	var levelToken string
	var matchToken string

	fields := strings.Split(s, ",")

	for _, f := range fields {
		var filterLevel int
		var err error
		var ok bool

		// Tokens should contain two elements: The filename and the trace
		// level. If there is only one token then we have to assume that this
		// is the 'global' filter (without filename component).
		tokens := strings.Split(f, "=")
		if len(tokens) == 1 {
			// Global level. We'll store this one for the end, since it needs
			// to sit last in the list of filters (during evaluation in gets
			// checked last).
			matchToken = ""
			levelToken = tokens[0]
		} else if len(tokens) == 2 {
			matchToken = tokens[0]
			levelToken = tokens[1]
		} else {
			// Skip anything else that's malformed
			continue
		}
		if isTraceLevels {
			// The level token should contain a numeric value
			if filterLevel, err = strconv.Atoi(levelToken); err != nil {
				continue
			}
		} else {
			// The level token should contain the name of a log level
			levelToken = strings.ToUpper(levelToken)
			filterLevel, ok = levelNumbers[levelToken]
			if !ok || filterLevel == levelTrace {
				// User not allowed to set trace log levels, so if that or
				// not a known log level then this specification will be
				// ignored.
				continue
			}

		}

		if matchToken == "" {
			// Global level just remembered for now, not yet added
			globalLevel = filterLevel
		} else {
			spec.Filters = append(spec.Filters, Filter{matchToken, filterLevel})
		}
	}

	// Now add the global level, so that later it will be evaluated last.
	spec.Filters = append(spec.Filters, Filter{"", globalLevel})

	return
}

// matchFilters checks if given filename and trace level are accepted
// by any of the filters
func (spec *FilterSpec) matchFilters(filename string, level int) bool {

	// If there are no filters then we don't match anything.
	if len(spec.Filters) == 0 {
		return false
	}

	// If at least one filter matches.
	for _, filter := range spec.Filters {
		if matched, loggit := filter.match(filename, level); matched {
			return loggit
		}
	}

	return false
}

// Filter holds filename and level to match logs against log messages.
type Filter struct {
	Pattern string
	Level   int
}

// match checks if given filename and level are matched by
// this filter. Returns two bools: One to indicate whether a filename match was
// made, and the second to indicate whether the message should be logged
// (matched the level).
func (f Filter) match(filename string, level int) (bool, bool) {
	var match bool
	if f.Pattern != "" {
		match, _ = filepath.Match(f.Pattern, filepath.Base(filename))
	} else {
		match = true
	}
	if match {
		return true, level <= f.Level
	}

	return false, false
}

// Rlog is controlled via environment variables. Those things won't change on
// us. Therefore, we can look them up once and store them in module level
// global variables.
var settingShowCallerInfo bool = false // whether we log info about calling function
var settingDateTimeFlags int           // flags for date/time output
var settingLogFile string = ""         // logfile name

var logWriter *log.Logger             // the writer to which output is sent
var logFilterSpec = new(FilterSpec)   // filters for log messages
var traceFilterSpec = new(FilterSpec) // filters for trace messages

// init extracts settings for our logger from environment variables when the
// module is imported.
func init() {
	logLevelEnv := os.Getenv("RLOG_LOG_LEVEL")
	callerInfoEnv := os.Getenv("RLOG_CALLER_INFO")
	traceLevelEnv := os.Getenv("RLOG_TRACE_LEVEL")
	dontLogTimeEnv := os.Getenv("RLOG_LOG_NOTIME")
	logFileEnv := os.Getenv("RLOG_LOG_FILE")

	// Evaluating the caller info variable.
	settingShowCallerInfo = isTrueBoolString(callerInfoEnv)

	// Evaluating whether date/time should be logged with each message
	// By default (if flag is not set) we want to log date and time.
	logTimeDate := !isTrueBoolString(dontLogTimeEnv)

	// Initialize filters for trace and log levels.
	traceFilterSpec.FromString(traceLevelEnv, true, -1)
	logFilterSpec.FromString(logLevelEnv, false, levelInfo)

	// By default we log to stderr...
	logWriter := os.Stderr

	// ... but if requested we'll create and/or append to a logfile instead
	if logFileEnv != "" {
		newLogFile, err := os.OpenFile(logFileEnv, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
		if err == nil {
			logWriter = newLogFile
		}
	}

	// Initialize the logger we will use throughout
	settingDateTimeFlags = 0
	if logTimeDate {
		// Store the flags that enable date/time logging, since that's
		// controlled by an environment variable. Any new loggers created at
		// runtime should inherit the same setting (the same flags).
		settingDateTimeFlags = log.Ldate | log.Ltime
	}
	SetNewLogWriter(logWriter)
}

// SetNewLogWriter re-wires the log output to a new io.Writer. By default rlog
// logs to os.Stderr, but this function can be used to direct the output
// somewhere else.
func SetNewLogWriter(writer io.Writer) {
	// Use the stored date/time flag settings
	logWriter = log.New(writer, "", settingDateTimeFlags)
}

// isTrueBoolString tests a string to see if it represents a 'true' value.
// The ParseBool function unfortunately doesn't recognize 'y' or 'yes', which
// is why we added that test here as well.
func isTrueBoolString(str string) bool {
	str = strings.ToUpper(str)
	if str == "Y" || str == "YES" {
		return true
	}
	if isTrue, err := strconv.ParseBool(str); err == nil && isTrue {
		return true
	}
	return false
}

// basicLog is called by all the 'level' log functions.
// It checks what is configured to be included in the log message,
// decorates it accordingly and assembles the entire line. It then
// uses the standard log package to finally output the message.
func basicLog(logLevel int, traceLevel int, format string, prefixAddition string, a ...interface{}) {
	// Extract information about the caller of the log function, if requested.
	var callingFuncName string = ""
	var moduleAndFileName string = ""
	pc, fullFilePath, line, ok := runtime.Caller(2)
	if ok {
		callingFuncName = runtime.FuncForPC(pc).Name()
		// We only want to print or examine file and package name, so use the
		// last two elements of the full path. The path package deals with
		// different path formats on different systems, so we use that instead
		// of just string-split.
		dirPath, fileName := path.Split(fullFilePath)
		var moduleName string = ""
		if dirPath != "" {
			dirPath = dirPath[:len(dirPath)-1]
			dirPath, moduleName = path.Split(dirPath)
		}
		moduleAndFileName = moduleName + "/" + fileName
	}

	// Perform tests to see if we should log this message.
	var allowLog bool
	if traceLevel == notATrace {
		if logFilterSpec.matchFilters(moduleAndFileName, logLevel) {
			allowLog = true
		}
	} else {
		if traceFilterSpec.matchFilters(moduleAndFileName, traceLevel) {
			allowLog = true
		}
	}
	if !allowLog {
		return
	}

	callerInfo := ""
	if settingShowCallerInfo {
		callerInfo = fmt.Sprintf("[%s:%d (%s)] ", moduleAndFileName,
			line, callingFuncName)
	}

	// Assemble the actual log line
	var msg string
	if format != "" {
		msg = fmt.Sprintf(format, a...)
	} else {
		msg = fmt.Sprintln(a...)
	}
	levelDecoration := levelStrings[logLevel] + prefixAddition
	logWriter.Printf("%-9s: %s%s", levelDecoration, callerInfo, msg)
}

// Trace is for low level tracing of activities. It takes an additional 'level'
// parameter. The RLOG_TRACE_LEVEL variable is used to determine which levels
// of trace message are output: Every message with a level lower or equal to
// what is specified in RLOG_TRACE_LEVEL. If RLOG_TRACE_LEVEL is not defined at
// all then no trace messages are printed.
func Trace(traceLevel int, a ...interface{}) {
	prefixAddition := fmt.Sprintf("(%d)", traceLevel)
	basicLog(levelTrace, traceLevel, "", prefixAddition, a...)
}

// Tracef prints trace messages, with formatting.
func Tracef(traceLevel int, format string, a ...interface{}) {
	prefixAddition := fmt.Sprintf("(%d)", traceLevel)
	basicLog(levelTrace, traceLevel, format, prefixAddition, a...)
}

// Debug prints a message if RLOG_LEVEL is set to DEBUG.
func Debug(a ...interface{}) {
	basicLog(levelDebug, notATrace, "", "", a...)
}

// Debugf prints a message if RLOG_LEVEL is set to DEBUG, with formatting.
func Debugf(format string, a ...interface{}) {
	basicLog(levelDebug, notATrace, format, "", a...)
}

// Info prints a message if RLOG_LEVEL is set to INFO or lower.
func Info(a ...interface{}) {
	basicLog(levelInfo, notATrace, "", "", a...)
}

// Infof prints a message if RLOG_LEVEL is set to INFO or lower, with
// formatting.
func Infof(format string, a ...interface{}) {
	basicLog(levelInfo, notATrace, format, "", a...)
}

// Warn prints a message if RLOG_LEVEL is set to WARN or lower.
func Warn(a ...interface{}) {
	basicLog(levelWarn, notATrace, "", "", a...)
}

// Warnf prints a message if RLOG_LEVEL is set to WARN or lower, with
// formatting.
func Warnf(format string, a ...interface{}) {
	basicLog(levelWarn, notATrace, format, "", a...)
}

// Error prints a message if RLOG_LEVEL is set to ERROR or lower.
func Error(a ...interface{}) {
	basicLog(levelErr, notATrace, "", "", a...)
}

// Errorf prints a message if RLOG_LEVEL is set to ERROR or lower, with
// formatting.
func Errorf(format string, a ...interface{}) {
	basicLog(levelErr, notATrace, format, "", a...)
}

// Critical prints a message if RLOG_LEVEL is set to CRITICAL or lower.
func Critical(a ...interface{}) {
	basicLog(levelCrit, notATrace, "", "", a...)
}

// Criticalf prints a message if RLOG_LEVEL is set to CRITICAL or lower, with
// formatting.
func Criticalf(format string, a ...interface{}) {
	basicLog(levelCrit, notATrace, format, "", a...)
}
<commit_msg>Removed unnecessary exports.<commit_after>// Copyright (c) 2016 Pani Networks
// All Rights Reserved.
//
// 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.

package rlog

import (
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
)

const notATrace = -1

// The known log levels
const (
	levelNone = iota
	levelCrit
	levelErr
	levelWarn
	levelInfo
	levelDebug
	levelTrace
)

// Translation map from level to string representation
var levelStrings = map[int]string{
	levelTrace: "TRACE",
	levelDebug: "DEBUG",
	levelInfo:  "INFO",
	levelWarn:  "WARN",
	levelErr:   "ERROR",
	levelCrit:  "CRITICAL",
	levelNone:  "NONE",
}

// Translation from level string to number.
var levelNumbers = map[string]int{
	"TRACE":    levelTrace,
	"DEBUG":    levelDebug,
	"INFO":     levelInfo,
	"WARN":     levelWarn,
	"ERROR":    levelErr,
	"CRITICAL": levelCrit,
	"NONE":     levelNone,
}

// filterSpec holds a list of filters. These are applied to the 'caller'
// information of a log message (calling module and file) to see if this
// message should be logged. Different log or trace levels per file can
// therefore be maintained. For log messages this is the log level, for trace
// messages this is going to be the trace level.
type filterSpec struct {
	filters []filter
}

// fromString initializes filterSpec from string.
//
// Use the isTraceLevel flag to indicate whether the levels are numeric (for
// trace messages) or are level strings (for log messages).
//
// Format "<filter>,<filter>,[<filter>]..."
//     filter:
//       <pattern=level> | <level>
//     pattern:
//       shell glob to match caller file name
//     level:
//       log or trace level of the logs to enable in matched files.
//
//     Example:
//     - "RLOG_TRACE_LEVEL=3"
//       Just a global trace level of 3 for all files and modules.
//     - "RLOG_TRACE_LEVEL=client.go=1,ip*=5,3"
//       This enables trace level 1 in client.go, level 5 in all files whose
//       names start with 'ip', and level 3 for everyone else.
//     - "RLOG_LOG_LEVEL=DEBUG"
//       Global log level DEBUG for all files and modules.
//     - "RLOG_LOG_LEVEL=client.go=ERROR,INFO,ip*=WARN"
//       ERROR and higher for client.go, WARN or higher for all files whose
//       name starts with 'ip', INFO for everyone else.
func (spec *filterSpec) fromString(s string, isTraceLevels bool, globalLevelDefault int) {
	var globalLevel int = globalLevelDefault
	var levelToken string
	var matchToken string

	fields := strings.Split(s, ",")

	for _, f := range fields {
		var filterLevel int
		var err error
		var ok bool

		// Tokens should contain two elements: The filename and the trace
		// level. If there is only one token then we have to assume that this
		// is the 'global' filter (without filename component).
		tokens := strings.Split(f, "=")
		if len(tokens) == 1 {
			// Global level. We'll store this one for the end, since it needs
			// to sit last in the list of filters (during evaluation in gets
			// checked last).
			matchToken = ""
			levelToken = tokens[0]
		} else if len(tokens) == 2 {
			matchToken = tokens[0]
			levelToken = tokens[1]
		} else {
			// Skip anything else that's malformed
			continue
		}
		if isTraceLevels {
			// The level token should contain a numeric value
			if filterLevel, err = strconv.Atoi(levelToken); err != nil {
				continue
			}
		} else {
			// The level token should contain the name of a log level
			levelToken = strings.ToUpper(levelToken)
			filterLevel, ok = levelNumbers[levelToken]
			if !ok || filterLevel == levelTrace {
				// User not allowed to set trace log levels, so if that or
				// not a known log level then this specification will be
				// ignored.
				continue
			}

		}

		if matchToken == "" {
			// Global level just remembered for now, not yet added
			globalLevel = filterLevel
		} else {
			spec.filters = append(spec.filters, filter{matchToken, filterLevel})
		}
	}

	// Now add the global level, so that later it will be evaluated last.
	spec.filters = append(spec.filters, filter{"", globalLevel})

	return
}

// matchfilters checks if given filename and trace level are accepted
// by any of the filters
func (spec *filterSpec) matchfilters(filename string, level int) bool {

	// If there are no filters then we don't match anything.
	if len(spec.filters) == 0 {
		return false
	}

	// If at least one filter matches.
	for _, filter := range spec.filters {
		if matched, loggit := filter.match(filename, level); matched {
			return loggit
		}
	}

	return false
}

// filter holds filename and level to match logs against log messages.
type filter struct {
	Pattern string
	Level   int
}

// match checks if given filename and level are matched by
// this filter. Returns two bools: One to indicate whether a filename match was
// made, and the second to indicate whether the message should be logged
// (matched the level).
func (f filter) match(filename string, level int) (bool, bool) {
	var match bool
	if f.Pattern != "" {
		match, _ = filepath.Match(f.Pattern, filepath.Base(filename))
	} else {
		match = true
	}
	if match {
		return true, level <= f.Level
	}

	return false, false
}

// Rlog is controlled via environment variables. Those things won't change on
// us. Therefore, we can look them up once and store them in module level
// global variables.
var settingShowCallerInfo bool = false // whether we log info about calling function
var settingDateTimeFlags int           // flags for date/time output
var settingLogFile string = ""         // logfile name

var logWriter *log.Logger             // the writer to which output is sent
var logfilterSpec = new(filterSpec)   // filters for log messages
var tracefilterSpec = new(filterSpec) // filters for trace messages

// init extracts settings for our logger from environment variables when the
// module is imported.
func init() {
	logLevelEnv := os.Getenv("RLOG_LOG_LEVEL")
	callerInfoEnv := os.Getenv("RLOG_CALLER_INFO")
	traceLevelEnv := os.Getenv("RLOG_TRACE_LEVEL")
	dontLogTimeEnv := os.Getenv("RLOG_LOG_NOTIME")
	logFileEnv := os.Getenv("RLOG_LOG_FILE")

	// Evaluating the caller info variable.
	settingShowCallerInfo = isTrueBoolString(callerInfoEnv)

	// Evaluating whether date/time should be logged with each message
	// By default (if flag is not set) we want to log date and time.
	logTimeDate := !isTrueBoolString(dontLogTimeEnv)

	// Initialize filters for trace and log levels.
	tracefilterSpec.fromString(traceLevelEnv, true, -1)
	logfilterSpec.fromString(logLevelEnv, false, levelInfo)

	// By default we log to stderr...
	logWriter := os.Stderr

	// ... but if requested we'll create and/or append to a logfile instead
	if logFileEnv != "" {
		newLogFile, err := os.OpenFile(logFileEnv, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
		if err == nil {
			logWriter = newLogFile
		}
	}

	// Initialize the logger we will use throughout
	settingDateTimeFlags = 0
	if logTimeDate {
		// Store the flags that enable date/time logging, since that's
		// controlled by an environment variable. Any new loggers created at
		// runtime should inherit the same setting (the same flags).
		settingDateTimeFlags = log.Ldate | log.Ltime
	}
	SetNewLogWriter(logWriter)
}

// SetNewLogWriter re-wires the log output to a new io.Writer. By default rlog
// logs to os.Stderr, but this function can be used to direct the output
// somewhere else.
func SetNewLogWriter(writer io.Writer) {
	// Use the stored date/time flag settings
	logWriter = log.New(writer, "", settingDateTimeFlags)
}

// isTrueBoolString tests a string to see if it represents a 'true' value.
// The ParseBool function unfortunately doesn't recognize 'y' or 'yes', which
// is why we added that test here as well.
func isTrueBoolString(str string) bool {
	str = strings.ToUpper(str)
	if str == "Y" || str == "YES" {
		return true
	}
	if isTrue, err := strconv.ParseBool(str); err == nil && isTrue {
		return true
	}
	return false
}

// basicLog is called by all the 'level' log functions.
// It checks what is configured to be included in the log message,
// decorates it accordingly and assembles the entire line. It then
// uses the standard log package to finally output the message.
func basicLog(logLevel int, traceLevel int, format string, prefixAddition string, a ...interface{}) {
	// Extract information about the caller of the log function, if requested.
	var callingFuncName string = ""
	var moduleAndFileName string = ""
	pc, fullFilePath, line, ok := runtime.Caller(2)
	if ok {
		callingFuncName = runtime.FuncForPC(pc).Name()
		// We only want to print or examine file and package name, so use the
		// last two elements of the full path. The path package deals with
		// different path formats on different systems, so we use that instead
		// of just string-split.
		dirPath, fileName := path.Split(fullFilePath)
		var moduleName string = ""
		if dirPath != "" {
			dirPath = dirPath[:len(dirPath)-1]
			dirPath, moduleName = path.Split(dirPath)
		}
		moduleAndFileName = moduleName + "/" + fileName
	}

	// Perform tests to see if we should log this message.
	var allowLog bool
	if traceLevel == notATrace {
		if logfilterSpec.matchfilters(moduleAndFileName, logLevel) {
			allowLog = true
		}
	} else {
		if tracefilterSpec.matchfilters(moduleAndFileName, traceLevel) {
			allowLog = true
		}
	}
	if !allowLog {
		return
	}

	callerInfo := ""
	if settingShowCallerInfo {
		callerInfo = fmt.Sprintf("[%s:%d (%s)] ", moduleAndFileName,
			line, callingFuncName)
	}

	// Assemble the actual log line
	var msg string
	if format != "" {
		msg = fmt.Sprintf(format, a...)
	} else {
		msg = fmt.Sprintln(a...)
	}
	levelDecoration := levelStrings[logLevel] + prefixAddition
	logWriter.Printf("%-9s: %s%s", levelDecoration, callerInfo, msg)
}

// Trace is for low level tracing of activities. It takes an additional 'level'
// parameter. The RLOG_TRACE_LEVEL variable is used to determine which levels
// of trace message are output: Every message with a level lower or equal to
// what is specified in RLOG_TRACE_LEVEL. If RLOG_TRACE_LEVEL is not defined at
// all then no trace messages are printed.
func Trace(traceLevel int, a ...interface{}) {
	prefixAddition := fmt.Sprintf("(%d)", traceLevel)
	basicLog(levelTrace, traceLevel, "", prefixAddition, a...)
}

// Tracef prints trace messages, with formatting.
func Tracef(traceLevel int, format string, a ...interface{}) {
	prefixAddition := fmt.Sprintf("(%d)", traceLevel)
	basicLog(levelTrace, traceLevel, format, prefixAddition, a...)
}

// Debug prints a message if RLOG_LEVEL is set to DEBUG.
func Debug(a ...interface{}) {
	basicLog(levelDebug, notATrace, "", "", a...)
}

// Debugf prints a message if RLOG_LEVEL is set to DEBUG, with formatting.
func Debugf(format string, a ...interface{}) {
	basicLog(levelDebug, notATrace, format, "", a...)
}

// Info prints a message if RLOG_LEVEL is set to INFO or lower.
func Info(a ...interface{}) {
	basicLog(levelInfo, notATrace, "", "", a...)
}

// Infof prints a message if RLOG_LEVEL is set to INFO or lower, with
// formatting.
func Infof(format string, a ...interface{}) {
	basicLog(levelInfo, notATrace, format, "", a...)
}

// Warn prints a message if RLOG_LEVEL is set to WARN or lower.
func Warn(a ...interface{}) {
	basicLog(levelWarn, notATrace, "", "", a...)
}

// Warnf prints a message if RLOG_LEVEL is set to WARN or lower, with
// formatting.
func Warnf(format string, a ...interface{}) {
	basicLog(levelWarn, notATrace, format, "", a...)
}

// Error prints a message if RLOG_LEVEL is set to ERROR or lower.
func Error(a ...interface{}) {
	basicLog(levelErr, notATrace, "", "", a...)
}

// Errorf prints a message if RLOG_LEVEL is set to ERROR or lower, with
// formatting.
func Errorf(format string, a ...interface{}) {
	basicLog(levelErr, notATrace, format, "", a...)
}

// Critical prints a message if RLOG_LEVEL is set to CRITICAL or lower.
func Critical(a ...interface{}) {
	basicLog(levelCrit, notATrace, "", "", a...)
}

// Criticalf prints a message if RLOG_LEVEL is set to CRITICAL or lower, with
// formatting.
func Criticalf(format string, a ...interface{}) {
	basicLog(levelCrit, notATrace, format, "", a...)
}
<|endoftext|>


<commit_before>package dnsr

var root = `
;       This file holds the information on root name servers needed to
;       initialize cache of Internet domain name servers
;       (e.g. reference this file in the "cache  .  <file>"
;       configuration file of BIND domain name servers).
;
;       This file is made available by InterNIC
;       under anonymous FTP as
;           file                /domain/named.cache
;           on server           FTP.INTERNIC.NET
;       -OR-                    RS.INTERNIC.NET
;
;       last update:     November 14, 2018
;       related version of root zone:     2018111402
;
; FORMERLY NS.INTERNIC.NET
;
.                        3600000      NS    A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET.      3600000      A     198.41.0.4
A.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:ba3e::2:30
;
; FORMERLY NS1.ISI.EDU
;
.                        3600000      NS    B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET.      3600000      A     199.9.14.201
B.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:200::b
;
; FORMERLY C.PSI.NET
;
.                        3600000      NS    C.ROOT-SERVERS.NET.
C.ROOT-SERVERS.NET.      3600000      A     192.33.4.12
C.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2::c
;
; FORMERLY TERP.UMD.EDU
;
.                        3600000      NS    D.ROOT-SERVERS.NET.
D.ROOT-SERVERS.NET.      3600000      A     199.7.91.13
D.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2d::d
;
; FORMERLY NS.NASA.GOV
;
.                        3600000      NS    E.ROOT-SERVERS.NET.
E.ROOT-SERVERS.NET.      3600000      A     192.203.230.10
E.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:a8::e
;
; FORMERLY NS.ISC.ORG
;
.                        3600000      NS    F.ROOT-SERVERS.NET.
F.ROOT-SERVERS.NET.      3600000      A     192.5.5.241
F.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2f::f
;
; FORMERLY NS.NIC.DDN.MIL
;
.                        3600000      NS    G.ROOT-SERVERS.NET.
G.ROOT-SERVERS.NET.      3600000      A     192.112.36.4
G.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:12::d0d
;
; FORMERLY AOS.ARL.ARMY.MIL
;
.                        3600000      NS    H.ROOT-SERVERS.NET.
H.ROOT-SERVERS.NET.      3600000      A     198.97.190.53
H.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:1::53
;
; FORMERLY NIC.NORDU.NET
;
.                        3600000      NS    I.ROOT-SERVERS.NET.
I.ROOT-SERVERS.NET.      3600000      A     192.36.148.17
I.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fe::53
;
; OPERATED BY VERISIGN, INC.
;
.                        3600000      NS    J.ROOT-SERVERS.NET.
J.ROOT-SERVERS.NET.      3600000      A     192.58.128.30
J.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:c27::2:30
;
; OPERATED BY RIPE NCC
;
.                        3600000      NS    K.ROOT-SERVERS.NET.
K.ROOT-SERVERS.NET.      3600000      A     193.0.14.129
K.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fd::1
;
; OPERATED BY ICANN
;
.                        3600000      NS    L.ROOT-SERVERS.NET.
L.ROOT-SERVERS.NET.      3600000      A     199.7.83.42
L.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:9f::42
;
; OPERATED BY WIDE
;
.                        3600000      NS    M.ROOT-SERVERS.NET.
M.ROOT-SERVERS.NET.      3600000      A     202.12.27.33
M.ROOT-SERVERS.NET.      3600000      AAAA  2001:dc3::35
; End of file`
<commit_msg>auto-update<commit_after>package dnsr

var root = `
;       This file holds the information on root name servers needed to
;       initialize cache of Internet domain name servers
;       (e.g. reference this file in the "cache  .  <file>"
;       configuration file of BIND domain name servers).
;
;       This file is made available by InterNIC
;       under anonymous FTP as
;           file                /domain/named.cache
;           on server           FTP.INTERNIC.NET
;       -OR-                    RS.INTERNIC.NET
;
;       last update:     January 07, 2019
;       related version of root zone:     2019010702
;
; FORMERLY NS.INTERNIC.NET
;
.                        3600000      NS    A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET.      3600000      A     198.41.0.4
A.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:ba3e::2:30
;
; FORMERLY NS1.ISI.EDU
;
.                        3600000      NS    B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET.      3600000      A     199.9.14.201
B.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:200::b
;
; FORMERLY C.PSI.NET
;
.                        3600000      NS    C.ROOT-SERVERS.NET.
C.ROOT-SERVERS.NET.      3600000      A     192.33.4.12
C.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2::c
;
; FORMERLY TERP.UMD.EDU
;
.                        3600000      NS    D.ROOT-SERVERS.NET.
D.ROOT-SERVERS.NET.      3600000      A     199.7.91.13
D.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2d::d
;
; FORMERLY NS.NASA.GOV
;
.                        3600000      NS    E.ROOT-SERVERS.NET.
E.ROOT-SERVERS.NET.      3600000      A     192.203.230.10
E.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:a8::e
;
; FORMERLY NS.ISC.ORG
;
.                        3600000      NS    F.ROOT-SERVERS.NET.
F.ROOT-SERVERS.NET.      3600000      A     192.5.5.241
F.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2f::f
;
; FORMERLY NS.NIC.DDN.MIL
;
.                        3600000      NS    G.ROOT-SERVERS.NET.
G.ROOT-SERVERS.NET.      3600000      A     192.112.36.4
G.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:12::d0d
;
; FORMERLY AOS.ARL.ARMY.MIL
;
.                        3600000      NS    H.ROOT-SERVERS.NET.
H.ROOT-SERVERS.NET.      3600000      A     198.97.190.53
H.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:1::53
;
; FORMERLY NIC.NORDU.NET
;
.                        3600000      NS    I.ROOT-SERVERS.NET.
I.ROOT-SERVERS.NET.      3600000      A     192.36.148.17
I.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fe::53
;
; OPERATED BY VERISIGN, INC.
;
.                        3600000      NS    J.ROOT-SERVERS.NET.
J.ROOT-SERVERS.NET.      3600000      A     192.58.128.30
J.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:c27::2:30
;
; OPERATED BY RIPE NCC
;
.                        3600000      NS    K.ROOT-SERVERS.NET.
K.ROOT-SERVERS.NET.      3600000      A     193.0.14.129
K.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fd::1
;
; OPERATED BY ICANN
;
.                        3600000      NS    L.ROOT-SERVERS.NET.
L.ROOT-SERVERS.NET.      3600000      A     199.7.83.42
L.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:9f::42
;
; OPERATED BY WIDE
;
.                        3600000      NS    M.ROOT-SERVERS.NET.
M.ROOT-SERVERS.NET.      3600000      A     202.12.27.33
M.ROOT-SERVERS.NET.      3600000      AAAA  2001:dc3::35
; End of file`
<|endoftext|>