Source

xplor / xplor.go

mpl 8d664fb 

mpl 35c329b 





mpl 22b5940 
mpl 35c329b 
mpl 8d664fb 
mpl 35c329b 





mpl 22b5940 
mpl 8d664fb 






mpl 22b5940 
mpl 8d664fb 

mpl 35c329b 
mpl 8d664fb 



mpl 35c329b 
mpl 8d664fb 








mpl 35c329b 

mpl 8d664fb 

mpl 35c329b 








mpl 8d664fb 
mpl 35c329b 


mpl 8d664fb 
mpl 35c329b 







mpl 8d664fb 
mpl 35c329b 



mpl 8d664fb 
mpl 35c329b 





mpl 22b5940 
mpl 35c329b 
mpl 22b5940 
mpl 35c329b 

mpl 22b5940 
mpl 35c329b 
mpl 8d664fb 





mpl 35c329b 

mpl 22b5940 
mpl 35c329b 











mpl 22b5940 





mpl 8d664fb 

















mpl 22b5940 









mpl 8d664fb 
mpl 22b5940 















mpl 8d664fb 


mpl 22b5940 
mpl 35c329b 
mpl 8d664fb 
mpl 35c329b 

mpl 22b5940 
mpl 8d664fb 
mpl 22b5940 
mpl 35c329b 
mpl 8d664fb 
mpl 35c329b 

mpl 8d664fb 
mpl 35c329b 
mpl 8d664fb 









mpl 35c329b 

mpl 8d664fb 

mpl 35c329b 
mpl 8d664fb 








































mpl 35c329b 















mpl 8d664fb 
mpl 35c329b 










// 2010 - Mathieu Lonjaret. BSD-style licence.

package main

import (
	"os"
	"path"
	"fmt"
	"strings"
	"sort"
	"flag"

	"goplan9.googlecode.com/hg/plan9/acme"
)

	var root string
	var w *acme.Win
	var INDENT string = "	"
	var PLAN9 = os.Getenv("PLAN9")
	
func usage() {
    fmt.Fprintf(os.Stderr, "usage: xplor [path] \n")
    flag.PrintDefaults()
    os.Exit(2)
}

//TODO: send error messages to +Errors instead of Stderr?
 
func main() {
	flag.Usage = usage
    flag.Parse()
    
	args := flag.Args()

	switch len(args) {
    case 0:
        root, _ = os.Getwd()
    case 1:
 		root = args[0]
    default:
        usage()
    }
    	
	initWindow()
	
	w.Write("+Errors", []byte("Hello world"))
	
	for word := range events() {
		go onLook(word) 
	}
}

func initWindow() {
	var err os.Error
	w, err = acme.New()
	if err != nil {
		fmt.Fprintf(os.Stderr, err.String())
		return 
	}

	title := "xplor-" + root
	w.Name(title)

	printDirContents(root, 0)
}

func printDirContents(path string, depth int) {
	currentDir, err := os.Open(path, os.O_RDONLY, 0644)
	if err != nil {
		fmt.Fprintf(os.Stderr, err.String())
		return 
	}
	names, err := currentDir.Readdirnames(-1)
	if err != nil {
		fmt.Fprintf(os.Stderr, err.String())
		return 
	}
	currentDir.Close();
	
	sort.SortStrings(names)

	indents := ""
	for i := 0; i < depth; i++ {
		indents = indents + INDENT
	}
	for _, v := range names {	
		w.Write("data", []byte(indents + v + "\n"))
	}
	
	//lame trick for now to dodge the out of range issue, until my address-foo gets better
	if depth == 0 {
		w.Write("body", []byte("\n"))
		w.Write("body", []byte("\n"))
	}
}

func readLine(addr string) ([]byte, os.Error) {
	const NBUF = 512	
	var b []byte = make([]byte, NBUF)
	var err os.Error = nil
	err = w.Addr("%s", addr)
	if err != nil {
		return b, err
	}
	n, err := w.Read("xdata", b)
	
	return b[0:n-1], err
}

func getDepth(line []byte) (depth int, trimedline string) {
	trimedline = strings.TrimLeft(string(line), INDENT)
	depth = (len(line) - len(trimedline)) / len(INDENT)
	return depth, trimedline
}

func isFolded(charaddr string) (bool, os.Error) {
	var err os.Error = nil
	var b []byte
	addr := "#" + charaddr + "+1-"
	b, err = readLine(addr)
	if err != nil {
		return true, err
	}
	depth, _ := getDepth(b)
	addr = "#" + charaddr + "+-"
	b, err = readLine(addr)
	if err != nil {
		return true, err
	}	
	nextdepth, _ := getDepth(b)
	return (nextdepth <= depth), err 
}

func getParents(charaddr string, depth int, prevline int) string {
	var addr string
	if depth == 0 {
		return ""
	}
	if prevline == 1 {
		addr = "#" + charaddr + "-+"
	} else {
		addr = "#" + charaddr + "-" + fmt.Sprint(prevline - 1)
	}
	for {
		b, err := readLine(addr)
		if err != nil {
			w.Write("body", []byte(err.String()))
			return ""
		}
		newdepth, line := getDepth(b)
		if newdepth < depth {
			fullpath := path.Join(getParents(charaddr, newdepth, prevline), "/", line)
			return fullpath
		}
		prevline++
		addr = "#" + charaddr + "-" + fmt.Sprint(prevline - 1)
	}
	return ""
}

func onLook(charaddr string) {
	// reconstruct full path and check if file or dir
	addr := "#" + charaddr + "+1-"
	b, err := readLine(addr)
	if err != nil {
		fmt.Fprintf(os.Stderr, err.String())
		return
	}
	depth, line := getDepth(b)
	fullpath := path.Join(root, "/", getParents(charaddr, depth, 1), "/", line)
	fi, err := os.Lstat(fullpath)
	if err != nil {
		fmt.Fprintf(os.Stderr, err.String())
		return
	}
	
	if !fi.IsDirectory() {
		// send that file to B (ie open it)
		if len(PLAN9) == 0 {
			fmt.Fprintf(os.Stderr, "$PLAN9 not defined \n")
			return
		}
		var args2 []string = make([]string, 2)
		args2[0] = path.Join(PLAN9 + "/bin/B")
		args2[1] = fullpath
	    fds := []*os.File{os.Stdout, os.Stdout, os.Stderr}
		os.ForkExec(args2[0], args2, os.Environ(), "", fds)
		return
	}
	
	folded, err := isFolded(charaddr)
	if err != nil {
		fmt.Fprint(os.Stderr, err.String())
		return	
	}
	if folded {
		// print dir contents
		addr = "#" + charaddr + "+2-1-#0"
		err = w.Addr("%s", addr)
		if err != nil {
			fmt.Fprintf(os.Stderr, err.String() + addr)
			return
		}	
		printDirContents(fullpath, depth + 1)
	} else {
		// unfold, ie delete lines below dir until we hit a dir of the same depth
		addr = "#" + charaddr + "+-"
		nextdepth := depth + 1
		nextline := 1
		for nextdepth > depth {
			err = w.Addr("%s", addr)
			if err != nil {
				fmt.Fprint(os.Stderr, err.String())
				return
			}
			b, err = readLine(addr)
			if err != nil {
				fmt.Fprint(os.Stderr, err.String())
				return
			}
			nextdepth, _ = getDepth(b)
			nextline++;
			addr = "#" + charaddr + "+" + fmt.Sprint(nextline-1)
		}
		nextline--
		addr = "#" + charaddr + "+-#0,#" + charaddr + "+" + fmt.Sprint(nextline-2)
		err = w.Addr("%s", addr)
			if err != nil {
				fmt.Fprint(os.Stderr, err.String())
				return
			}
		w.Write("data", []byte(""))
	}
}

func events() <-chan string {
	c := make(chan string, 10)
	go func() {
		for e := range w.EventChan() {
			switch e.C2 {
			case 'x', 'X':	// execute
				if string(e.Text) == "Del" {
					w.Ctl("delete")
				}
				w.WriteEvent(e)
			case 'l': // in the tag, let the plumber deal with it
				w.WriteEvent(e)
			case 'L':	// look
				w.Ctl("clean")
				//ignore expansions
				if e.OrigQ0 != e.OrigQ1 {
					continue
				}
				msg := fmt.Sprint(e.OrigQ0)
				c <- msg
			}
		}
		w.CloseFiles()
		close(c)
	}()
	return c
}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.