<commit_before>package main

import (
	"flag"
	"fmt"
	"strings"
	"bufio"
	"os"
	"math/rand"
	"time"

	"github.com/bwmarrin/discordgo"
)

func init() {
	flag.StringVar(&token, "t", "", "Bot Token")
	flag.Parse()
	readLines("links.txt")
}

var token string
var lines []string
func main() {
	if token == "" {
		fmt.Println("No token provided. Please run: airhorn -t <bot token>")
		return
	}

	// Create a new Discord session using the provided bot token.
	dg, err := discordgo.New("Bot " + token)
	if err != nil {
		fmt.Println("Error creating Discord session: ", err)
		return
	}

	// Register ready as a callback for the ready events.
	dg.AddHandler(ready)

	// Register messageCreate as a callback for the messageCreate events.
	dg.AddHandler(messageCreate)

	// Register guildCreate as a callback for the guildCreate events.
	dg.AddHandler(guildCreate)

	// Open the websocket and begin listening.
	err = dg.Open()
	if err != nil {
		fmt.Println("Error opening Discord session: ", err)
	}

	fmt.Println("KawaiiBot is now running.  Press CTRL-C to exit.")
	// Simple way to keep program running until CTRL-C is pressed.
	<-make(chan struct{})
	return
}

func ready(s *discordgo.Session, event *discordgo.Ready) {
	// Set the playing status.
	_ = s.UpdateStatus(0, "!info")
}

// This function will be called (due to AddHandler above) every time a new
// message is created on any channel that the autenticated bot has access to.
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
	if strings.HasPrefix(m.Content, "!info") {

		s.ChannelMessageSend(m.ChannelID, "Hello, I am Just a testing bot, created due to improve my master's Go skills.")
	}

	if strings.HasPrefix(m.Content, "!wank") {

		s.ChannelMessageSend(m.ChannelID, "Suck my dee------ick, bitch ")
	}

	if strings.HasPrefix(m.Content, "!hentai"){

		k := rand.NewSource(time.Now().Unix())
		r := rand.New(k) // initialize local pseudorandom generator
		s.ChannelMessageSend(m.ChannelID, lines[r.Intn(len(lines))])
		fmt.Println(string(len(lines)))
	}
	if strings.HasPrefix(m.Content, "!help"){
		s.ChannelMessageSend(m.ChannelID, "Available commands: \n!info\tIt will tell you more about bot\n!hentai\tTells why did I chose programming")
	}
}

// This function will be called (due to AddHandler above) every time a new
// guild is joined.
func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) {
	if event.Guild.Unavailable {
		return
	}

	for _, channel := range event.Guild.Channels {
		if channel.ID == event.Guild.ID {
			_, _ = s.ChannelMessageSend(channel.ID, "Kawaii is ready! Type !info to get some usefull shit about me.")
			return
		}
	}
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

func readLines(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	return lines, scanner.Err()
}
<commit_msg>Update main.go<commit_after>package main

import (
	"flag"
	"fmt"
	"strings"
	"bufio"
	"os"
	"math/rand"

	"time"
	"strconv"

	"github.com/bwmarrin/discordgo"
	"github.com/PuloV/ics-golang"

)

func init() {
	flag.StringVar(&token, "t", "", "Bot Token")
	flag.Parse()
	readLines("links.txt")
}

var token string
var lines []string
var anounced = false
var event []string
var eightballResponses = []string{
	"Most definitely yes",
	"For sure",
	"As I see it, yes",
	"My sources say yes",
	"Yes",
	"According to BBC, your mother is a whore",
	"Most likely",
	"Signs point to yes",
	"Perhaps",
	"Maybe",
	"Reply hazy, try again",
	"Ask again later ",
	"Better not tell you now",
	" Cannot predict now",
	"Concentrate and ask again",
	"Not sure",
	"It is uncertain",
	"Ask me again later",
	"Don't count on it",
	"Probably not",
	"Very doubtful",
	"Most likely no",
	"Nope",
	"No",
	"My sources say no",
	"Dont even think about it",
	"Definitely no",
	"NO, YOU WHORE!!",
	"NO - It may cause disease contraction",
}
var wankOpions = []string{
	"Amateur",
	"American",
	"Anal Sex",
	"Hentai",
	"BDSM",
	"Beach Sex",
	"Blowjob",
	"Creampie",
	"Deapthroat",
	"Ebony",
	"German",
	"Hardcore",
	"Indian",
	"Milf",
	"Orgy",
	"Public",
	"Redhead",
	"Retro",
	"Russian",
	"School",
	"Shemale",
	"Jap",
	"Teacher",
	"Latin",
	"Solo Girls",
	"Gay",
}
var coin = []string{
	"HEADS",
	"TAILS",
}
func main() {
	if token == "" {
		fmt.Println("No token provided. Please run: airhorn -t <bot token>")
		return
	}

	// Create a new Discord session using the provided bot token.
	dg, err := discordgo.New("Bot " + token)
	if err != nil {
		fmt.Println("Error creating Discord session: ", err)
		return
	}
	//dg.GuildMemberRoleRemove("238048572413575168","299602264039882752","295907725022593024")
	// Register ready as a callback for the ready events.
	dg.AddHandler(ready)

	// Register messageCreate as a callback for the messageCreate events.
	dg.AddHandler(messageCreate)

	// Register guildCreate as a callback for the guildCreate events.
	dg.AddHandler(guildCreate)

	dg.AddHandler(newMember)

	// Open the websocket and begin listening.
	err = dg.Open()
	if err != nil {
		fmt.Println("Error opening Discord session: ", err)
	}
	ticker:=time.NewTicker(time.Hour * 12)
	var msg string
	var num int
	msg,num = readCal("courseworks.ics")
	go func(){
		for range ticker.C {
			if num!= 0 {
				dg.ChannelMessageSend("238048572413575168", "@everyone you have a lot of courseworks to do!"+"\n"+msg)
			}else {
				dg.ChannelMessageSend("238048572413575168", " no courseworks!! You are free!")
			}
		}

	}()
	fmt.Print("Bot is running.... Thanks god!!")
	// Simple way to keep program running until CTRL-C is pressed.
	<-make(chan struct{})
	return
}

func ready(s *discordgo.Session, event *discordgo.Ready) {
	// Set the playing status.
	_ = s.UpdateStatus(0, "@AlexBot !info|HadalHW17")

}

// This function will be called (due to AddHandler above) every time a new
// message is created on any channel that the autenticated bot has access to.
func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
	var text = m.ContentWithMentionsReplaced()
	text=strings.Replace(text,"@everyone","",-1)
	if strings.HasPrefix(m.Content, "!info") {

		s.ChannelMessageSend(m.ChannelID, "Hello, I am Just a testing bot, created due to improve my master's Go skills.")
	}

	if strings.HasPrefix(m.Content, "!wank") {

		s.ChannelMessageSend(m.ChannelID, wankWheel(m))
	}

	if strings.HasPrefix(m.Content, "!hentai") && strings.Compare(m.ChannelID, "295887491809017858")==0 {

		k := rand.NewSource(time.Now().Unix())
		r := rand.New(k) // initialize local pseudorandom generator
		s.ChannelMessageSend(m.ChannelID, lines[r.Intn(len(lines))])
		fmt.Println(string(len(lines)))
	}else if strings.HasPrefix(m.Content, "!hentai") && strings.Compare(m.ChannelID, "295887491809017858")!=0{
		s.ChannelMessageSend(m.ChannelID, "**@"+ m.Author.Username +"** will be banned cuz hentai should be posted at #nsfw")
		s.GuildMemberRoleAdd("238048572413575168",m.Author.ID,"295907725022593024")
		s.ChannelMessageSend(m.ChannelID,"You are now @Naughty Faggot")
		time.Sleep(time.Minute*5)
		s.GuildMemberRoleRemove("238048572413575168",m.Author.ID,"295907725022593024")
		s.ChannelMessageSend(m.ChannelID,"**@"+ m.Author.Username +"** you are not a @Naughty Faggot any more, but be careful next time. " + m.Author.Token)
	}
	if strings.HasPrefix(m.Content, "!help"){
		if strings.Contains(m.Content, "meme"){
			s.ChannelMessageSend(m.ChannelID, "To generate meme, please type !meme <meme_name> <top_text> <bot_text>\n" +
				"Instead of spaces in <top_text> and <bot_text> use '_'\n" +
				"List of available memes:\nfry\nkermit\nafraid\naag\nblb\nkeanu\nbd\n" +
				"Example: !memes fry top_text_here bot_text_here")
		}else {
			s.ChannelMessageSend(m.ChannelID, "```Available commands: \n!info\tIt will tell you more about bot\n!hentai\tTells why did I chose programming"+
				"\n!wank\tProbably the best feature here.\n!deadline\tShow the list of all deadlines.\n!timetable\tShow our timetable\n!meme\tGet help by typing" +
				" !help meme\n!google\tGoogles you any question```")
		}
	}
	var num int
	var msg string
	msg,num = readCal("courseworks.ics")
	if strings.HasPrefix(m.Content, "!deadline"){
		s.ChannelMessageSend(m.ChannelID, "You have "+ strconv.Itoa(num-1) + " courseworks to do:\n"+msg)
	}
	if strings.HasPrefix(m.Content, "!timetable"){
		s.ChannelMessageSend(m.ChannelID, "Here you go, wanker http://imgur.com/a/jx8N2")
	}
	if strings.HasPrefix(m.Content, "!8ball"){
		s.ChannelMessageSend(m.ChannelID, eightball(text))
	}
	if strings.HasPrefix(m.Content, "@everyone you have a lot of courseworks to do!"){
		pinLastMessage(m,s)
	}
	if strings.HasPrefix(m.Content, "!meme"){
		//fmt.Print(getMemes("","",""))
		s.ChannelMessageSend(m.ChannelID, getMemes(text))
	}
	if strings.HasPrefix(m.Content, "!google"){
		fmt.Print("Hey "+googleForMe(text))
		s.ChannelMessageSend(m.ChannelID,googleForMe(text))
	}
	if strings.HasPrefix(m.Content, "!flip"){
		s.ChannelMessageSend(m.ChannelID, flip())
	}

}
// This function will be called (due to AddHandler above) every time a new
// guild is joined.+
func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) {
	if event.Guild.Unavailable {
		return
	}

	for _, channel := range event.Guild.Channels {
		if channel.ID == event.Guild.ID {
			//_, _ = s.ChannelMessageSend(channel.ID, "Kawaii is ready! Type !info to get some usefull shit about me.")
			return
		}
	}
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}
func newMember(s *discordgo.Session, event *discordgo.GuildMemberAdd){
	s.ChannelMessageSend("238048572413575168", "New member joined. Welcome @" + event.Member.User.Username)
	fmt.Print("New member joined. Welcome *@" + event.Member.User.Username+"*")

}
func readCal(path string) (string, int){
	var a = ""
	var i = 1
	parser := ics.New()
	parserChan := parser.GetInputChan()
	parserChan <- path

	// wait to kill the main goroute
	parser.Wait()
	cal, err := parser.GetCalendars()
	if err == nil {
		for _, calendar := range cal {
			event := calendar.GetEvents()
			a= a+ fmt.Sprintf("```")
			for _, events := range event {
				// print event info (event implements Stringer interface)
				if time.Now().Before(events.GetEnd()){
					a=a+fmt.Sprintf("%d) %s\t DEADLINE: %d/%d/%d\n", i,events.GetSummary(), events.GetEnd().Day(),events.GetEnd().Month(),events.GetEnd().Year())
					i++
				}

			}
			a = a + fmt.Sprintf("```")
		}
	}
	return a, i
}
func readLines(path string) ([]string, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	return lines, scanner.Err()
}

func eightball(text string) string {
	answer := eightballResponses[rand.Intn(len(eightballResponses))]

	if len(text) > 7 {
		question := text[7:]

		return fmt.Sprintf(":question:`Question:` *%s* \n:8ball:`8Ball answer:` **%s**", question, answer)
	}

	return answer
}

func pinLastMessage(m *discordgo.MessageCreate, s *discordgo.Session){
	s.ChannelMessagePin(m.ChannelID, m.ID)
}

func getMemes(text string) (resp string){

	id := strings.Fields(strings.TrimSpace(text))
	id[1] = strings.Replace(id[1],"_", "-",1)
	id[2] = strings.Replace(id[2],"_", "-",1)
	resp = "https://memegen.link/"+id[1]+"/"+id[2]+"/"+id[3]+".jpg"
	return resp
}

func googleForMe(text string) string{
	tmp:= text[8:]
	link:=strings.Replace(tmp," ", "+", 1000)
	request:="http://lmgtfy.com/?q="+link
	return request

}

func wankWheel(m *discordgo.MessageCreate) string{
	if(strings.Compare(m.Author.ID,"238046128292102145")==0){
		return "Time for some Gay Porn, man"
	}else{
		k := rand.NewSource(time.Now().Unix())
		r := rand.New(k) // initialize local pseudorandom generator
		return "Time for some "+ wankOpions[r.Intn(len(lines))]+" Porn, man"
	}
}
func flip() string{
	k := rand.NewSource(time.Now().Unix())
	r := rand.New(k) // initialize local pseudorandom generator
	return ":regional_indicator_f: :regional_indicator_l: :regional_indicator_i: :regional_indicator_p:: `" + coin[r.Intn(len(coin))] + "`"
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"

	"github.com/google/gops/agent"
	"github.com/julienschmidt/httprouter"
	log "github.com/meifamily/logrus"
	"github.com/robfig/cron"

	ctrlr "github.com/meifamily/ptt-alertor/controllers"
	"github.com/meifamily/ptt-alertor/jobs"
	"github.com/meifamily/ptt-alertor/line"
	"github.com/meifamily/ptt-alertor/messenger"
	"github.com/meifamily/ptt-alertor/myutil"
	"github.com/meifamily/ptt-alertor/telegram"
)

var auth map[string]string
var telegramToken string

type myRouter struct {
	httprouter.Router
}

func (mr myRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.WithFields(log.Fields{
		"method": r.Method,
		"IP":     r.RemoteAddr,
		"URI":    r.URL.Path,
	}).Info("visit")
	mr.Router.ServeHTTP(w, r)
}

func newRouter() *myRouter {
	return &myRouter{
		Router: *httprouter.New(),
	}
}

func basicAuth(handle httprouter.Handle) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
		user, password, hasAuth := r.BasicAuth()
		if hasAuth && user == auth["user"] && password == auth["password"] {
			handle(w, r, params)
		} else {
			w.Header().Set("WWW-Authenticate", "Basic realm=Restricted")
			http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		}
	}
}

func main() {
	log.Info("Start Jobs")
	startJobs()
	// Web Server
	log.Info("Web Server Start on Port 9090")

	router := newRouter()
	router.NotFound = http.FileServer(http.Dir("public"))
	m := messenger.New()

	router.GET("/", ctrlr.Index)
	router.GET("/messenger", ctrlr.MessengerIndex)
	router.GET("/line", ctrlr.LineIndex)
	router.GET("/telegram", ctrlr.TelegramIndex)
	router.GET("/redirect/:checksum", ctrlr.Redirect)
	router.GET("/top", ctrlr.Top)
	router.GET("/docs", ctrlr.Docs)

	// websocket
	router.GET("/ws", ctrlr.WebSocket)

	router.POST("/broadcast", basicAuth(ctrlr.Broadcast))

	// boards apis
	router.GET("/boards/:boardName/articles/:code", ctrlr.BoardArticle)
	router.GET("/boards/:boardName/articles", ctrlr.BoardArticleIndex)
	router.GET("/boards", ctrlr.BoardIndex)

	// keyword apis
	router.GET("/keyword/boards", ctrlr.KeywordBoards)

	// author apis
	router.GET("/author/boards", ctrlr.AuthorBoards)

	// pushsum apis
	router.GET("/pushsum/boards", ctrlr.PushSumBoards)

	// articles apis
	router.GET("/articles", ctrlr.ArticleIndex)

	// users apis
	router.GET("/users/:account", basicAuth(ctrlr.UserFind))
	router.GET("/users", basicAuth(ctrlr.UserAll))
	router.POST("/users", basicAuth(ctrlr.UserCreate))
	router.PUT("/users/:account", basicAuth(ctrlr.UserModify))

	// line
	router.POST("/line/callback", line.HandleRequest)
	router.POST("/line/notify/callback", line.CatchCallback)

	// facebook messenger
	router.GET("/messenger/webhook", m.Verify)
	router.POST("/messenger/webhook", m.Received)

	// telegram
	router.POST("/telegram/"+telegramToken, telegram.HandleRequest)

	// gops agent
	if err := agent.Listen(&agent.Options{Addr: ":6060", NoShutdownCleanup: false}); err != nil {
		log.Fatal(err)
	}

	err := http.ListenAndServe(":9090", router)
	if err != nil {
		log.Fatal("ListenAndServer ", err)
	}

}

func startJobs() {
	go jobs.NewChecker().Run()
	go jobs.NewPushSumChecker().Run()
	go jobs.NewPushListChecker().Run()
	go jobs.NewPttMonitor().Run()
	c := cron.New()
	c.AddJob("@hourly", jobs.NewTop())
	c.AddJob("@every 3d", jobs.NewPushSumKeyReplacer())
	c.Start()
}

func init() {
	auth = myutil.Config("auth")
	telegramToken = myutil.Config("telegram")["token"]
	// for initial app
	// jobs.NewTop().Run()
	// jobs.NewCacheCleaner().Run()
	// jobs.NewGenerator().Run()
	// jobs.NewFetcher().Run()
}
<commit_msg>refactor<commit_after>package main

import (
	"net/http"

	"github.com/google/gops/agent"
	"github.com/julienschmidt/httprouter"
	log "github.com/meifamily/logrus"
	"github.com/robfig/cron"

	ctrlr "github.com/meifamily/ptt-alertor/controllers"
	"github.com/meifamily/ptt-alertor/jobs"
	"github.com/meifamily/ptt-alertor/line"
	"github.com/meifamily/ptt-alertor/messenger"
	"github.com/meifamily/ptt-alertor/myutil"
	"github.com/meifamily/ptt-alertor/telegram"
)

var auth map[string]string
var telegramToken string

type myRouter struct {
	httprouter.Router
}

func (mr myRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log.WithFields(log.Fields{
		"method": r.Method,
		"IP":     r.RemoteAddr,
		"URI":    r.URL.Path,
	}).Info("visit")
	mr.Router.ServeHTTP(w, r)
}

func newRouter() *myRouter {
	r := &myRouter{
		Router: *httprouter.New(),
	}
	r.NotFound = http.FileServer(http.Dir("public"))
	return r
}

func basicAuth(handle httprouter.Handle) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
		user, password, hasAuth := r.BasicAuth()
		if hasAuth && user == auth["user"] && password == auth["password"] {
			handle(w, r, params)
		} else {
			w.Header().Set("WWW-Authenticate", "Basic realm=Restricted")
			http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
		}
	}
}

func main() {
	log.Info("Start Jobs")
	startJobs()

	router := newRouter()
	m := messenger.New()

	router.GET("/", ctrlr.Index)
	router.GET("/messenger", ctrlr.MessengerIndex)
	router.GET("/line", ctrlr.LineIndex)
	router.GET("/telegram", ctrlr.TelegramIndex)
	router.GET("/redirect/:checksum", ctrlr.Redirect)
	router.GET("/top", ctrlr.Top)
	router.GET("/docs", ctrlr.Docs)

	// websocket
	router.GET("/ws", ctrlr.WebSocket)

	router.POST("/broadcast", basicAuth(ctrlr.Broadcast))

	// boards apis
	router.GET("/boards/:boardName/articles/:code", ctrlr.BoardArticle)
	router.GET("/boards/:boardName/articles", ctrlr.BoardArticleIndex)
	router.GET("/boards", ctrlr.BoardIndex)

	// keyword apis
	router.GET("/keyword/boards", ctrlr.KeywordBoards)

	// author apis
	router.GET("/author/boards", ctrlr.AuthorBoards)

	// pushsum apis
	router.GET("/pushsum/boards", ctrlr.PushSumBoards)

	// articles apis
	router.GET("/articles", ctrlr.ArticleIndex)

	// users apis
	router.GET("/users/:account", basicAuth(ctrlr.UserFind))
	router.GET("/users", basicAuth(ctrlr.UserAll))
	router.POST("/users", basicAuth(ctrlr.UserCreate))
	router.PUT("/users/:account", basicAuth(ctrlr.UserModify))

	// line
	router.POST("/line/callback", line.HandleRequest)
	router.POST("/line/notify/callback", line.CatchCallback)

	// facebook messenger
	router.GET("/messenger/webhook", m.Verify)
	router.POST("/messenger/webhook", m.Received)

	// telegram
	router.POST("/telegram/"+telegramToken, telegram.HandleRequest)

	// gops agent
	if err := agent.Listen(&agent.Options{Addr: ":6060", NoShutdownCleanup: false}); err != nil {
		log.Fatal(err)
	}

	// Web Server
	log.Info("Web Server Start on Port 9090")
	err := http.ListenAndServe(":9090", router)
	if err != nil {
		log.Fatal("ListenAndServer ", err)
	}

}

func startJobs() {
	go jobs.NewChecker().Run()
	go jobs.NewPushSumChecker().Run()
	go jobs.NewPushListChecker().Run()
	go jobs.NewPttMonitor().Run()
	c := cron.New()
	c.AddJob("@hourly", jobs.NewTop())
	c.AddJob("@every 3d", jobs.NewPushSumKeyReplacer())
	c.Start()
}

func init() {
	auth = myutil.Config("auth")
	telegramToken = myutil.Config("telegram")["token"]
	// for initial app
	// jobs.NewTop().Run()
	// jobs.NewCacheCleaner().Run()
	// jobs.NewGenerator().Run()
	// jobs.NewFetcher().Run()
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"errors"
	"flag"
	"hash/crc32"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"sync"
	"time"

	"qiniupkg.com/http/httputil.v2"
	"qiniupkg.com/x/log.v7"
)

const (
	mutexCount = 9973
)

var (
	ErrUnmatchedInodeType = errors.New("unmatched inode type(file or dir)")
	ErrRefreshWithoutPath = httputil.NewError(400, "refresh without path")
	ErrInvalidPkgPath     = httputil.NewError(400, "invalid package path")
)

var (
	doxygenApp string

	dataRootDir string
	srcRootDir  string
	tmpRootDir  string

	refreshRootDir string

	mutexs [mutexCount]sync.Mutex
)

func handleHome(w http.ResponseWriter, req *http.Request) {

}

func handleUnknown(w http.ResponseWriter, req *http.Request) {

}

func handleRefresh(w http.ResponseWriter, req *http.Request) {

	pkg := req.PostFormValue("path")
	if pkg == "" {
		httputil.Error(w, ErrRefreshWithoutPath)
		return
	}

	log.Info("Refresh", pkg)

	err := refresh(pkg)
	if err != nil {
		httputil.Error(w, err)
		return
	}

	http.Redirect(w, req, "/" + pkg + "/", 301)
}

func refresh(pkg string) (err error) {

	dataDir := dataRootDir + pkg
	indexFile := dataDir + "/html/index.html"
	if isRefreshed(indexFile) {
		return nil
	}

	if strings.Index(pkg, "..") >= 0 {
		return ErrInvalidPkgPath
	}

	parts := strings.SplitN(pkg, "/", 4)
	if len(parts) != 3 {
		return ErrInvalidPkgPath
	}

	refreshDir := refreshRootDir + pkg
	refreshHtmlDir := refreshDir + "/html/"
	os.RemoveAll(refreshDir)
	err = genDoc(parts, pkg, refreshDir, refreshHtmlDir)
	if err != nil {
		return
	}

	os.RemoveAll(dataDir)
	return os.Rename(refreshDir, dataDir)
}

func isRefreshed(indexFile string) bool {

	fi, err := os.Stat(indexFile)
	if err != nil {
		return false
	}

	return time.Now().Sub(fi.ModTime()) < 10*time.Second
}

func handleMain(w http.ResponseWriter, req *http.Request) {

	path := req.URL.Path

	if path == "/" {
		handleHome(w, req)
		return
	}

	log.Info("View", path)

	if strings.Index(path, "..") >= 0 {
		handleUnknown(w, req)
		return
	}

	parts := strings.SplitN(path[1:], "/", 4)
	if parts[0] != "github.com" || len(parts) < 3 {
		handleUnknown(w, req)
		return
	}

	pkg := strings.Join(parts[:3], "/")
	dataDir := dataRootDir + pkg
	htmlDir := dataDir + "/html/"
	err := isEntryExists(htmlDir, true)
	if err != nil {
		err = genDoc(parts, pkg, dataDir, htmlDir)
		if err != nil {
			httputil.Error(w, err)
			return
		}
	}

	if len(parts) > 3 {
		file := htmlDir + parts[3]
		if strings.HasSuffix(file, "/") {
			file += "index.html"
		}
		f, err := os.Open(file)
		if err != nil {
			httputil.Error(w, err)
			return
		}
		defer f.Close()
		fi, err := f.Stat()
		if err != nil {
			httputil.Error(w, err)
			return
		}
		serveContent(w, req, pkg, fi.Name(), fi.ModTime(), f)
	} else {
		http.Redirect(w, req, path + "/", 301)
	}
}

func genDoc(parts []string, pkg, dataDir, htmlDir string) (err error) {

	srcDir := srcRootDir + pkg
	repo := "https://github.com/" + parts[1] + "/" + parts[2] + ".git"

	crc := crc32.ChecksumIEEE([]byte(pkg))
	mutex := &mutexs[crc % mutexCount]
	mutex.Lock()
	defer mutex.Unlock()

	err2 := isEntryExists(htmlDir, true)
	if err2 != nil {
		err = cloneRepo(srcDir, repo)
		if err != nil {
			return
		}

		err = os.MkdirAll(dataDir, 0755)
		if err != nil {
			return
		}

		doxyfile := tmpRootDir + "github.com!" + parts[1] + "!" + parts[2] + ".doxyfile"
		err = genDoxyfileFile(doxyfile, &doxyfileConf{
			ProjectName:  parts[2],
			OutputDir:    dataDir,
			InputDir:     srcDir,
			FilePatterns: "*.java *.h *.hpp *.hxx *.py *.php *.rb *.cs *.js *.scala *.go *.lua *.asp",
		})
		if err != nil {
			return
		}

		err = runCmd(doxygenApp, doxyfile)
		if err != nil {
			return
		}
	}
	return
}

func cloneRepo(srcDir string, repo string) (err error) {

	os.RemoveAll(srcDir)
	err = os.MkdirAll(srcDir, 0755)
	if err != nil {
		return
	}

	return runCmd("git", "clone", "--depth=50", repo, srcDir)
}

func runCmd(command string, args ...string) (err error) {

	cmd := exec.Command(command, args...)

	var out bytes.Buffer
	cmd.Stderr = &out

	err = cmd.Run()
	if err == nil {
		return
	}

	emsg := out.String()
	if emsg != "" {
		return errors.New(emsg)
	}
	return err
}

func isEntryExists(entryPath string, isDir bool) (err error) {

	fi, err := os.Stat(entryPath)
	if err != nil {
		return
	}

	if fi.IsDir() != isDir {
		err = ErrUnmatchedInodeType
		return
	}
	return nil
}

var (
	bindHost = flag.String("http", ":8888", "address that doxygen.io server listen")
)

func main() {

	flag.Parse()

	rootDir := os.Getenv("HOME") + "/.doxygen.io/"
	doxygenApp = os.Getenv("DOXYGEN")
	if doxygenApp == "" {
		doxygenApp = "doxygen"
	}

	dataRootDir = rootDir + "data/"
	refreshRootDir = rootDir + "refresh/"
	srcRootDir = rootDir + "src/"
	tmpRootDir = rootDir + "tmp/"
	os.MkdirAll(tmpRootDir, 0755)

	http.HandleFunc("/-/refresh", handleRefresh)
	http.HandleFunc("/", handleMain)
	err := http.ListenAndServe(*bindHost, nil)
	if err != nil {
		log.Fatal(err)
	}
}

<commit_msg>markdown<commit_after>package main

import (
	"bytes"
	"errors"
	"flag"
	"hash/crc32"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"sync"
	"time"

	"qiniupkg.com/http/httputil.v2"
	"qiniupkg.com/x/log.v7"
)

const (
	mutexCount = 9973
)

var (
	ErrUnmatchedInodeType = errors.New("unmatched inode type(file or dir)")
	ErrRefreshWithoutPath = httputil.NewError(400, "refresh without path")
	ErrInvalidPkgPath     = httputil.NewError(400, "invalid package path")
)

var (
	doxygenApp string

	dataRootDir string
	srcRootDir  string
	tmpRootDir  string

	refreshRootDir string

	mutexs [mutexCount]sync.Mutex
)

func handleHome(w http.ResponseWriter, req *http.Request) {

}

func handleUnknown(w http.ResponseWriter, req *http.Request) {

}

func handleRefresh(w http.ResponseWriter, req *http.Request) {

	pkg := req.PostFormValue("path")
	if pkg == "" {
		httputil.Error(w, ErrRefreshWithoutPath)
		return
	}

	log.Info("Refresh", pkg)

	err := refresh(pkg)
	if err != nil {
		httputil.Error(w, err)
		return
	}

	http.Redirect(w, req, "/" + pkg + "/", 301)
}

func refresh(pkg string) (err error) {

	dataDir := dataRootDir + pkg
	indexFile := dataDir + "/html/index.html"
	if isRefreshed(indexFile) {
		return nil
	}

	if strings.Index(pkg, "..") >= 0 {
		return ErrInvalidPkgPath
	}

	parts := strings.SplitN(pkg, "/", 4)
	if len(parts) != 3 {
		return ErrInvalidPkgPath
	}

	refreshDir := refreshRootDir + pkg
	refreshHtmlDir := refreshDir + "/html/"
	os.RemoveAll(refreshDir)
	err = genDoc(parts, pkg, refreshDir, refreshHtmlDir)
	if err != nil {
		return
	}

	os.RemoveAll(dataDir)
	return os.Rename(refreshDir, dataDir)
}

func isRefreshed(indexFile string) bool {

	fi, err := os.Stat(indexFile)
	if err != nil {
		return false
	}

	return time.Now().Sub(fi.ModTime()) < 10*time.Second
}

func handleMain(w http.ResponseWriter, req *http.Request) {

	path := req.URL.Path

	if path == "/" {
		handleHome(w, req)
		return
	}

	log.Info("View", path)

	if strings.Index(path, "..") >= 0 {
		handleUnknown(w, req)
		return
	}

	parts := strings.SplitN(path[1:], "/", 4)
	if parts[0] != "github.com" || len(parts) < 3 {
		handleUnknown(w, req)
		return
	}

	pkg := strings.Join(parts[:3], "/")
	dataDir := dataRootDir + pkg
	htmlDir := dataDir + "/html/"
	err := isEntryExists(htmlDir, true)
	if err != nil {
		err = genDoc(parts, pkg, dataDir, htmlDir)
		if err != nil {
			httputil.Error(w, err)
			return
		}
	}

	if len(parts) > 3 {
		file := htmlDir + parts[3]
		if strings.HasSuffix(file, "/") {
			file += "index.html"
		}
		f, err := os.Open(file)
		if err != nil {
			httputil.Error(w, err)
			return
		}
		defer f.Close()
		fi, err := f.Stat()
		if err != nil {
			httputil.Error(w, err)
			return
		}
		serveContent(w, req, pkg, fi.Name(), fi.ModTime(), f)
	} else {
		http.Redirect(w, req, path + "/", 301)
	}
}

func genDoc(parts []string, pkg, dataDir, htmlDir string) (err error) {

	srcDir := srcRootDir + pkg
	repo := "https://github.com/" + parts[1] + "/" + parts[2] + ".git"

	crc := crc32.ChecksumIEEE([]byte(pkg))
	mutex := &mutexs[crc % mutexCount]
	mutex.Lock()
	defer mutex.Unlock()

	err2 := isEntryExists(htmlDir, true)
	if err2 != nil {
		err = cloneRepo(srcDir, repo)
		if err != nil {
			return
		}

		err = os.MkdirAll(dataDir, 0755)
		if err != nil {
			return
		}

		doxyfile := tmpRootDir + "github.com!" + parts[1] + "!" + parts[2] + ".doxyfile"
		err = genDoxyfileFile(doxyfile, &doxyfileConf{
			ProjectName:  parts[2],
			OutputDir:    dataDir,
			InputDir:     srcDir,
			FilePatterns: "*.md *.java *.h *.hpp *.hxx *.py *.php *.rb *.cs *.js *.scala *.go *.lua *.asp",
		})
		if err != nil {
			return
		}

		err = runCmd(doxygenApp, doxyfile)
		if err != nil {
			return
		}
	}
	return
}

func cloneRepo(srcDir string, repo string) (err error) {

	os.RemoveAll(srcDir)
	err = os.MkdirAll(srcDir, 0755)
	if err != nil {
		return
	}

	return runCmd("git", "clone", "--depth=50", repo, srcDir)
}

func runCmd(command string, args ...string) (err error) {

	cmd := exec.Command(command, args...)

	var out bytes.Buffer
	cmd.Stderr = &out

	err = cmd.Run()
	if err == nil {
		return
	}

	emsg := out.String()
	if emsg != "" {
		return errors.New(emsg)
	}
	return err
}

func isEntryExists(entryPath string, isDir bool) (err error) {

	fi, err := os.Stat(entryPath)
	if err != nil {
		return
	}

	if fi.IsDir() != isDir {
		err = ErrUnmatchedInodeType
		return
	}
	return nil
}

var (
	bindHost = flag.String("http", ":8888", "address that doxygen.io server listen")
)

func main() {

	flag.Parse()

	rootDir := os.Getenv("HOME") + "/.doxygen.io/"
	doxygenApp = os.Getenv("DOXYGEN")
	if doxygenApp == "" {
		doxygenApp = "doxygen"
	}

	dataRootDir = rootDir + "data/"
	refreshRootDir = rootDir + "refresh/"
	srcRootDir = rootDir + "src/"
	tmpRootDir = rootDir + "tmp/"
	os.MkdirAll(tmpRootDir, 0755)

	http.HandleFunc("/-/refresh", handleRefresh)
	http.HandleFunc("/", handleMain)
	err := http.ListenAndServe(*bindHost, nil)
	if err != nil {
		log.Fatal(err)
	}
}

<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"path"
)

type blob struct {
	Path     string   `json:"path"`
	Files    []string `json:"files,omitempty"`
	Dirs     []string `json:"dirs,omitempty"`
	Contents *string  `json:"contents,omitempty"`
	Err      string   `json:"err,omitempty"`
	Mode     string   `json:"mode,omitempty"`
}

var BUFMAX = 1024 * 1024 * 4
var DIRMAX = 1024

func readFile(path string) (rval *blob) {
	rval = new(blob)

	fd, err := os.Open(path)

	if err != nil {
		rval.Err = err.Error()
		return
	}

	buf := make([]byte, BUFMAX)
	length, err := fd.Read(buf)
	if err != nil {
		rval.Err = err.Error()
		return
	}
	contents := string(buf[:length])
	rval.Contents = &contents
	return
}

func readDir(path string) (rval *blob) {
	rval = new(blob)

	fd, err := os.Open(path)

	if err != nil {
		fmt.Println("not a valid path", err)
		rval.Err = err.Error()
		return
	}
	files, err := fd.Readdir(DIRMAX)
	if err != nil {
		rval.Err = err.Error()
		return
	}

	// Not ideal
	rval.Files = make([]string, 0, len(files))
	rval.Dirs = make([]string, 0, len(files))
	for _, literal := range files {
		if literal.IsDir() {
			rval.Dirs = append(rval.Dirs, literal.Name())
		} else {
			rval.Files = append(rval.Files, literal.Name())
		}
	}
	return
}

func readPath(path string) (rval *blob) {
	rval = &blob{Path: path}
	fileinfo, err := os.Stat(path)

	if err != nil {
		rval.Err = err.Error()
		return
	}
	rval.Mode = fileinfo.Mode().String()

	if fileinfo.Mode().IsRegular() {
		latest := readFile(path)
		rval.Contents = latest.Contents
		rval.Err = latest.Err
	} else if fileinfo.Mode().IsDir() {
		latest := readDir(path)
		rval.Files = latest.Files
		rval.Dirs = latest.Dirs
		rval.Err = latest.Err
	}
	return
}

func main() {
	listen := flag.String("listen", ":9234", "What to listen on- you should prefer to bind to a local interface, like 10.0.1.3:9234")

	flag.Parse()

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		b := readPath(path.Join("/proc", r.URL.Path))
		b_str, err := json.Marshal(*b)
		if err != nil {
			log.Println("marshalling error", err, b)
		}
		fmt.Fprintf(w, string(b_str))
	})

	log.Fatal(http.ListenAndServe(*listen, nil))
}
<commit_msg>Set HTTP error code on error<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"path"
)

type blob struct {
	Path     string   `json:"path"`
	Files    []string `json:"files,omitempty"`
	Dirs     []string `json:"dirs,omitempty"`
	Contents *string  `json:"contents,omitempty"`
	Err      string   `json:"err,omitempty"`
	Mode     string   `json:"mode,omitempty"`
}

var BUFMAX = 1024 * 1024 * 4
var DIRMAX = 1024

func readFile(path string) (rval *blob) {
	rval = new(blob)

	fd, err := os.Open(path)

	if err != nil {
		rval.Err = err.Error()
		return
	}

	buf := make([]byte, BUFMAX)
	length, err := fd.Read(buf)
	if err != nil {
		rval.Err = err.Error()
		return
	}
	contents := string(buf[:length])
	rval.Contents = &contents
	return
}

func readDir(path string) (rval *blob) {
	rval = new(blob)

	fd, err := os.Open(path)

	if err != nil {
		fmt.Println("not a valid path", err)
		rval.Err = err.Error()
		return
	}
	files, err := fd.Readdir(DIRMAX)
	if err != nil {
		rval.Err = err.Error()
		return
	}

	// Not ideal
	rval.Files = make([]string, 0, len(files))
	rval.Dirs = make([]string, 0, len(files))
	for _, literal := range files {
		if literal.IsDir() {
			rval.Dirs = append(rval.Dirs, literal.Name())
		} else {
			rval.Files = append(rval.Files, literal.Name())
		}
	}
	return
}

func readPath(path string) (rval *blob) {
	rval = &blob{Path: path}
	fileinfo, err := os.Stat(path)

	if err != nil {
		rval.Err = err.Error()
		return
	}
	rval.Mode = fileinfo.Mode().String()

	if fileinfo.Mode().IsRegular() {
		latest := readFile(path)
		rval.Contents = latest.Contents
		rval.Err = latest.Err
	} else if fileinfo.Mode().IsDir() {
		latest := readDir(path)
		rval.Files = latest.Files
		rval.Dirs = latest.Dirs
		rval.Err = latest.Err
	}
	return
}

func main() {
	listen := flag.String("listen", ":9234", "What to listen on- you should prefer to bind to a local interface, like 10.0.1.3:9234")

	flag.Parse()

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		b := readPath(path.Join("/proc", r.URL.Path))
		b_str, err := json.Marshal(*b)
		if err != nil {
			log.Println("marshalling error", err, b)
		}
		if err != nil || b.Err != "" {
			w.WriteHeader(http.StatusInternalServerError)
		}
		fmt.Fprintf(w, string(b_str))
	})

	log.Fatal(http.ListenAndServe(*listen, nil))
}
<|endoftext|>


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

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"time"

	"github.com/googlecloudplatform/gcsfuse/fs"
	"github.com/googlecloudplatform/gcsfuse/mount"
	"github.com/googlecloudplatform/gcsfuse/timeutil"
	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fsutil"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcscaching"
	"golang.org/x/net/context"
)

var fBucketName = flag.String("bucket", "", "Name of GCS bucket to mount.")
var fMountPoint = flag.String("mount_point", "", "File system location.")

var fMountOptions = mount.OptionFlag(
	"o",
	"Additional system-specific mount options. Be careful!")

var fFileMode = flag.Uint(
	"file_mode",
	0644,
	"Permissions bits for files. Default is 0644.")

var fDirMode = flag.Uint(
	"dir_mode",
	0755,
	"Permissions bits for directories. Default is 0755.")

var fTempDir = flag.String(
	"temp_dir", "",
	"The temporary directory in which to store local copies of GCS objects. "+
		"If empty, the system default (probably /tmp) will be used.")

var fTempDirLimit = flag.Int64(
	"temp_dir_bytes", 1<<31,
	"A desired limit on the number of bytes used in --temp_dir. May be exceeded "+
		"for dirty files that have not been flushed or closed.")

var fGCSChunkSize = flag.Uint64(
	"gcs_chunk_size", 1<<24,
	"If set to a non-zero value N, split up GCS objects into multiple chunks of "+
		"size at most N when reading, and do not read or cache unnecessary chunks.")

var fImplicitDirs = flag.Bool(
	"implicit_dirs",
	false,
	"Implicitly define directories based on their content. See "+
		"docs/semantics.md.")

var fSupportNlink = flag.Bool(
	"support_nlink",
	false,
	"Return meaningful values for nlink from fstat(2). See docs/semantics.md.")

var fStatCacheTTL = flag.Duration(
	"stat_cache_ttl",
	time.Minute,
	"How long to cache StatObject results from GCS.")

var fTypeCacheTTL = flag.Duration(
	"type_cache_ttl",
	time.Minute,
	"How long to cache name -> file/dir type mappings in directory inodes.")

func getBucketName() string {
	s := *fBucketName
	if s == "" {
		fmt.Println("You must set --bucket.")
		os.Exit(1)
	}

	return s
}

func registerSIGINTHandler(mountPoint string) {
	// Register for SIGINT.
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt)

	// Start a goroutine that will unmount when the signal is received.
	go func() {
		for {
			<-signalChan
			log.Println("Received SIGINT, attempting to unmount...")

			err := fuse.Unmount(mountPoint)
			if err != nil {
				log.Printf("Failed to unmount in response to SIGINT: %v", err)
			} else {
				log.Printf("Successfully unmounted in response to SIGINT.")
				return
			}
		}
	}()
}

func getBucket() (b gcs.Bucket) {
	// Set up a GCS connection.
	log.Println("Initializing GCS connection.")
	conn, err := getConn()
	if err != nil {
		log.Fatal("Couldn't get GCS connection: ", err)
	}

	// Extract the appropriate bucket.
	b = conn.GetBucket(getBucketName())

	// Enable cached StatObject results, if appropriate.
	if *fStatCacheTTL != 0 {
		const cacheCapacity = 4096
		b = gcscaching.NewFastStatBucket(
			*fStatCacheTTL,
			gcscaching.NewStatCache(cacheCapacity),
			timeutil.RealClock(),
			b)
	}

	return
}

func main() {
	// Make logging output better.
	log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)

	// Set up flags.
	flag.Parse()

	// Check --mount_point.
	if *fMountPoint == "" {
		log.Fatalf("You must set --mount_point.")
	}

	mountPoint := *fMountPoint

	// Sanity check: make sure the temporary directory exists and is writable
	// currently. This gives a better user experience than harder to debug EIO
	// errors when reading files in the future.
	if *fTempDir != "" {
		f, err := fsutil.AnonymousFile(*fTempDir)
		f.Close()

		if err != nil {
			log.Fatalf(
				"Error writing to temporary directory (%q); are you sure it exists "+
					"with the correct permissions?",
				err.Error())
		}
	}

	// Create a file system server.
	bucket := getBucket()
	serverCfg := &fs.ServerConfig{
		Clock:               timeutil.RealClock(),
		Bucket:              bucket,
		TempDir:             *fTempDir,
		TempDirLimit:        *fTempDirLimit,
		GCSChunkSize:        *fGCSChunkSize,
		ImplicitDirectories: *fImplicitDirs,
		SupportNlink:        *fSupportNlink,
		DirTypeCacheTTL:     *fTypeCacheTTL,
		FilePerms:           os.FileMode(*fFileMode),
		DirPerms:            os.FileMode(*fDirMode),
	}

	server, err := fs.NewServer(serverCfg)
	if err != nil {
		log.Fatal("fs.NewServer:", err)
	}

	// Mount the file system.
	mountCfg := &fuse.MountConfig{
		FSName:  bucket.Name(),
		Options: fMountOptions,
	}

	mountedFS, err := fuse.Mount(mountPoint, server, mountCfg)
	if err != nil {
		log.Fatal("Mount:", err)
	}

	log.Println("File system has been successfully mounted.")

	// Let the user unmount with Ctrl-C (SIGINT).
	registerSIGINTHandler(mountedFS.Dir())

	// Wait for it to be unmounted.
	if err := mountedFS.Join(context.Background()); err != nil {
		log.Fatal("MountedFileSystem.Join:", err)
	}

	log.Println("Successfully exiting.")
}
<commit_msg>Added gcsfuse flags.<commit_after>// Copyright 2015 Google Inc. 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 main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"time"

	"github.com/googlecloudplatform/gcsfuse/fs"
	"github.com/googlecloudplatform/gcsfuse/mount"
	"github.com/googlecloudplatform/gcsfuse/perms"
	"github.com/googlecloudplatform/gcsfuse/timeutil"
	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fsutil"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcscaching"
	"golang.org/x/net/context"
)

var fBucketName = flag.String("bucket", "", "Name of GCS bucket to mount.")
var fMountPoint = flag.String("mount_point", "", "File system location.")

var fMountOptions = mount.OptionFlag(
	"o",
	"Additional system-specific mount options. Be careful!")

var fUid = flag.Int64(
	"uid",
	-1,
	"If non-negative, the UID that owns all inodes. The default is the UID of "+
		"the gcsfuse process.")

var fGid = flag.Int64(
	"gid",
	-1,
	"If non-negative, the GID that owns all inodes. The default is the GID of "+
		"the gcsfuse process.")

var fFileMode = flag.Uint(
	"file_mode",
	0644,
	"Permissions bits for files. Default is 0644.")

var fDirMode = flag.Uint(
	"dir_mode",
	0755,
	"Permissions bits for directories. Default is 0755.")

var fTempDir = flag.String(
	"temp_dir", "",
	"The temporary directory in which to store local copies of GCS objects. "+
		"If empty, the system default (probably /tmp) will be used.")

var fTempDirLimit = flag.Int64(
	"temp_dir_bytes", 1<<31,
	"A desired limit on the number of bytes used in --temp_dir. May be exceeded "+
		"for dirty files that have not been flushed or closed.")

var fGCSChunkSize = flag.Uint64(
	"gcs_chunk_size", 1<<24,
	"If set to a non-zero value N, split up GCS objects into multiple chunks of "+
		"size at most N when reading, and do not read or cache unnecessary chunks.")

var fImplicitDirs = flag.Bool(
	"implicit_dirs",
	false,
	"Implicitly define directories based on their content. See "+
		"docs/semantics.md.")

var fSupportNlink = flag.Bool(
	"support_nlink",
	false,
	"Return meaningful values for nlink from fstat(2). See docs/semantics.md.")

var fStatCacheTTL = flag.Duration(
	"stat_cache_ttl",
	time.Minute,
	"How long to cache StatObject results from GCS.")

var fTypeCacheTTL = flag.Duration(
	"type_cache_ttl",
	time.Minute,
	"How long to cache name -> file/dir type mappings in directory inodes.")

func getBucketName() string {
	s := *fBucketName
	if s == "" {
		fmt.Println("You must set --bucket.")
		os.Exit(1)
	}

	return s
}

func registerSIGINTHandler(mountPoint string) {
	// Register for SIGINT.
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt)

	// Start a goroutine that will unmount when the signal is received.
	go func() {
		for {
			<-signalChan
			log.Println("Received SIGINT, attempting to unmount...")

			err := fuse.Unmount(mountPoint)
			if err != nil {
				log.Printf("Failed to unmount in response to SIGINT: %v", err)
			} else {
				log.Printf("Successfully unmounted in response to SIGINT.")
				return
			}
		}
	}()
}

func getBucket() (b gcs.Bucket) {
	// Set up a GCS connection.
	log.Println("Initializing GCS connection.")
	conn, err := getConn()
	if err != nil {
		log.Fatal("Couldn't get GCS connection: ", err)
	}

	// Extract the appropriate bucket.
	b = conn.GetBucket(getBucketName())

	// Enable cached StatObject results, if appropriate.
	if *fStatCacheTTL != 0 {
		const cacheCapacity = 4096
		b = gcscaching.NewFastStatBucket(
			*fStatCacheTTL,
			gcscaching.NewStatCache(cacheCapacity),
			timeutil.RealClock(),
			b)
	}

	return
}

func main() {
	// Make logging output better.
	log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)

	// Set up flags.
	flag.Parse()

	// Check --mount_point.
	if *fMountPoint == "" {
		log.Fatalf("You must set --mount_point.")
	}

	mountPoint := *fMountPoint

	// Sanity check: make sure the temporary directory exists and is writable
	// currently. This gives a better user experience than harder to debug EIO
	// errors when reading files in the future.
	if *fTempDir != "" {
		f, err := fsutil.AnonymousFile(*fTempDir)
		f.Close()

		if err != nil {
			log.Fatalf(
				"Error writing to temporary directory (%q); are you sure it exists "+
					"with the correct permissions?",
				err.Error())
		}
	}

	// Choose UID and GID.
	uid, gid, err := perms.MyUserAndGroup()
	if err != nil {
		err = fmt.Errorf("MyUserAndGroup: %v", err)
		return
	}

	if *fUid >= 0 {
		uid = uint32(*fUid)
	}

	if *fGid >= 0 {
		gid = uint32(*fGid)
	}

	// Create a file system server.
	bucket := getBucket()
	serverCfg := &fs.ServerConfig{
		Clock:               timeutil.RealClock(),
		Bucket:              bucket,
		TempDir:             *fTempDir,
		TempDirLimit:        *fTempDirLimit,
		GCSChunkSize:        *fGCSChunkSize,
		ImplicitDirectories: *fImplicitDirs,
		SupportNlink:        *fSupportNlink,
		DirTypeCacheTTL:     *fTypeCacheTTL,
		Uid:                 uid,
		Gid:                 gid,
		FilePerms:           os.FileMode(*fFileMode),
		DirPerms:            os.FileMode(*fDirMode),
	}

	server, err := fs.NewServer(serverCfg)
	if err != nil {
		log.Fatal("fs.NewServer:", err)
	}

	// Mount the file system.
	mountCfg := &fuse.MountConfig{
		FSName:  bucket.Name(),
		Options: fMountOptions,
	}

	mountedFS, err := fuse.Mount(mountPoint, server, mountCfg)
	if err != nil {
		log.Fatal("Mount:", err)
	}

	log.Println("File system has been successfully mounted.")

	// Let the user unmount with Ctrl-C (SIGINT).
	registerSIGINTHandler(mountedFS.Dir())

	// Wait for it to be unmounted.
	if err := mountedFS.Join(context.Background()); err != nil {
		log.Fatal("MountedFileSystem.Join:", err)
	}

	log.Println("Successfully exiting.")
}
<|endoftext|>


<commit_before>package main

import (
    "log"
    "net/http"
)

func pingServer(w http.ResponseWriter, req *http.Request) {
    w.Write([]byte("Built by Drone!"))
}

func main() {
    http.HandleFunc("/", pingServer)
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}
<commit_msg>second trigger<commit_after>package main

import (
    "log"
    "net/http"
)

func droneServer(w http.ResponseWriter, req *http.Request) {
    w.Write([]byte("Built by Drone!"))
}

func main() {
    http.HandleFunc("/", droneServer)
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/bitrise-io/go-utils/command"
	"github.com/bitrise-io/go-utils/command/rubycommand"
	"github.com/bitrise-io/go-utils/fileutil"
	"github.com/bitrise-io/go-utils/log"
	"github.com/bitrise-io/go-utils/pathutil"
	shellquote "github.com/kballard/go-shellquote"
)

// ConfigsModel ...
type ConfigsModel struct {
	WorkDir        string
	Lane           string
	UpdateFastlane string
}

func createConfigsModelFromEnvs() ConfigsModel {
	return ConfigsModel{
		WorkDir:        os.Getenv("work_dir"),
		Lane:           os.Getenv("lane"),
		UpdateFastlane: os.Getenv("update_fastlane"),
	}
}

func (configs ConfigsModel) print() {
	log.Infof("Configs:")
	log.Printf("- WorkDir: %s", configs.WorkDir)
	log.Printf("- Lane: %s", configs.Lane)
	log.Printf("- UpdateFastlane: %s", configs.UpdateFastlane)
}

func (configs ConfigsModel) validate() error {
	if configs.Lane == "" {
		return errors.New("no Lane parameter specified")
	}

	if configs.WorkDir != "" {
		if exist, err := pathutil.IsDirExists(configs.WorkDir); err != nil {
			return fmt.Errorf("failed to check if WorkDir exist at: %s, error: %s", configs.WorkDir, err)
		} else if !exist {
			return fmt.Errorf("WorkDir not exist at: %s", configs.WorkDir)
		}
	}

	if configs.UpdateFastlane == "" {
		return errors.New("no UpdateFastlane parameter specified")
	} else if configs.UpdateFastlane != "true" && configs.UpdateFastlane != "false" {
		return fmt.Errorf(`invalid UpdateFastlane parameter specified: %s, available: ["true", "false"]`, configs.UpdateFastlane)
	}

	return nil
}

func failf(format string, v ...interface{}) {
	log.Errorf(format, v...)
	os.Exit(1)
}

func fastlaneVersionFromGemfileLockContent(content string) string {
	relevantLines := []string{}
	lines := strings.Split(content, "\n")

	specsStart := false
	for _, line := range lines {
		if strings.Contains(line, "specs:") {
			specsStart = true
		}

		trimmed := strings.Trim(line, " ")
		if trimmed == "" {
			specsStart = false
		}

		if specsStart {
			relevantLines = append(relevantLines, line)
		}
	}

	//     fastlane (1.109.0)
	exp := regexp.MustCompile(`fastlane \((.+)\)`)
	for _, line := range relevantLines {
		match := exp.FindStringSubmatch(line)
		if match != nil && len(match) == 2 {
			return match[1]
		}
	}

	return ""
}

func fastlaneVersionFromGemfileLock(gemfileLockPth string) (string, error) {
	content, err := fileutil.ReadStringFromFile(gemfileLockPth)
	if err != nil {
		return "", err
	}
	return fastlaneVersionFromGemfileLockContent(content), nil
}

func main() {
	configs := createConfigsModelFromEnvs()

	fmt.Println()
	configs.print()

	if err := configs.validate(); err != nil {
		failf("Issue with input: %s", err)
	}

	// Expand WorkDir
	fmt.Println()
	log.Infof("Expand WorkDir")

	workDir := configs.WorkDir
	if workDir == "" {
		log.Printf("WorkDir not set, using CurrentWorkingDirectory...")
		currentDir, err := pathutil.CurrentWorkingDirectoryAbsolutePath()
		if err != nil {
			failf("Failed to get current dir, error: %s", err)
		}
		workDir = currentDir
	} else {
		absWorkDir, err := pathutil.AbsPath(workDir)
		if err != nil {
			failf("Failed to expand path (%s), error: %s", workDir, err)
		}
		workDir = absWorkDir
	}

	log.Donef("Expanded WorkDir: %s", workDir)

	// Split lane option
	laneOptions, err := shellquote.Split(configs.Lane)
	if err != nil {
		failf("Failed to parse lane (%s), error: %s", configs.Lane, err)
	}

	// Determine desired Fastlane version
	fmt.Println()
	log.Infof("Determine desired Fastlane version")

	useBundler := false

	gemfileLockPth := filepath.Join(workDir, "Gemfile.lock")
	log.Printf("Checking Gemfile.lock (%s) for fastlane gem", gemfileLockPth)

	if exist, err := pathutil.IsPathExists(gemfileLockPth); err != nil {
		failf("Failed to check if Gemfile.lock exist at (%s), error: %s", gemfileLockPth, err)
	} else if exist {
		version, err := fastlaneVersionFromGemfileLock(gemfileLockPth)
		if err != nil {
			failf("Failed to read Fastlane versiom from Gemfile.lock (%s), error: %s", gemfileLockPth, err)
		}

		if version != "" {
			log.Printf("Gemfile.lock defined fastlane version: %s", version)

			useBundler = true
		} else {
			log.Printf("No fastlane version defined in Gemfile.lock")
		}
	} else {
		log.Printf("Gemfile.lock does not exist")
	}

	fmt.Println()

	// Install desired Fastlane version
	if useBundler {
		log.Infof("Install Fastlane with bundler")

		bundleInstallCmd := []string{"bundle", "install", "--jobs", "20", "--retry", "5"}

		log.Donef("$ %s", command.PrintableCommandArgs(false, bundleInstallCmd))

		cmd, err := rubycommand.NewFromSlice(bundleInstallCmd...)
		if err != nil {
			failf("Failed to create command model, error: %s", err)
		}

		cmd.SetStdout(os.Stdout).SetStderr(os.Stderr)
		cmd.SetDir(workDir)

		if err := cmd.Run(); err != nil {
			failf("Command failed, error: %s", err)
		}
	} else if configs.UpdateFastlane == "true" {
		log.Infof("Update system installed Fastlane")

		cmds, err := rubycommand.GemInstall("fastlane", "")
		if err != nil {
			failf("Failed to create command model, error: %s", err)
		}

		for _, cmd := range cmds {
			log.Donef("$ %s", cmd.PrintableCommandArgs())

			cmd.SetStdout(os.Stdout).SetStderr(os.Stderr)
			cmd.SetDir(workDir)

			if err := cmd.Run(); err != nil {
				failf("Command failed, error: %s", err)
			}
		}
	} else {
		log.Infof("Using system installed Fastlane")
	}

	fmt.Println()
	log.Infof("Fastlane version:")

	versionCmd := []string{"fastlane", "--version"}
	if useBundler {
		versionCmd = append([]string{"bundle", "exec"}, versionCmd...)
	}

	log.Donef("$ %s", command.PrintableCommandArgs(false, versionCmd))

	cmd, err := rubycommand.NewFromSlice(versionCmd...)
	if err != nil {
		failf("Command failed, error: %s", err)
	}

	cmd.SetStdout(os.Stdout).SetStderr(os.Stderr)
	cmd.SetDir(workDir)

	if err := cmd.Run(); err != nil {
		failf("Command failed, error: %s", err)
	}

	// Run fastlane
	fmt.Println()
	log.Infof("Run Fastlane")

	fastlaneCmd := []string{"fastlane"}
	fastlaneCmd = append(fastlaneCmd, laneOptions...)
	if useBundler {
		fastlaneCmd = append([]string{"bundle", "exec"}, fastlaneCmd...)
	}

	log.Donef("$ %s", command.PrintableCommandArgs(false, fastlaneCmd))

	cmd, err = rubycommand.NewFromSlice(fastlaneCmd...)
	if err != nil {
		failf("Failed to create command model, error: %s", err)
	}

	cmd.SetStdout(os.Stdout).SetStderr(os.Stderr)
	cmd.SetDir(workDir)

	buildlogPth := ""

	if tempDir, err := pathutil.NormalizedOSTempDirPath("fastlane_logs"); err != nil {
		log.Errorf("Failed to create temp dir for fastlane logs, error: %s", err)
	} else {
		buildlogPth = tempDir
		cmd.AppendEnvs("FL_BUILDLOG_PATH=" + buildlogPth)
	}

	deployDir := os.Getenv("BITRISE_DEPLOY_DIR")
	if deployDir == "" {
		log.Warnf("No BITRISE_DEPLOY_DIR found")
	}
	deployPth := filepath.Join(deployDir, "fastlane_env.log")

	if err := cmd.Run(); err != nil {
		fmt.Println()
		log.Errorf("Fastlane command: (%s) failed", cmd.PrintableCommandArgs())
		log.Errorf("If you want to send an issue report to fastlane (https://github.com/fastlane/fastlane/issues/new), you can find the output of fastlane env int he following log file:")
		fmt.Println()
		log.Infof(deployPth)
		fmt.Println()

		if cmd, err := rubycommand.New("fastlane", "env"); err != nil {
			log.Warnf("Failed to create command model, error: %s", err)
		} else {
			inputReader := strings.NewReader("n")
			var outBuffer bytes.Buffer
			outWriter := bufio.NewWriter(&outBuffer)

			cmd.SetStdin(inputReader)
			cmd.SetStdout(outWriter).SetStderr(os.Stderr)
			cmd.SetDir(workDir)

			if errEnv := cmd.Run(); errEnv != nil {
				log.Warnf("Fastlane command: (%s) failed", cmd.PrintableCommandArgs())
			} else if outBuffer.String() != "" {
				if err := fileutil.WriteStringToFile(deployPth, outBuffer.String()); err != nil {
					log.Warnf("Failed to write fastlane env log file, error: %s", err)
				}
			}
		}

		if err := filepath.Walk(buildlogPth, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if !info.IsDir() {
				if relLogPath, err := filepath.Rel(buildlogPth, path); err != nil {
					return err
				} else if err := os.Rename(path, filepath.Join(deployDir, strings.Replace(relLogPath, "/", "_", -1))); err != nil {
					return err
				}
			}
			return nil
		}); err != nil {
			log.Errorf("Failed to walk directory, error: %s", err)
		}
		failf("Command failed, error: %s", err)
	}
}
<commit_msg>Fix: commander-fastlane gem's version detected instead of fastlane version (#34)<commit_after>package main

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/bitrise-io/go-utils/command"
	"github.com/bitrise-io/go-utils/command/rubycommand"
	"github.com/bitrise-io/go-utils/fileutil"
	"github.com/bitrise-io/go-utils/log"
	"github.com/bitrise-io/go-utils/pathutil"
	shellquote "github.com/kballard/go-shellquote"
)

// ConfigsModel ...
type ConfigsModel struct {
	WorkDir        string
	Lane           string
	UpdateFastlane string
}

func createConfigsModelFromEnvs() ConfigsModel {
	return ConfigsModel{
		WorkDir:        os.Getenv("work_dir"),
		Lane:           os.Getenv("lane"),
		UpdateFastlane: os.Getenv("update_fastlane"),
	}
}

func (configs ConfigsModel) print() {
	log.Infof("Configs:")
	log.Printf("- WorkDir: %s", configs.WorkDir)
	log.Printf("- Lane: %s", configs.Lane)
	log.Printf("- UpdateFastlane: %s", configs.UpdateFastlane)
}

func (configs ConfigsModel) validate() error {
	if configs.Lane == "" {
		return errors.New("no Lane parameter specified")
	}

	if configs.WorkDir != "" {
		if exist, err := pathutil.IsDirExists(configs.WorkDir); err != nil {
			return fmt.Errorf("failed to check if WorkDir exist at: %s, error: %s", configs.WorkDir, err)
		} else if !exist {
			return fmt.Errorf("WorkDir not exist at: %s", configs.WorkDir)
		}
	}

	if configs.UpdateFastlane == "" {
		return errors.New("no UpdateFastlane parameter specified")
	} else if configs.UpdateFastlane != "true" && configs.UpdateFastlane != "false" {
		return fmt.Errorf(`invalid UpdateFastlane parameter specified: %s, available: ["true", "false"]`, configs.UpdateFastlane)
	}

	return nil
}

func failf(format string, v ...interface{}) {
	log.Errorf(format, v...)
	os.Exit(1)
}

func fastlaneVersionFromGemfileLockContent(content string) string {
	relevantLines := []string{}
	lines := strings.Split(content, "\n")

	specsStart := false
	for _, line := range lines {
		if strings.Contains(line, "specs:") {
			specsStart = true
		}

		trimmed := strings.Trim(line, " ")
		if trimmed == "" {
			specsStart = false
		}

		if specsStart {
			relevantLines = append(relevantLines, line)
		}
	}

	//     fastlane (1.109.0)
	exp := regexp.MustCompile(`^fastlane \((.+)\)`)
	for _, line := range relevantLines {
		match := exp.FindStringSubmatch(strings.TrimSpace(line))
		if match != nil && len(match) == 2 {
			return match[1]
		}
	}

	return ""
}

func fastlaneVersionFromGemfileLock(gemfileLockPth string) (string, error) {
	content, err := fileutil.ReadStringFromFile(gemfileLockPth)
	if err != nil {
		return "", err
	}
	return fastlaneVersionFromGemfileLockContent(content), nil
}

func main() {
	configs := createConfigsModelFromEnvs()

	fmt.Println()
	configs.print()

	if err := configs.validate(); err != nil {
		failf("Issue with input: %s", err)
	}

	// Expand WorkDir
	fmt.Println()
	log.Infof("Expand WorkDir")

	workDir := configs.WorkDir
	if workDir == "" {
		log.Printf("WorkDir not set, using CurrentWorkingDirectory...")
		currentDir, err := pathutil.CurrentWorkingDirectoryAbsolutePath()
		if err != nil {
			failf("Failed to get current dir, error: %s", err)
		}
		workDir = currentDir
	} else {
		absWorkDir, err := pathutil.AbsPath(workDir)
		if err != nil {
			failf("Failed to expand path (%s), error: %s", workDir, err)
		}
		workDir = absWorkDir
	}

	log.Donef("Expanded WorkDir: %s", workDir)

	// Split lane option
	laneOptions, err := shellquote.Split(configs.Lane)
	if err != nil {
		failf("Failed to parse lane (%s), error: %s", configs.Lane, err)
	}

	// Determine desired Fastlane version
	fmt.Println()
	log.Infof("Determine desired Fastlane version")

	useBundler := false

	gemfileLockPth := filepath.Join(workDir, "Gemfile.lock")
	log.Printf("Checking Gemfile.lock (%s) for fastlane gem", gemfileLockPth)

	if exist, err := pathutil.IsPathExists(gemfileLockPth); err != nil {
		failf("Failed to check if Gemfile.lock exist at (%s), error: %s", gemfileLockPth, err)
	} else if exist {
		version, err := fastlaneVersionFromGemfileLock(gemfileLockPth)
		if err != nil {
			failf("Failed to read Fastlane versiom from Gemfile.lock (%s), error: %s", gemfileLockPth, err)
		}

		if version != "" {
			log.Printf("Gemfile.lock defined fastlane version: %s", version)

			useBundler = true
		} else {
			log.Printf("No fastlane version defined in Gemfile.lock")
		}
	} else {
		log.Printf("Gemfile.lock does not exist")
	}

	fmt.Println()

	// Install desired Fastlane version
	if useBundler {
		log.Infof("Install Fastlane with bundler")

		bundleInstallCmd := []string{"bundle", "install", "--jobs", "20", "--retry", "5"}

		log.Donef("$ %s", command.PrintableCommandArgs(false, bundleInstallCmd))

		cmd, err := rubycommand.NewFromSlice(bundleInstallCmd...)
		if err != nil {
			failf("Failed to create command model, error: %s", err)
		}

		cmd.SetStdout(os.Stdout).SetStderr(os.Stderr)
		cmd.SetDir(workDir)

		if err := cmd.Run(); err != nil {
			failf("Command failed, error: %s", err)
		}
	} else if configs.UpdateFastlane == "true" {
		log.Infof("Update system installed Fastlane")

		cmds, err := rubycommand.GemInstall("fastlane", "")
		if err != nil {
			failf("Failed to create command model, error: %s", err)
		}

		for _, cmd := range cmds {
			log.Donef("$ %s", cmd.PrintableCommandArgs())

			cmd.SetStdout(os.Stdout).SetStderr(os.Stderr)
			cmd.SetDir(workDir)

			if err := cmd.Run(); err != nil {
				failf("Command failed, error: %s", err)
			}
		}
	} else {
		log.Infof("Using system installed Fastlane")
	}

	fmt.Println()
	log.Infof("Fastlane version:")

	versionCmd := []string{"fastlane", "--version"}
	if useBundler {
		versionCmd = append([]string{"bundle", "exec"}, versionCmd...)
	}

	log.Donef("$ %s", command.PrintableCommandArgs(false, versionCmd))

	cmd, err := rubycommand.NewFromSlice(versionCmd...)
	if err != nil {
		failf("Command failed, error: %s", err)
	}

	cmd.SetStdout(os.Stdout).SetStderr(os.Stderr)
	cmd.SetDir(workDir)

	if err := cmd.Run(); err != nil {
		failf("Command failed, error: %s", err)
	}

	// Run fastlane
	fmt.Println()
	log.Infof("Run Fastlane")

	fastlaneCmd := []string{"fastlane"}
	fastlaneCmd = append(fastlaneCmd, laneOptions...)
	if useBundler {
		fastlaneCmd = append([]string{"bundle", "exec"}, fastlaneCmd...)
	}

	log.Donef("$ %s", command.PrintableCommandArgs(false, fastlaneCmd))

	cmd, err = rubycommand.NewFromSlice(fastlaneCmd...)
	if err != nil {
		failf("Failed to create command model, error: %s", err)
	}

	cmd.SetStdout(os.Stdout).SetStderr(os.Stderr)
	cmd.SetDir(workDir)

	buildlogPth := ""

	if tempDir, err := pathutil.NormalizedOSTempDirPath("fastlane_logs"); err != nil {
		log.Errorf("Failed to create temp dir for fastlane logs, error: %s", err)
	} else {
		buildlogPth = tempDir
		cmd.AppendEnvs("FL_BUILDLOG_PATH=" + buildlogPth)
	}

	deployDir := os.Getenv("BITRISE_DEPLOY_DIR")
	if deployDir == "" {
		log.Warnf("No BITRISE_DEPLOY_DIR found")
	}
	deployPth := filepath.Join(deployDir, "fastlane_env.log")

	if err := cmd.Run(); err != nil {
		fmt.Println()
		log.Errorf("Fastlane command: (%s) failed", cmd.PrintableCommandArgs())
		log.Errorf("If you want to send an issue report to fastlane (https://github.com/fastlane/fastlane/issues/new), you can find the output of fastlane env int he following log file:")
		fmt.Println()
		log.Infof(deployPth)
		fmt.Println()

		if cmd, err := rubycommand.New("fastlane", "env"); err != nil {
			log.Warnf("Failed to create command model, error: %s", err)
		} else {
			inputReader := strings.NewReader("n")
			var outBuffer bytes.Buffer
			outWriter := bufio.NewWriter(&outBuffer)

			cmd.SetStdin(inputReader)
			cmd.SetStdout(outWriter).SetStderr(os.Stderr)
			cmd.SetDir(workDir)

			if errEnv := cmd.Run(); errEnv != nil {
				log.Warnf("Fastlane command: (%s) failed", cmd.PrintableCommandArgs())
			} else if outBuffer.String() != "" {
				if err := fileutil.WriteStringToFile(deployPth, outBuffer.String()); err != nil {
					log.Warnf("Failed to write fastlane env log file, error: %s", err)
				}
			}
		}

		if err := filepath.Walk(buildlogPth, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if !info.IsDir() {
				if relLogPath, err := filepath.Rel(buildlogPth, path); err != nil {
					return err
				} else if err := os.Rename(path, filepath.Join(deployDir, strings.Replace(relLogPath, "/", "_", -1))); err != nil {
					return err
				}
			}
			return nil
		}); err != nil {
			log.Errorf("Failed to walk directory, error: %s", err)
		}
		failf("Command failed, error: %s", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/bogem/id3v2"
	"github.com/mikkyang/id3-go"
	"github.com/mkideal/cli"
)

func main() {
	if err := cli.Root(root,
		cli.Tree(help),
		cli.Tree(tag),
		cli.Tree(tags),
		cli.Tree(cover),
		cli.Tree(artwork),
	).Run(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

var help = cli.HelpCommand("display help information")

// root command
type rootT struct {
	cli.Helper
	Name string `cli:"name" usage:"your name"`
}

var root = &cli.Command{
	Desc: "this is root command",
	// Argv is a factory function of argument object
	// ctx.Argv() is if Command.Argv == nil or Command.Argv() is nil
	Argv: func() interface{} { return new(rootT) },
	Fn: func(ctx *cli.Context) error {
		argv := ctx.Argv().(*rootT)
		ctx.String("Hello, root command, I am %s\n", argv.Name)
		return nil
	},
}

// child command
type childT struct {
	cli.Helper
	Name string `cli:"name" usage:"your name"`
}

// tag command

type tagT struct {
	cli.Helper
	Auto   bool   `cli:"auto" usage:"short and long format flags both are supported"`
	Artist string `cli:"artist" usage:"Artist name"`
	Album  string `cli:"album" usage:"Album name"`
	Title  string `cli:"title" usage:"Song name"`
}

var tag = &cli.Command{
	Name: "tag",
	Desc: "Set the id3 tags for mp3 files",
	Argv: func() interface{} { return new(tagT) },
	Fn: func(ctx *cli.Context) error {
		argv := ctx.Argv().(*tagT)

		ctx.String("auto=%v, artist=%s, album=%s, title=%s\n", argv.Auto, argv.Artist, argv.Album, argv.Title)

		// if auto flag
		// use filename
		if argv.Auto {
			files, _ := filepath.Glob("*.mp3")
			for i := 0; i < len(files); i++ {

				fmt.Println("\n************************************")
				fmt.Println("File: " + files[i] + ":")

				if strings.Index(files[i], " - ") == -1 {
					fmt.Println("Unable to determine track name/artist, skipping")
					continue
				}

				info := strings.Replace(files[i], ".mp3", "", 1)
				trackInfo := strings.Split(info, " - ")

				fmt.Println("  Artist: " + trackInfo[0])
				fmt.Println("  Title: " + trackInfo[1])

				tag, err := id3v2.Open(files[i])
				if err != nil {
					mp3File, terr := id3.Open(files[i])
					if terr != nil {
						log.Fatal("Error while opening mp3 file: ", terr)
					}
					defer mp3File.Close()
				} else {
					defer tag.Close()

				}
			}

		} else {
			// Open file and find tag in it
			tag, err := id3v2.Open("file.mp3")
			if err != nil {
				log.Fatal("Error while opening mp3 file: ", err)
			}
			defer tag.Close()

			// if artist flag
			tag.SetArtist(argv.Artist)

			// if album flag
			tag.SetAlbum(argv.Album)

			// if song flag
			tag.SetTitle(argv.Title)

			if err = tag.Save(); err != nil {
				log.Fatal("Error while saving a tag: ", err)
			}
		}

		return nil
	},
}

// tags command

type tagsT struct {
	cli.Helper
}

var tags = &cli.Command{
	Name: "tags",
	Desc: "Show currently set tags for all mp3 files",
	Argv: func() interface{} { return new(tagsT) },
	Fn: func(ctx *cli.Context) error {

		files, _ := filepath.Glob("*.mp3")
		for i := 0; i < len(files); i++ {
			fmt.Println("\n************************************")
			fmt.Println("File: " + files[i] + ":")

			// Open file and find tag in it
			tag, err := id3v2.Open(files[i])
			if err != nil {
				mp3File, terr := id3.Open(files[i])
				if terr != nil {
					log.Fatal("Error while opening mp3 file: ", terr)
				}
				defer mp3File.Close()

				// Read tags
				fmt.Println("  Artist: " + mp3File.Artist())
				fmt.Println("  Title: " + mp3File.Title())
				fmt.Println("  Album: " + mp3File.Album())
			} else {
				defer tag.Close()

				// Read tags
				fmt.Println("  Artist: " + tag.Artist())
				fmt.Println("  Title: " + tag.Title())
				fmt.Println("  Album: " + tag.Album())
			}
		}

		return nil
	},
}

var artwork = &cli.Command{
	Name: "artwork",
	Desc: "Set the artwork for a file",
	Argv: func() interface{} { return new(childT) },
	Fn: func(ctx *cli.Context) error {
		argv := ctx.Argv().(*childT)
		ctx.String("Hello, child command, I am %s\n", argv.Name)
		return nil
	},
}

var cover = &cli.Command{
	Name: "cover",
	Desc: "Update the artwork for all files with cover.jpg",
	Argv: func() interface{} { return new(childT) },
	Fn: func(ctx *cli.Context) error {
		// argv := ctx.Argv().(*childT)
		_, err := os.Stat("cover.jpg")

		if err != nil {
			fmt.Println("could not find file: cover.jpg")
		} else {
			fmt.Println("setting cover")
		}

		return nil
	},
}
<commit_msg>reworked artwork and cover commands<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/bogem/id3v2"
	"github.com/mikkyang/id3-go"
	"github.com/mkideal/cli"
)

func main() {
	if err := cli.Root(root,
		cli.Tree(help),
		cli.Tree(tag),
		cli.Tree(tags),
		cli.Tree(cover),
		cli.Tree(artwork),
	).Run(os.Args[1:]); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

var help = cli.HelpCommand("display help information")

// root command
type rootT struct {
	cli.Helper
	Name string `cli:"name" usage:"your name"`
}

var root = &cli.Command{
	Desc: "this is root command",
	// Argv is a factory function of argument object
	// ctx.Argv() is if Command.Argv == nil or Command.Argv() is nil
	Argv: func() interface{} { return new(rootT) },
	Fn: func(ctx *cli.Context) error {
		argv := ctx.Argv().(*rootT)
		ctx.String("Hello, root command, I am %s\n", argv.Name)
		return nil
	},
}

// child command
type childT struct {
	cli.Helper
	Name string `cli:"name" usage:"your name"`
}

// tag command

type tagT struct {
	cli.Helper
	Auto   bool   `cli:"auto" usage:"short and long format flags both are supported"`
	Artist string `cli:"artist" usage:"Artist name"`
	Album  string `cli:"album" usage:"Album name"`
	Title  string `cli:"title" usage:"Song name"`
}

var tag = &cli.Command{
	Name: "tag",
	Desc: "Set the id3 tags for mp3 files",
	Argv: func() interface{} { return new(tagT) },
	Fn: func(ctx *cli.Context) error {
		argv := ctx.Argv().(*tagT)

		ctx.String("auto=%v, artist=%s, album=%s, title=%s\n", argv.Auto, argv.Artist, argv.Album, argv.Title)

		// if auto flag
		// use filename
		if argv.Auto {
			files, _ := filepath.Glob("*.mp3")
			for i := 0; i < len(files); i++ {

				fmt.Println("\n************************************")
				fmt.Println("File: " + files[i] + ":")

				if strings.Index(files[i], " - ") == -1 {
					fmt.Println("Unable to determine track name/artist, skipping")
					continue
				}

				info := strings.Replace(files[i], ".mp3", "", 1)
				trackInfo := strings.Split(info, " - ")

				fmt.Println("  Artist: " + trackInfo[0])
				fmt.Println("  Title: " + trackInfo[1])

				tag, err := id3v2.Open(files[i])
				if err != nil {
					mp3File, terr := id3.Open(files[i])
					if terr != nil {
						log.Fatal("Error while opening mp3 file: ", terr)
					}
					defer mp3File.Close()
				} else {
					defer tag.Close()

				}
			}

		} else {
			// Open file and find tag in it
			tag, err := id3v2.Open("file.mp3")
			if err != nil {
				log.Fatal("Error while opening mp3 file: ", err)
			}
			defer tag.Close()

			// if artist flag
			tag.SetArtist(argv.Artist)

			// if album flag
			tag.SetAlbum(argv.Album)

			// if song flag
			tag.SetTitle(argv.Title)

			if err = tag.Save(); err != nil {
				log.Fatal("Error while saving a tag: ", err)
			}
		}

		return nil
	},
}

// tags command

type tagsT struct {
	cli.Helper
}

var tags = &cli.Command{
	Name: "tags",
	Desc: "Show currently set tags for all mp3 files",
	Argv: func() interface{} { return new(tagsT) },
	Fn: func(ctx *cli.Context) error {

		files, _ := filepath.Glob("*.mp3")
		for i := 0; i < len(files); i++ {
			fmt.Println("\n************************************")
			fmt.Println("File: " + files[i] + ":")

			// Open file and find tag in it
			tag, err := id3v2.Open(files[i])
			if err != nil {
				mp3File, terr := id3.Open(files[i])
				if terr != nil {
					log.Fatal("Error while opening mp3 file: ", terr)
				}
				defer mp3File.Close()

				// Read tags
				fmt.Println("  Artist: " + mp3File.Artist())
				fmt.Println("  Title: " + mp3File.Title())
				fmt.Println("  Album: " + mp3File.Album())
			} else {
				defer tag.Close()

				// Read tags
				fmt.Println("  Artist: " + tag.Artist())
				fmt.Println("  Title: " + tag.Title())
				fmt.Println("  Album: " + tag.Album())
			}
		}

		return nil
	},
}

// artwork command

type artworkT struct {
	cli.Helper
}

var artwork = &cli.Command{
	Name: "artwork",
	Desc: "Set the artwork for a file",
	Argv: func() interface{} { return new(artworkT) },
	Fn: func(ctx *cli.Context) error {

		files, _ := filepath.Glob("*.mp3")
		for i := 0; i < len(files); i++ {
			artworkFilename := strings.Replace(files[i], ".mp3", ".jpg", 1)

			if fileExists(artworkFilename) {
				fmt.Println("setting artwork on file: " + files[i])
				err := setPictureTag(artworkFilename, files[i])
				if err != nil {
					log.Fatal("could not save artwork", err)
				}
			} else {
				continue
			}
		}

		return nil
	},
}

// cover command

type coverT struct {
	cli.Helper
}

var cover = &cli.Command{
	Name: "cover",
	Desc: "Update the artwork for all files with cover.jpg",
	Argv: func() interface{} { return new(coverT) },
	Fn: func(ctx *cli.Context) error {
		if fileExists("cover.jpg") {
			files, _ := filepath.Glob("*.mp3")
			for i := 0; i < len(files); i++ {
				fmt.Println("setting cover on file: " + files[i])
				err := setPictureTag("cover.jpg", files[i])
				if err != nil {
					log.Fatal("could not save cover", err)
				}
			}
		} else {
			log.Fatal("could not find file: cover.jpg")
		}

		return nil
	},
}

// helper functions

func setPictureTag(coverFilename string, filename string) error {
	frontCover, err := os.Open("cover.jpg")
	if err != nil {
		return err
	}

	defer frontCover.Close()

	pic := id3v2.PictureFrame{
		Encoding:    id3v2.ENUTF8,
		MimeType:    "image/jpeg",
		Picture:     frontCover,
		PictureType: id3v2.PTFrontCover,
	}

	// Open file and find tag in it
	tag, err := id3v2.Open(filename)
	if err != nil {
		return err
	}

	defer tag.Close()
	tag.DeleteAllFrames()
	tag.AddAttachedPicture(pic)

	fmt.Println("picture saved")

	err = tag.Save()

	if err != nil {
		return err
	}

	return nil
}

func fileExists(filename string) bool {
	_, err := os.Stat(filename)
	if err != nil {
		return true
	}

	return false
}

func checkNotEmpty(str string) bool {
	if len(str) > 0 {
		return true
	}

	return false
}
<|endoftext|>


<commit_before>package miniprogram

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"encoding/json"
	"errors"
)

var (
	// ErrAppIDNotMatch appid不匹配
	ErrAppIDNotMatch = errors.New("app id not match")
	// ErrInvalidBlockSize block size不合法
	ErrInvalidBlockSize = errors.New("invalid block size")
	// ErrInvalidPKCS7Data PKCS7数据不合法
	ErrInvalidPKCS7Data = errors.New("invalid PKCS7 data")
	// ErrInvalidPKCS7Padding 输入padding失败
	ErrInvalidPKCS7Padding = errors.New("invalid padding on input")
)

// UserInfo 用户信息
type UserInfo struct {
	OpenID    string `json:"openId"`
	UnionID   string `json:"unionId"`
	NickName  string `json:"nickName"`
	Gender    int    `json:"gender"`
	City      string `json:"city"`
	Province  string `json:"province"`
	Country   string `json:"country"`
	AvatarURL string `json:"avatarUrl"`
	Language  string `json:"language"`
	Watermark struct {
		Timestamp int64  `json:"timestamp"`
		AppID     string `json:"appid"`
	} `json:"watermark"`
}

// 用户手机号
type PhoneInfo struct {
	PhoneNumber     string `json:"phoneNumber"`
	PurePhoneNumber string `json:"purePhoneNumber"`
	CountryCode     string `json:"countryCode"`
	Watermark       struct {
		Timestamp int64  `json:"timestamp"`
		AppID     string `json:"appid"`
	} `json:"watermark"`
}

// get cipherText
func getCipherText(sessionKey, encryptedData, iv string) ([]byte, error) {
	aesKey, err := base64.StdEncoding.DecodeString(sessionKey)
	if err != nil {
		return nil, err
	}
	cipherText, err := base64.StdEncoding.DecodeString(encryptedData)
	if err != nil {
		return nil, err
	}
	ivBytes, err := base64.StdEncoding.DecodeString(iv)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(aesKey)
	if err != nil {
		return nil, err
	}
	mode := cipher.NewCBCDecrypter(block, ivBytes)
	mode.CryptBlocks(cipherText, cipherText)
	cipherText, err = pkcs7Unpad(cipherText, block.BlockSize())
	if err != nil {
		return nil, err
	}
	return cipherText, nil
}

// pkcs7Unpad returns slice of the original data without padding
func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
	if blockSize <= 0 {
		return nil, ErrInvalidBlockSize
	}
	if len(data)%blockSize != 0 || len(data) == 0 {
		return nil, ErrInvalidPKCS7Data
	}
	c := data[len(data)-1]
	n := int(c)
	if n == 0 || n > len(data) {
		return nil, ErrInvalidPKCS7Padding
	}
	for i := 0; i < n; i++ {
		if data[len(data)-n+i] != c {
			return nil, ErrInvalidPKCS7Padding
		}
	}
	return data[:len(data)-n], nil
}

// Decrypt 解密数据
func (wxa *MiniProgram) Decrypt(sessionKey, encryptedData, iv string) (*UserInfo, error) {
	cipherText, err := getCipherText(sessionKey, encryptedData, iv)
	if err != nil {
		return nil, err
	}
	var userInfo UserInfo
	err = json.Unmarshal(cipherText, &userInfo)
	if err != nil {
		return nil, err
	}
	if userInfo.Watermark.AppID != wxa.AppID {
		return nil, ErrAppIDNotMatch
	}
	return &userInfo, nil
}

// DecryptPhone 解密数据(手机)
func (wxa *MiniProgram) DecryptPhone(sessionKey, encryptedData, iv string) (*PhoneInfo, error) {
	cipherText, err := getCipherText(sessionKey, encryptedData, iv)
	if err != nil {
		return nil, err
	}
	var phoneInfo PhoneInfo
	err = json.Unmarshal(cipherText, &phoneInfo)
	if err != nil {
		return nil, err
	}
	if phoneInfo.Watermark.AppID != wxa.AppID {
		return nil, ErrAppIDNotMatch
	}
	return &phoneInfo, nil
}
<commit_msg>小程序数据解密，增加手机号解密，兼容之前的用户信息解密<commit_after>package miniprogram

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"encoding/json"
	"errors"
)

var (
	// ErrAppIDNotMatch appid不匹配
	ErrAppIDNotMatch = errors.New("app id not match")
	// ErrInvalidBlockSize block size不合法
	ErrInvalidBlockSize = errors.New("invalid block size")
	// ErrInvalidPKCS7Data PKCS7数据不合法
	ErrInvalidPKCS7Data = errors.New("invalid PKCS7 data")
	// ErrInvalidPKCS7Padding 输入padding失败
	ErrInvalidPKCS7Padding = errors.New("invalid padding on input")
)

// UserInfo 用户信息
type UserInfo struct {
	OpenID    string `json:"openId"`
	UnionID   string `json:"unionId"`
	NickName  string `json:"nickName"`
	Gender    int    `json:"gender"`
	City      string `json:"city"`
	Province  string `json:"province"`
	Country   string `json:"country"`
	AvatarURL string `json:"avatarUrl"`
	Language  string `json:"language"`
	Watermark struct {
		Timestamp int64  `json:"timestamp"`
		AppID     string `json:"appid"`
	} `json:"watermark"`
}

// PhoneInfo 用户手机号
type PhoneInfo struct {
	PhoneNumber     string `json:"phoneNumber"`
	PurePhoneNumber string `json:"purePhoneNumber"`
	CountryCode     string `json:"countryCode"`
	Watermark       struct {
		Timestamp int64  `json:"timestamp"`
		AppID     string `json:"appid"`
	} `json:"watermark"`
}

// pkcs7Unpad returns slice of the original data without padding
func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
	if blockSize <= 0 {
		return nil, ErrInvalidBlockSize
	}
	if len(data)%blockSize != 0 || len(data) == 0 {
		return nil, ErrInvalidPKCS7Data
	}
	c := data[len(data)-1]
	n := int(c)
	if n == 0 || n > len(data) {
		return nil, ErrInvalidPKCS7Padding
	}
	for i := 0; i < n; i++ {
		if data[len(data)-n+i] != c {
			return nil, ErrInvalidPKCS7Padding
		}
	}
	return data[:len(data)-n], nil
}

// getCipherText returns slice of the cipher text
func getCipherText(sessionKey, encryptedData, iv string) ([]byte, error) {
	aesKey, err := base64.StdEncoding.DecodeString(sessionKey)
	if err != nil {
		return nil, err
	}
	cipherText, err := base64.StdEncoding.DecodeString(encryptedData)
	if err != nil {
		return nil, err
	}
	ivBytes, err := base64.StdEncoding.DecodeString(iv)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(aesKey)
	if err != nil {
		return nil, err
	}
	mode := cipher.NewCBCDecrypter(block, ivBytes)
	mode.CryptBlocks(cipherText, cipherText)
	cipherText, err = pkcs7Unpad(cipherText, block.BlockSize())
	if err != nil {
		return nil, err
	}
	return cipherText, nil
}

// Decrypt 解密数据
func (wxa *MiniProgram) Decrypt(sessionKey, encryptedData, iv string) (*UserInfo, error) {
	cipherText, err := getCipherText(sessionKey, encryptedData, iv)
	if err != nil {
		return nil, err
	}
	var userInfo UserInfo
	err = json.Unmarshal(cipherText, &userInfo)
	if err != nil {
		return nil, err
	}
	if userInfo.Watermark.AppID != wxa.AppID {
		return nil, ErrAppIDNotMatch
	}
	return &userInfo, nil
}

// DecryptPhone 解密数据(手机)
func (wxa *MiniProgram) DecryptPhone(sessionKey, encryptedData, iv string) (*PhoneInfo, error) {
	cipherText, err := getCipherText(sessionKey, encryptedData, iv)
	if err != nil {
		return nil, err
	}
	var phoneInfo PhoneInfo
	err = json.Unmarshal(cipherText, &phoneInfo)
	if err != nil {
		return nil, err
	}
	if phoneInfo.Watermark.AppID != wxa.AppID {
		return nil, ErrAppIDNotMatch
	}
	return &phoneInfo, nil
}
<|endoftext|>


<commit_before>/* ELF tree - Tree viewer for ELF library dependency */
package main

import (
	"bufio"
	"debug/elf"
	"encoding/binary"
	"flag"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"strings"
)

type DepsNode struct {
	name   string
	parent *DepsNode
	child  []*DepsNode
	depth  int
}

type DynInfo struct {
	tag elf.DynTag
	val interface{}
}

type DepsInfo struct {
	path   string
	mach   elf.Machine
	bits   elf.Class
	endian binary.ByteOrder
	kind   elf.Type
	abi    elf.OSABI
	ver    uint8

	libs []string
	dsym []elf.ImportedSymbol
	syms []elf.Symbol
	prog []*elf.Prog
	dyns []DynInfo
}

var (
	deps      map[string]DepsInfo
	deps_list []*DepsNode
	deps_root *DepsNode
	deflib    []string
	envlib    string
	conflib   []string
)

// command-line options
var (
	verbose  bool
	showPath bool
	showTui  bool
)

func readLdSoConf(name string, libpath []string) {
	f, err := os.Open(name)
	if err != nil {
		return
	}
	defer f.Close()

	s := bufio.NewScanner(f)
	for s.Scan() {
		t := s.Text()

		if len(strings.TrimSpace(t)) == 0 {
			continue
		}
		if strings.HasPrefix(t, "#") {
			continue
		}

		if strings.HasPrefix(t, "include") {
			libs, err := filepath.Glob(t[8:])
			if err != nil {
				continue
			}
			for _, l := range libs {
				readLdSoConf(l, libpath)
			}
		} else {
			libpath = append(libpath, t)
		}
	}
}

func init() {
	deps = make(map[string]DepsInfo)
	deflib = []string{"/lib/", "/usr/lib/"}
	envlib = os.Getenv("LD_LIBRARY_PATH")
	readLdSoConf("/etc/ld.so.conf", conflib)

	flag.BoolVar(&verbose, "v", false, "Show binary info")
	flag.BoolVar(&showPath, "p", false, "Show library path")
	flag.BoolVar(&showTui, "tui", false, "Show it with TUI")
}

func findLib(name string) string {
	if strings.Contains(name, "/") {
		return name
	}

	// check LD_LIBRARY_PATH environ
	for _, libpath := range strings.Split(envlib, ":") {
		fullpath := path.Join(libpath, name)
		if _, err := os.Stat(fullpath); err == nil {
			return fullpath
		}
	}

	// check libraries in /etc/ld.so.conf
	for _, libpath := range conflib {
		fullpath := path.Join(libpath, name)
		if _, err := os.Stat(fullpath); err == nil {
			return fullpath
		}
	}

	// check default library directories
	for _, libpath := range deflib {
		fullpath := path.Join(libpath, name)
		if _, err := os.Stat(fullpath); err == nil {
			return fullpath
		}
	}
	return ""
}

func realPath(pathname string) string {
	if pathname == "" {
		return ""
	}

	relpath, _ := filepath.EvalSymlinks(pathname)
	abspath, _ := filepath.Abs(relpath)

	return abspath
}

func readElfString(strtab []byte, i uint64) string {
	var len uint64

	for len = 0; strtab[i+len] != '\x00'; len++ {
		continue
	}

	return string(strtab[i : i+len])
}

func readDynamic(f *elf.File, info *DepsInfo) {
	var i, count uint

	dyn := f.Section(".dynamic")
	data, err := dyn.Data()
	if err != nil {
		return
	}
	str := f.Section(".dynstr")
	stab, err := str.Data()
	if err != nil {
		return
	}

	count = uint(dyn.Size / dyn.Entsize)
	for i = 0; i < count; i++ {
		var tag, val uint64
		if f.Class == elf.ELFCLASS64 {
			tag = f.ByteOrder.Uint64(data[(i*2+0)*8 : (i*2+1)*8])
			val = f.ByteOrder.Uint64(data[(i*2+1)*8 : (i*2+2)*8])
		} else {
			tag = uint64(f.ByteOrder.Uint32(data[(i*2+0)*4 : (i*2+1)*4]))
			val = uint64(f.ByteOrder.Uint32(data[(i*2+1)*4 : (i*2+2)*4]))
		}

		dtag := elf.DynTag(tag)
		switch dtag {
		case elf.DT_NEEDED:
			fallthrough
		case elf.DT_RPATH:
			fallthrough
		case elf.DT_RUNPATH:
			fallthrough
		case elf.DT_SONAME:
			sval := readElfString(stab, val)
			info.dyns = append(info.dyns, DynInfo{dtag, sval})
			break
		default:
			info.dyns = append(info.dyns, DynInfo{dtag, val})
			break
		}
	}
}

func processDep(dep *DepsNode) {
	// skip duplicate libraries
	if _, ok := deps[dep.name]; ok {
		return
	}

	info := DepsInfo{path: realPath(findLib(dep.name))}

	if dep.parent == nil {
		info.path = realPath(flag.Args()[0])
	}

	f, err := elf.Open(info.path)
	if err != nil {
		fmt.Printf("%v: %s (%s)\n", err, info.path, dep.name)
		os.Exit(1)
	}
	defer f.Close()

	info.mach = f.Machine
	info.bits = f.Class
	info.kind = f.Type
	info.abi = f.OSABI
	info.ver = f.ABIVersion
	info.endian = f.ByteOrder

	info.prog = f.Progs

	readDynamic(f, &info)

	libs, err := f.ImportedLibraries()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	dsym, err := f.ImportedSymbols()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	syms, err := f.Symbols()
	if err == nil {
		info.syms = syms
	}

	info.libs = libs
	info.dsym = dsym

	var L []*DepsNode
	for _, soname := range libs {
		N := new(DepsNode)
		N.name = soname
		N.parent = dep
		N.depth = dep.depth + 1

		L = append(L, N)
		dep.child = append(dep.child, N)
	}

	deps_list = append(L, deps_list...)
	deps[dep.name] = info
}

func printDepTree(n *DepsNode, f *elf.File) {
	for i := 0; i < n.depth; i++ {
		fmt.Printf("   ")
	}

	if showPath {
		fmt.Printf("%s  => %s\n", n.name, deps[n.name].path)
	} else {
		fmt.Println(n.name)
	}

	for _, v := range n.child {
		printDepTree(v, f)
	}

	if verbose && n.parent == nil {
		showDetails(f, deps[n.name].path)
	}
}

func showDetails(f *elf.File, pathname string) {
	s := f.Section(".interp")
	if s == nil {
		fmt.Printf("static linked executable: %s\n", pathname)
		os.Exit(1)
	}
	interp, err := s.Data()
	if err != nil {
		fmt.Printf("%v: %s\n", err, pathname)
		os.Exit(1)
	}

	di_deps, err := f.ImportedLibraries()
	if err != nil {
		fmt.Printf("imported libraries: %v\n", err)
		os.Exit(1)
	}

	fmt.Println()
	fmt.Printf("%s: %s\n", path.Base(pathname), realPath(pathname))
	fmt.Printf("  type:                     %s  (%s / %s / %s)\n",
		f.Type, f.Machine, f.Class, f.ByteOrder)
	fmt.Printf("  interpreter:              %s\n", string(interp))
	fmt.Printf("  total dependency:         %d\n", len(deps)-1) // exclude itself
	fmt.Printf("  direct dependency:        %d\n", len(di_deps))
}

func main() {
	flag.Parse()

	args := flag.Args()
	if len(args) < 1 {
		fmt.Println("Usage: elftree [<options>] <executable>")
		os.Exit(1)
	}

	pathname := args[0]
	f, err := elf.Open(pathname)
	if err != nil {
		fmt.Printf("%v: %s\n", err, pathname)
		os.Exit(1)
	}
	defer f.Close()

	deps_root = new(DepsNode)
	deps_root.name = path.Base(pathname)

	deps_list = append(deps_list, deps_root)
	for len(deps_list) > 0 {
		// pop first element
		dep := deps_list[0]
		deps_list = deps_list[1:]

		processDep(dep)
	}

	if showTui {
		ShowWithTUI(deps_root)
	} else {
		printDepTree(deps_root, f)
	}
}
<commit_msg>Consider DT_RPATH and DT_RUNPATH<commit_after>/* ELF tree - Tree viewer for ELF library dependency */
package main

import (
	"bufio"
	"debug/elf"
	"encoding/binary"
	"flag"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"strings"
)

type DepsNode struct {
	name   string
	parent *DepsNode
	child  []*DepsNode
	depth  int
}

type DynInfo struct {
	tag elf.DynTag
	val interface{}
}

type DepsInfo struct {
	path   string
	mach   elf.Machine
	bits   elf.Class
	endian binary.ByteOrder
	kind   elf.Type
	abi    elf.OSABI
	ver    uint8

	libs []string
	dsym []elf.ImportedSymbol
	syms []elf.Symbol
	prog []*elf.Prog
	dyns []DynInfo
}

var (
	deps      map[string]DepsInfo
	deps_list []*DepsNode
	deps_root *DepsNode
	deflib    []string
	envlib    string
	conflib   []string
)

// command-line options
var (
	verbose  bool
	showPath bool
	showTui  bool
)

func readLdSoConf(name string, libpath []string) {
	f, err := os.Open(name)
	if err != nil {
		return
	}
	defer f.Close()

	s := bufio.NewScanner(f)
	for s.Scan() {
		t := s.Text()

		if len(strings.TrimSpace(t)) == 0 {
			continue
		}
		if strings.HasPrefix(t, "#") {
			continue
		}

		if strings.HasPrefix(t, "include") {
			libs, err := filepath.Glob(t[8:])
			if err != nil {
				continue
			}
			for _, l := range libs {
				readLdSoConf(l, libpath)
			}
		} else {
			libpath = append(libpath, t)
		}
	}
}

func init() {
	deps = make(map[string]DepsInfo)
	deflib = []string{"/lib/", "/usr/lib/"}
	envlib = os.Getenv("LD_LIBRARY_PATH")
	readLdSoConf("/etc/ld.so.conf", conflib)

	flag.BoolVar(&verbose, "v", false, "Show binary info")
	flag.BoolVar(&showPath, "p", false, "Show library path")
	flag.BoolVar(&showTui, "tui", false, "Show it with TUI")
}

// search shared libraries as described in `man ld.so(8)`
func findLib(name string, parent *DepsNode) string {
	if strings.Contains(name, "/") {
		return name
	}

	// check DT_RPATH attribute
	if parent != nil {
		info := deps[parent.name]
		for _, dyn := range info.dyns {
			if dyn.tag != elf.DT_RPATH {
				continue
			}

			fullpath := path.Join(dyn.val.(string), name)
			if _, err := os.Stat(fullpath); err == nil {
				return fullpath
			}
		}
	}

	// check LD_LIBRARY_PATH environ
	for _, libpath := range strings.Split(envlib, ":") {
		fullpath := path.Join(libpath, name)
		if _, err := os.Stat(fullpath); err == nil {
			return fullpath
		}
	}

	// check DT_RUNPATH attribute
	if parent != nil {
		info := deps[parent.name]
		for _, dyn := range info.dyns {
			if dyn.tag != elf.DT_RUNPATH {
				continue
			}

			fullpath := path.Join(dyn.val.(string), name)
			if _, err := os.Stat(fullpath); err == nil {
				return fullpath
			}
		}
	}

	// check libraries in /etc/ld.so.conf
	for _, libpath := range conflib {
		fullpath := path.Join(libpath, name)
		if _, err := os.Stat(fullpath); err == nil {
			return fullpath
		}
	}

	// check default library directories
	for _, libpath := range deflib {
		fullpath := path.Join(libpath, name)
		if _, err := os.Stat(fullpath); err == nil {
			return fullpath
		}
	}
	return ""
}

func realPath(pathname string) string {
	if pathname == "" {
		return ""
	}

	relpath, _ := filepath.EvalSymlinks(pathname)
	abspath, _ := filepath.Abs(relpath)

	return abspath
}

func readElfString(strtab []byte, i uint64) string {
	var len uint64

	for len = 0; strtab[i+len] != '\x00'; len++ {
		continue
	}

	return string(strtab[i : i+len])
}

func readDynamic(f *elf.File, info *DepsInfo) {
	var i, count uint

	dyn := f.Section(".dynamic")
	data, err := dyn.Data()
	if err != nil {
		return
	}
	str := f.Section(".dynstr")
	stab, err := str.Data()
	if err != nil {
		return
	}

	count = uint(dyn.Size / dyn.Entsize)
	for i = 0; i < count; i++ {
		var tag, val uint64

		if f.Class == elf.ELFCLASS64 {
			tag = f.ByteOrder.Uint64(data[(i*2+0)*8 : (i*2+1)*8])
			val = f.ByteOrder.Uint64(data[(i*2+1)*8 : (i*2+2)*8])
		} else {
			tag = uint64(f.ByteOrder.Uint32(data[(i*2+0)*4 : (i*2+1)*4]))
			val = uint64(f.ByteOrder.Uint32(data[(i*2+1)*4 : (i*2+2)*4]))
		}

		dtag := elf.DynTag(tag)
		switch dtag {
		case elf.DT_NEEDED:
			fallthrough
		case elf.DT_RPATH:
			fallthrough
		case elf.DT_RUNPATH:
			fallthrough
		case elf.DT_SONAME:
			sval := readElfString(stab, val)
			info.dyns = append(info.dyns, DynInfo{dtag, sval})
			break
		default:
			info.dyns = append(info.dyns, DynInfo{dtag, val})
			break
		}
	}
}

func processDep(dep *DepsNode) {
	// skip duplicate libraries
	if _, ok := deps[dep.name]; ok {
		return
	}

	info := DepsInfo{path: realPath(findLib(dep.name, dep.parent))}

	if dep.parent == nil {
		info.path = realPath(flag.Args()[0])
	}

	f, err := elf.Open(info.path)
	if err != nil {
		fmt.Printf("%v: %s (%s)\n", err, info.path, dep.name)
		os.Exit(1)
	}
	defer f.Close()

	info.mach = f.Machine
	info.bits = f.Class
	info.kind = f.Type
	info.abi = f.OSABI
	info.ver = f.ABIVersion
	info.endian = f.ByteOrder

	info.prog = f.Progs

	readDynamic(f, &info)

	libs, err := f.ImportedLibraries()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	dsym, err := f.ImportedSymbols()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	syms, err := f.Symbols()
	if err == nil {
		info.syms = syms
	}

	info.libs = libs
	info.dsym = dsym

	var L []*DepsNode
	for _, soname := range libs {
		N := new(DepsNode)
		N.name = soname
		N.parent = dep
		N.depth = dep.depth + 1

		L = append(L, N)
		dep.child = append(dep.child, N)
	}

	deps_list = append(L, deps_list...)
	deps[dep.name] = info
}

func printDepTree(n *DepsNode, f *elf.File) {
	for i := 0; i < n.depth; i++ {
		fmt.Printf("   ")
	}

	if showPath {
		fmt.Printf("%s  => %s\n", n.name, deps[n.name].path)
	} else {
		fmt.Println(n.name)
	}

	for _, v := range n.child {
		printDepTree(v, f)
	}

	if verbose && n.parent == nil {
		showDetails(f, deps[n.name].path)
	}
}

func showDetails(f *elf.File, pathname string) {
	s := f.Section(".interp")
	if s == nil {
		fmt.Printf("static linked executable: %s\n", pathname)
		os.Exit(1)
	}
	interp, err := s.Data()
	if err != nil {
		fmt.Printf("%v: %s\n", err, pathname)
		os.Exit(1)
	}

	di_deps, err := f.ImportedLibraries()
	if err != nil {
		fmt.Printf("imported libraries: %v\n", err)
		os.Exit(1)
	}

	fmt.Println()
	fmt.Printf("%s: %s\n", path.Base(pathname), realPath(pathname))
	fmt.Printf("  type:                     %s  (%s / %s / %s)\n",
		f.Type, f.Machine, f.Class, f.ByteOrder)
	fmt.Printf("  interpreter:              %s\n", string(interp))
	fmt.Printf("  total dependency:         %d\n", len(deps)-1) // exclude itself
	fmt.Printf("  direct dependency:        %d\n", len(di_deps))
}

func main() {
	flag.Parse()

	args := flag.Args()
	if len(args) < 1 {
		fmt.Println("Usage: elftree [<options>] <executable>")
		os.Exit(1)
	}

	pathname := args[0]
	f, err := elf.Open(pathname)
	if err != nil {
		fmt.Printf("%v: %s\n", err, pathname)
		os.Exit(1)
	}
	defer f.Close()

	deps_root = new(DepsNode)
	deps_root.name = path.Base(pathname)

	deps_list = append(deps_list, deps_root)
	for len(deps_list) > 0 {
		// pop first element
		dep := deps_list[0]
		deps_list = deps_list[1:]

		processDep(dep)
	}

	if showTui {
		ShowWithTUI(deps_root)
	} else {
		printDepTree(deps_root, f)
	}
}
<|endoftext|>