Browse Source

cleanup for tokenizing

tags/v3.0.0
Chris Wiegman 5 months ago
parent
commit
5b1a9344d5
Signed by: chriswiegman <chris@chriswiegman.com> GPG Key ID: 2B23D7A74FDE2DF4
8 changed files with 234 additions and 214 deletions
  1. +3
    -2
      cmd/main.go
  2. +1
    -0
      const.go
  3. +1
    -0
      const_windows.go
  4. +5
    -0
      go.mod
  5. +2
    -0
      go.sum
  6. +1
    -212
      goodhosts.go
  7. +44
    -0
      hostLine.go
  8. +177
    -0
      hostsFile.go

+ 3
- 2
cmd/main.go View File

@@ -2,9 +2,10 @@ package main

import (
"fmt"
"github.com/docopt/docopt-go"
"github.com/lextoumbourou/goodhosts"
"os"

"github.com/ChrisWiegman/goodhosts"
"github.com/docopt/docopt-go"
)

func check(err error) {


+ 1
- 0
const.go View File

@@ -4,3 +4,4 @@ package goodhosts

const hostsFilePath = "/etc/hosts"
const eol = "\n"
const commentChar string = "#"

+ 1
- 0
const_windows.go View File

@@ -2,3 +2,4 @@ package goodhosts

const hostsFilePath = "${SystemRoot}/System32/drivers/etc/hosts"
const eol = "\r\n"
const commentChar string = "#"

+ 5
- 0
go.mod View File

@@ -0,0 +1,5 @@
module github.com/ChrisWiegman/goodhosts

go 1.13

require github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815

+ 2
- 0
go.sum View File

@@ -0,0 +1,2 @@
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=

+ 1
- 212
goodhosts.go View File

@@ -1,222 +1,11 @@
package goodhosts

import (
"bufio"
"errors"
"fmt"
"net"
"os"
"path/filepath"
"strings"
)

const commentChar string = "#"

// Represents a single line in the hosts file.
type HostsLine struct {
IP string
Hosts []string
Raw string
Err error
}

// Return ```true``` if the line is a comment.
func (l HostsLine) IsComment() bool {
trimLine := strings.TrimSpace(l.Raw)
isComment := strings.HasPrefix(trimLine, commentChar)
return isComment
}

// Return a new instance of ```HostsLine```.
func NewHostsLine(raw string) HostsLine {
fields := strings.Fields(raw)
if len(fields) == 0 {
return HostsLine{Raw: raw}
}

output := HostsLine{Raw: raw}
if !output.IsComment() {
rawIP := fields[0]
if net.ParseIP(rawIP) == nil {
output.Err = errors.New(fmt.Sprintf("Bad hosts line: %q", raw))
}

output.IP = rawIP
output.Hosts = fields[1:]
}

return output
}

// Represents a hosts file.
type Hosts struct {
Path string
Lines []HostsLine
}

// Return ```true``` if hosts file is writable.
func (h *Hosts) IsWritable() bool {
_, err := os.OpenFile(h.Path, os.O_WRONLY, 0660)
if err != nil {
return false
}

return true
}

// Load the hosts file into ```l.Lines```.
// ```Load()``` is called by ```NewHosts()``` and ```Hosts.Flush()``` so you
// generally you won't need to call this yourself.
func (h *Hosts) Load() error {
var lines []HostsLine

file, err := os.Open(h.Path)
if err != nil {
return err
}
defer file.Close()

scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := NewHostsLine(scanner.Text())
if err != nil {
return err
}

lines = append(lines, line)
}

if err := scanner.Err(); err != nil {
return err
}

h.Lines = lines

return nil
}

// Flush any changes made to hosts file.
func (h Hosts) Flush() error {
file, err := os.Create(h.Path)
if err != nil {
return err
}

w := bufio.NewWriter(file)

for _, line := range h.Lines {
fmt.Fprintf(w, "%s%s", line.Raw, eol)
}

err = w.Flush()
if err != nil {
return err
}

return h.Load()
}

// Add an entry to the hosts file.
func (h *Hosts) Add(ip string, hosts ...string) error {
if net.ParseIP(ip) == nil {
return errors.New(fmt.Sprintf("%q is an invalid IP address.", ip))
}

position := h.getIpPosition(ip)
if position == -1 {
endLine := NewHostsLine(buildRawLine(ip, hosts))
// Ip line is not in file, so we just append our new line.
h.Lines = append(h.Lines, endLine)
} else {
// Otherwise, we replace the line in the correct position
newHosts := h.Lines[position].Hosts
for _, addHost := range hosts {
if itemInSlice(addHost, newHosts) {
continue
}

newHosts = append(newHosts, addHost)
}
endLine := NewHostsLine(buildRawLine(ip, newHosts))
h.Lines[position] = endLine
}

return nil
}

// Return a bool if ip/host combo in hosts file.
func (h Hosts) Has(ip string, host string) bool {
pos := h.getHostPosition(ip, host)

return pos != -1
}

// Remove an entry from the hosts file.
func (h *Hosts) Remove(ip string, hosts ...string) error {
var outputLines []HostsLine

if net.ParseIP(ip) == nil {
return errors.New(fmt.Sprintf("%q is an invalid IP address.", ip))
}

for _, line := range h.Lines {

// Bad lines or comments just get readded.
if line.Err != nil || line.IsComment() || line.IP != ip {
outputLines = append(outputLines, line)
continue
}

var newHosts []string
for _, checkHost := range line.Hosts {
if !itemInSlice(checkHost, hosts) {
newHosts = append(newHosts, checkHost)
}
}

// If hosts is empty, skip the line completely.
if len(newHosts) > 0 {
newLineRaw := line.IP

for _, host := range newHosts {
newLineRaw = fmt.Sprintf("%s %s", newLineRaw, host)
}
newLine := NewHostsLine(newLineRaw)
outputLines = append(outputLines, newLine)
}
}

h.Lines = outputLines
return nil
}

func (h Hosts) getHostPosition(ip string, host string) int {
for i := range h.Lines {
line := h.Lines[i]
if !line.IsComment() && line.Raw != "" {
if ip == line.IP && itemInSlice(host, line.Hosts) {
return i
}
}
}

return -1
}

func (h Hosts) getIpPosition(ip string) int {
for i := range h.Lines {
line := h.Lines[i]
if !line.IsComment() && line.Raw != "" {
if line.IP == ip {
return i
}
}
}

return -1
}

// Return a new instance of ``Hosts``.
// NewHosts Return a new instance of ``Hosts``.
func NewHosts() (Hosts, error) {
osHostsFilePath := ""



+ 44
- 0
hostLine.go View File

@@ -0,0 +1,44 @@
package goodhosts

import (
"fmt"
"net"
"strings"
)

// HostsLine Represents a single line in the hosts file.
type HostsLine struct {
IP string
Hosts []string
Raw string
Err error
}

// IsComment Return ```true``` if the line is a comment.
func (l HostsLine) IsComment() bool {
trimLine := strings.TrimSpace(l.Raw)
isComment := strings.HasPrefix(trimLine, commentChar)
return isComment
}

// NewHostsLine Return a new instance of ```HostsLine```.
func NewHostsLine(raw string) HostsLine {

fields := strings.Fields(raw)
if len(fields) == 0 {
return HostsLine{Raw: raw}
}

output := HostsLine{Raw: raw}
if !output.IsComment() {
rawIP := fields[0]
if net.ParseIP(rawIP) == nil {
output.Err = fmt.Errorf("Bad hosts line: %q", raw)
}

output.IP = rawIP
output.Hosts = fields[1:]
}

return output
}

+ 177
- 0
hostsFile.go View File

@@ -0,0 +1,177 @@
package goodhosts

import (
"bufio"
"errors"
"fmt"
"net"
"os"
)

// Hosts Represents a hosts file.
type Hosts struct {
Path string
Lines []HostsLine
}

// IsWritable Return ```true``` if hosts file is writable.
func (h *Hosts) IsWritable() bool {
_, err := os.OpenFile(h.Path, os.O_WRONLY, 0660)
if err != nil {
return false
}

return true
}

// Load the hosts file into ```l.Lines```.
// ```Load()``` is called by ```NewHosts()``` and ```Hosts.Flush()``` so you
// generally you won't need to call this yourself.
func (h *Hosts) Load() error {
var lines []HostsLine

file, err := os.Open(h.Path)
if err != nil {
return err
}
defer file.Close()

scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := NewHostsLine(scanner.Text())
if err != nil {
return err
}

lines = append(lines, line)
}

if err := scanner.Err(); err != nil {
return err
}

h.Lines = lines

return nil
}

// Flush any changes made to hosts file.
func (h Hosts) Flush() error {
file, err := os.Create(h.Path)
if err != nil {
return err
}

w := bufio.NewWriter(file)

for _, line := range h.Lines {
fmt.Fprintf(w, "%s%s", line.Raw, eol)
}

err = w.Flush()
if err != nil {
return err
}

return h.Load()
}

// Add an entry to the hosts file.
func (h *Hosts) Add(ip string, hosts ...string) error {
if net.ParseIP(ip) == nil {
return fmt.Errorf("%q is an invalid IP address.", ip)
}

position := h.getIPPosition(ip)
if position == -1 {
endLine := NewHostsLine(buildRawLine(ip, hosts))
// Ip line is not in file, so we just append our new line.
h.Lines = append(h.Lines, endLine)
} else {
// Otherwise, we replace the line in the correct position
newHosts := h.Lines[position].Hosts
for _, addHost := range hosts {
if itemInSlice(addHost, newHosts) {
continue
}

newHosts = append(newHosts, addHost)
}
endLine := NewHostsLine(buildRawLine(ip, newHosts))
h.Lines[position] = endLine
}

return nil
}

// Has Return a bool if ip/host combo in hosts file.
func (h Hosts) Has(ip string, host string) bool {
pos := h.getHostPosition(ip, host)

return pos != -1
}

// Remove an entry from the hosts file.
func (h *Hosts) Remove(ip string, hosts ...string) error {
var outputLines []HostsLine

if net.ParseIP(ip) == nil {
return errors.New(fmt.Sprintf("%q is an invalid IP address.", ip))
}

for _, line := range h.Lines {

// Bad lines or comments just get readded.
if line.Err != nil || line.IsComment() || line.IP != ip {
outputLines = append(outputLines, line)
continue
}

var newHosts []string
for _, checkHost := range line.Hosts {
if !itemInSlice(checkHost, hosts) {
newHosts = append(newHosts, checkHost)
}
}

// If hosts is empty, skip the line completely.
if len(newHosts) > 0 {
newLineRaw := line.IP

for _, host := range newHosts {
newLineRaw = fmt.Sprintf("%s %s", newLineRaw, host)
}
newLine := NewHostsLine(newLineRaw)
outputLines = append(outputLines, newLine)
}
}

h.Lines = outputLines
return nil
}

func (h Hosts) getHostPosition(ip string, host string) int {
for i := range h.Lines {
line := h.Lines[i]
if !line.IsComment() && line.Raw != "" {
if ip == line.IP && itemInSlice(host, line.Hosts) {
return i
}
}
}

return -1
}

func (h Hosts) getIPPosition(ip string) int {
for i := range h.Lines {
line := h.Lines[i]
if !line.IsComment() && line.Raw != "" {
if line.IP == ip {
return i
}
}
}

return -1
}

Loading…
Cancel
Save