package main
import (
"os"
"fmt"
"time"
"flag"
"bufio"
"strings"
"path/filepath"
"text/template"
)
var searchDir string
var searchDirs []string
func main() {
fmt.Println("Starting...")
// Get any arguments
dirPtr := flag.String("d", "Code", "Directory to scan for each user.")
flag.Parse()
searchDir = *dirPtr
searchDirs = strings.Split(searchDir, ",")
if len(searchDirs) > 1 {
generate(findProjects(), searchDirs[0])
} else {
generate(findProjects(), searchDir)
}
}
type user struct {
Name string
Projects []project
}
type project struct {
Name string
Path string
CSSClass string
}
func findProjects() map[string]user {
var files []string
users := make(map[string]user)
if len(searchDirs) > 1 {
for _, d := range searchDirs {
files, _ = filepath.Glob("/home/*/" + d + "/*")
mapFiles(&files, users)
}
} else {
files, _ = filepath.Glob("/home/*/" + searchDir + "/*")
mapFiles(&files, users)
}
return users
}
func mapFiles(files *[]string, users map[string]user) {
for _, path := range *files {
pparts := strings.Split(path, "/")
uname := pparts[2]
fname := filepath.Base(path)
// Exclude certain file names
if strings.HasPrefix(fname, ".") || strings.HasSuffix(fname, ".log") || strings.HasPrefix(fname, "README") || strings.HasSuffix(fname, "~") {
continue
}
// Ensure file is other-readable
// TODO: just detect if we can actually read this, instead
info, _ := os.Stat(path)
mode := info.Mode()
if mode & 0004 == 0 {
continue
}
// Mark directories vs. regular files
cssClass := "file"
if mode.IsDir() {
path = path + "/"
fname = fname + "/"
cssClass = "dir"
}
// Check if files are executable by all
if mode & 0001 != 0 {
cssClass = cssClass + " exec"
}
proj := &project{Name: fname, Path: strings.Replace(path, "/home/", "~", -1), CSSClass: cssClass}
u, exists := users[uname]
if !exists {
fmt.Printf("Found %s for ~%s.\n", pparts[3], uname)
projs := []project{*proj}
u = user{Name: uname, Projects: projs}
} else {
u.Projects = append(u.Projects, *proj)
}
users[uname] = u
}
}
type page struct {
FolderName string
Folders []string
Host string
Users map[string]user
Updated string
UpdatedForHumans string
}
func graphicalName(n string) string {
r := strings.NewReplacer("tilde", "~", "ctrl-c", "^C", "nuclear", "☢")
return r.Replace(n)
}
func split(s string, d string) []string {
arr := strings.Split(s, d)
return arr
}
func listDirs(dirs []string) string {
if len(dirs) > 1 {
for i := 0; i < len(dirs); i++ {
d := dirs[i]
d = fmt.Sprintf("%s", d)
dirs[i] = d
}
return strings.Join(dirs, " or ")
}
return fmt.Sprintf("%s", dirs[0])
}
func generate(users map[string]user, outputFile string) {
fmt.Println("Generating page.")
f, err := os.Create(os.Getenv("HOME") + "/public_html/" + strings.ToLower(outputFile) + ".html")
if err != nil {
panic(err)
}
defer f.Close()
funcMap := template.FuncMap {
"Split": split,
"ListDirs": listDirs,
}
w := bufio.NewWriter(f)
template, err := template.New("").Funcs(funcMap).ParseFiles("../templates/code.html")
if err != nil {
panic(err)
}
// Extra page data
host, _ := os.Hostname()
curTime := time.Now().UTC()
updatedReadable := curTime.Format(time.RFC1123)
updated := curTime.Format(time.RFC3339)
// Generate the page
p := &page{FolderName: searchDirs[0], Folders: searchDirs, Host: graphicalName(host), UpdatedForHumans: updatedReadable, Updated: updated, Users: users}
template.ExecuteTemplate(w, "code", p)
w.Flush()
fmt.Println("DONE!")
}