2014-09-01 00:01:03 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"code.google.com/p/go.net/html"
|
2014-09-16 17:11:16 +00:00
|
|
|
"code.google.com/p/go.net/html/charset"
|
2014-09-01 00:01:03 +00:00
|
|
|
"fmt"
|
2014-09-02 03:53:12 +00:00
|
|
|
"github.com/ericchiang/pup/funcs"
|
2014-09-01 00:01:03 +00:00
|
|
|
"github.com/ericchiang/pup/selector"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2014-09-19 20:37:06 +00:00
|
|
|
const VERSION string = "0.2.2"
|
2014-09-01 18:18:34 +00:00
|
|
|
|
2014-09-01 00:01:03 +00:00
|
|
|
var (
|
|
|
|
// Flags
|
2014-09-02 03:53:12 +00:00
|
|
|
attributes []string = []string{}
|
|
|
|
inputStream io.ReadCloser = os.Stdin
|
|
|
|
indentString string = " "
|
|
|
|
maxPrintLevel int = -1
|
|
|
|
printNumber bool = false
|
|
|
|
printColor bool = false
|
|
|
|
displayer funcs.Displayer = nil
|
2014-09-01 00:01:03 +00:00
|
|
|
)
|
|
|
|
|
2014-09-01 18:50:10 +00:00
|
|
|
// Print to stderr and exit
|
2014-09-01 00:01:03 +00:00
|
|
|
func Fatal(format string, args ...interface{}) {
|
|
|
|
fmt.Fprintf(os.Stderr, format, args...)
|
|
|
|
fmt.Fprintf(os.Stderr, "\n")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
2014-09-01 18:50:10 +00:00
|
|
|
// Print help to stderr and quit
|
|
|
|
func PrintHelp() {
|
2014-09-01 18:18:34 +00:00
|
|
|
helpString := `Usage
|
2014-09-01 00:01:03 +00:00
|
|
|
|
|
|
|
pup [list of css selectors]
|
|
|
|
|
2014-09-01 18:18:34 +00:00
|
|
|
Version
|
|
|
|
|
|
|
|
%s
|
|
|
|
|
|
|
|
Flags
|
2014-09-01 00:01:03 +00:00
|
|
|
|
2014-09-01 18:18:34 +00:00
|
|
|
-c --color print result with color
|
2014-09-01 00:01:03 +00:00
|
|
|
-f --file file to read from
|
|
|
|
-h --help display this help
|
|
|
|
-i --indent number of spaces to use for indent or character
|
|
|
|
-n --number print number of elements selected
|
2014-09-01 18:18:34 +00:00
|
|
|
-l --limit restrict number of levels printed
|
|
|
|
--version display version`
|
|
|
|
Fatal(helpString, VERSION)
|
2014-09-01 00:01:03 +00:00
|
|
|
}
|
|
|
|
|
2014-09-01 18:50:10 +00:00
|
|
|
// Process command arguments and return all non-flags.
|
|
|
|
func ProcessFlags(cmds []string) []string {
|
2014-09-01 00:01:03 +00:00
|
|
|
var i int
|
|
|
|
var err error
|
|
|
|
defer func() {
|
|
|
|
if r := recover(); r != nil {
|
|
|
|
Fatal("Option '%s' requires an argument", cmds[i])
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
nonFlagCmds := make([]string, len(cmds))
|
|
|
|
n := 0
|
|
|
|
for i = 0; i < len(cmds); i++ {
|
|
|
|
cmd := cmds[i]
|
|
|
|
switch cmd {
|
2014-09-02 03:53:12 +00:00
|
|
|
case "-a", "--attr":
|
|
|
|
attributes = append(attributes, cmds[i+1])
|
|
|
|
i++
|
2014-09-01 18:18:34 +00:00
|
|
|
case "-c", "--color":
|
|
|
|
printColor = true
|
2014-09-01 00:01:03 +00:00
|
|
|
case "-f", "--file":
|
|
|
|
filename := cmds[i+1]
|
|
|
|
inputStream, err = os.Open(filename)
|
|
|
|
if err != nil {
|
|
|
|
Fatal(err.Error())
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
case "-h", "--help":
|
2014-09-01 18:50:10 +00:00
|
|
|
PrintHelp()
|
2014-09-01 00:01:03 +00:00
|
|
|
os.Exit(1)
|
|
|
|
case "-i", "--indent":
|
|
|
|
indentLevel, err := strconv.Atoi(cmds[i+1])
|
|
|
|
if err == nil {
|
2014-09-01 18:18:34 +00:00
|
|
|
indentString = strings.Repeat(" ", indentLevel)
|
2014-09-01 00:01:03 +00:00
|
|
|
} else {
|
2014-09-01 18:18:34 +00:00
|
|
|
indentString = cmds[i+1]
|
2014-09-01 00:01:03 +00:00
|
|
|
}
|
|
|
|
i++
|
|
|
|
case "-n", "--number":
|
|
|
|
printNumber = true
|
2014-09-01 18:18:34 +00:00
|
|
|
case "-l", "--limit":
|
2014-09-01 00:01:03 +00:00
|
|
|
maxPrintLevel, err = strconv.Atoi(cmds[i+1])
|
|
|
|
if err != nil {
|
|
|
|
Fatal("Argument for '%s' must be numeric",
|
|
|
|
cmds)
|
|
|
|
}
|
|
|
|
i++
|
2014-09-01 18:18:34 +00:00
|
|
|
case "--version":
|
|
|
|
Fatal(VERSION)
|
2014-09-01 00:01:03 +00:00
|
|
|
default:
|
|
|
|
if cmd[0] == '-' {
|
|
|
|
Fatal("Unrecognized flag '%s'", cmd)
|
|
|
|
}
|
|
|
|
nonFlagCmds[n] = cmds[i]
|
|
|
|
n++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nonFlagCmds[:n]
|
|
|
|
}
|
|
|
|
|
2014-09-01 18:50:10 +00:00
|
|
|
// pup
|
2014-09-01 00:01:03 +00:00
|
|
|
func main() {
|
2014-09-01 18:50:10 +00:00
|
|
|
cmds := ProcessFlags(os.Args[1:])
|
2014-09-16 17:11:16 +00:00
|
|
|
cr, err := charset.NewReader(inputStream, "")
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, err.Error())
|
|
|
|
os.Exit(2)
|
|
|
|
}
|
|
|
|
root, err := html.Parse(cr)
|
2014-09-01 00:01:03 +00:00
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, err.Error())
|
|
|
|
os.Exit(2)
|
|
|
|
}
|
|
|
|
inputStream.Close()
|
|
|
|
if len(cmds) == 0 {
|
|
|
|
PrintNode(root, 0)
|
|
|
|
os.Exit(0)
|
|
|
|
}
|
2014-09-15 00:34:21 +00:00
|
|
|
selectors := make([]selector.Selector, len(cmds))
|
2014-09-01 00:01:03 +00:00
|
|
|
for i, cmd := range cmds {
|
2014-09-18 00:32:49 +00:00
|
|
|
// if this is the last element, check for a function like
|
|
|
|
// text{} or attr{}
|
2014-09-02 03:53:12 +00:00
|
|
|
if i+1 == len(cmds) {
|
|
|
|
d, err := funcs.NewDisplayFunc(cmd)
|
|
|
|
if err == nil {
|
|
|
|
displayer = d
|
|
|
|
selectors = selectors[0 : len(cmds)-1]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2014-09-01 18:50:10 +00:00
|
|
|
selectors[i], err = selector.NewSelector(cmd)
|
2014-09-01 00:01:03 +00:00
|
|
|
if err != nil {
|
2014-09-01 20:39:26 +00:00
|
|
|
Fatal("Selector parse error: %s", err)
|
2014-09-01 00:01:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
currNodes := []*html.Node{root}
|
|
|
|
for _, selector := range selectors {
|
2014-09-18 00:32:49 +00:00
|
|
|
currNodes = selector.Select(currNodes)
|
2014-09-01 00:01:03 +00:00
|
|
|
}
|
2014-09-02 03:53:12 +00:00
|
|
|
if displayer != nil {
|
|
|
|
displayer.Display(currNodes)
|
|
|
|
} else if printNumber {
|
2014-09-01 00:01:03 +00:00
|
|
|
fmt.Println(len(currNodes))
|
|
|
|
} else {
|
|
|
|
for _, s := range currNodes {
|
2014-09-01 18:18:34 +00:00
|
|
|
// defined in `printing.go`
|
2014-09-01 00:01:03 +00:00
|
|
|
PrintNode(s, 0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|