2015-01-26 03:39:16 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
|
|
|
"fmt"
|
|
|
|
"time"
|
2015-01-26 05:29:07 +00:00
|
|
|
"flag"
|
2015-01-26 03:39:16 +00:00
|
|
|
"bufio"
|
|
|
|
"strings"
|
|
|
|
"path/filepath"
|
|
|
|
"text/template"
|
|
|
|
)
|
|
|
|
|
2015-01-26 05:29:07 +00:00
|
|
|
var searchDir string
|
2015-01-26 22:49:59 +00:00
|
|
|
var searchDirs []string
|
2015-01-26 05:03:27 +00:00
|
|
|
|
2015-01-26 03:39:16 +00:00
|
|
|
func main() {
|
|
|
|
fmt.Println("Starting...")
|
2015-01-26 05:29:07 +00:00
|
|
|
|
|
|
|
// Get any arguments
|
|
|
|
dirPtr := flag.String("d", "Code", "Directory to scan for each user.")
|
|
|
|
flag.Parse()
|
|
|
|
searchDir = *dirPtr
|
2015-01-26 22:49:59 +00:00
|
|
|
searchDirs = strings.Split(searchDir, ",")
|
2015-01-26 05:29:07 +00:00
|
|
|
|
2015-01-26 22:49:59 +00:00
|
|
|
if len(searchDirs) > 1 {
|
|
|
|
generate(findProjects(), searchDirs[0])
|
|
|
|
} else {
|
|
|
|
generate(findProjects(), searchDir)
|
|
|
|
}
|
2015-01-26 03:39:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type User struct {
|
|
|
|
Name string
|
|
|
|
Projects []Project
|
|
|
|
}
|
|
|
|
|
|
|
|
type Project struct {
|
|
|
|
Name string
|
|
|
|
Path string
|
2015-02-10 04:30:24 +00:00
|
|
|
CssClass string
|
2015-01-26 03:39:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func findProjects() map[string]User {
|
2015-01-26 22:49:59 +00:00
|
|
|
var files []string
|
2015-01-26 03:39:16 +00:00
|
|
|
users := make(map[string]User)
|
|
|
|
|
2015-01-26 22:49:59 +00:00
|
|
|
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 {
|
2015-01-26 03:39:16 +00:00
|
|
|
pparts := strings.Split(path, "/")
|
|
|
|
uname := pparts[2]
|
2015-01-26 04:08:10 +00:00
|
|
|
fname := filepath.Base(path)
|
|
|
|
|
2015-01-26 05:04:02 +00:00
|
|
|
// Exclude certain file names
|
2015-02-02 21:18:49 +00:00
|
|
|
if strings.HasPrefix(fname, ".") || strings.HasSuffix(fname, ".log") || strings.HasPrefix(fname, "README") || strings.HasSuffix(fname, "~") {
|
2015-01-26 04:08:10 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2015-01-26 05:05:53 +00:00
|
|
|
// Ensure file is other-readable
|
|
|
|
// TODO: just detect if we can actually read this, instead
|
|
|
|
info, _ := os.Stat(path)
|
2015-02-10 04:30:24 +00:00
|
|
|
mode := info.Mode()
|
|
|
|
if mode & 0004 == 0 {
|
2015-01-26 05:05:53 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2015-02-10 04:30:24 +00:00
|
|
|
// 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}
|
2015-01-26 03:39:16 +00:00
|
|
|
u, exists := users[uname]
|
|
|
|
if !exists {
|
2015-01-26 22:49:59 +00:00
|
|
|
fmt.Printf("Found %s for ~%s.\n", pparts[3], uname)
|
2015-01-26 03:39:16 +00:00
|
|
|
projs := []Project{*proj}
|
|
|
|
u = User{Name: uname, Projects: projs}
|
|
|
|
} else {
|
|
|
|
u.Projects = append(u.Projects, *proj)
|
|
|
|
}
|
|
|
|
users[uname] = u
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type Page struct {
|
2015-01-26 05:03:27 +00:00
|
|
|
FolderName string
|
2015-01-26 22:49:59 +00:00
|
|
|
Folders []string
|
2015-01-26 03:39:16 +00:00
|
|
|
Host string
|
|
|
|
Users map[string]User
|
|
|
|
Updated string
|
|
|
|
UpdatedForHumans string
|
|
|
|
}
|
|
|
|
|
2015-01-26 04:28:05 +00:00
|
|
|
func graphicalName(n string) string {
|
|
|
|
r := strings.NewReplacer("tilde", "~", "ctrl-c", "^C", "nuclear", "☢")
|
|
|
|
return r.Replace(n)
|
|
|
|
}
|
|
|
|
|
2015-01-26 22:49:59 +00:00
|
|
|
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("<strong>%s</strong>", d)
|
|
|
|
dirs[i] = d
|
|
|
|
}
|
|
|
|
return strings.Join(dirs, " or ")
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("<strong>%s</strong>", dirs[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
func generate(users map[string]User, outputFile string) {
|
2015-01-26 03:39:16 +00:00
|
|
|
fmt.Println("Generating page.")
|
|
|
|
|
2015-01-26 22:49:59 +00:00
|
|
|
f, err := os.Create(os.Getenv("HOME") + "/public_html/" + strings.ToLower(outputFile) + ".html")
|
2015-01-26 03:39:16 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
defer f.Close()
|
2015-01-26 22:49:59 +00:00
|
|
|
|
|
|
|
funcMap := template.FuncMap {
|
|
|
|
"Split": Split,
|
|
|
|
"ListDirs": ListDirs,
|
|
|
|
}
|
2015-01-26 03:39:16 +00:00
|
|
|
|
|
|
|
w := bufio.NewWriter(f)
|
2015-02-13 04:54:19 +00:00
|
|
|
template, err := template.New("").Funcs(funcMap).ParseFiles("../templates/code.html")
|
2015-01-26 03:39:16 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extra page data
|
|
|
|
host, _ := os.Hostname()
|
2015-01-26 04:27:34 +00:00
|
|
|
curTime := time.Now().UTC()
|
|
|
|
updatedReadable := curTime.Format(time.RFC1123)
|
|
|
|
updated := curTime.Format(time.RFC3339)
|
2015-01-26 03:39:16 +00:00
|
|
|
|
|
|
|
// Generate the page
|
2015-01-26 22:49:59 +00:00
|
|
|
page := &Page{FolderName: searchDirs[0], Folders: searchDirs, Host: graphicalName(host), UpdatedForHumans: updatedReadable, Updated: updated, Users: users}
|
2015-01-26 03:39:16 +00:00
|
|
|
template.ExecuteTemplate(w, "code", page)
|
|
|
|
w.Flush()
|
|
|
|
|
|
|
|
fmt.Println("DONE!")
|
|
|
|
}
|