diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..89dcd6b --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module tildegit.org/sloum/mailcap + +go 1.10 diff --git a/mailcap.go b/mailcap.go new file mode 100644 index 0000000..ec674aa --- /dev/null +++ b/mailcap.go @@ -0,0 +1,171 @@ +package mailcap + +import ( + "os" + "strings" + "fmt" + "bufio" +) + +type Entry map[string]string +type Fields []Entry +type Cap map[string]Fields +type Mailcap struct { + Caps Cap +} + +func NewMailcap() *Mailcap { + mc := Mailcap{Caps{}} + mc.getCaps() + return &mc +} + +(m *Mailcap) func ShowAll() { + fmt.Print(m.Caps) +} + +(m *Mailcap) func Show(mime string) { + if v, ok := m.Caps["mime"] { + fmt.Println(mime + ":") + fmt.Print(v) + } else { + fmt.Printf("Cannot find %s\n", mime) + } +} + +(m *Mailcap) func getCaps() { + lnNum := 0 + for mailcapFile := range getMailcapFileList() { + file, err := os.Open(mailcapFile) + if err != nil { + continue + } + moreCaps, lnNum, err := readMailcapFile(file, lnNum) + for k, v := range moreCaps { + if v, ok := m.Caps[key]; ok { + m.Caps[key] = m.Caps[key] + value + } else { + m.Caps[key] = value + } + } + file.close() + } +} + + +// Retrieve a slice of strings with all mailcap files +// found on the system. +func getMailcapFileList() (mCapSlice []string) { + var home string = "." + if val, ok := os.LookupEnv('MAILCAPS'); ok { + mCapSlice = strings.Split(val, string(os.PathListSeparator)) + } else { + if val, ok := os.LookupEnv('HOME'); ok { + home = val + } + mCapSlice = []string{ + home + "/.mailcap", + "/etc/mailcap", + "/usr/etc/mailcap", + "/usr/local/etc/mailcap" + } + } +} + +func readMailcapFile(f *os.File,ln int) (Cap, int) { + caps := Cap{} + reader := bufio.NewReader(f) + for { + l, e := reader.ReadString('\n') + if e != nil || l[0] == '#' || strings.TrimSpace(l) == "" { + continue + } + + // Handle continuations for long lines + nxtLn := l + for ;nxtLn[len(nxtLn)-3:] == "\\\n"; { + nxtLn, er = reader.ReadString('\n') + if er != nil || strings.TrimSpace(nxtLn) == "" { + nxtLn = "\n" + } + l = l[:len(l)-2] + nxtLn + } + + // Parse the line + key, fields, err := parseLine(l) + if err != nil { + continue + } + if ln >= 0 { + fields["lineno"] = ln + ln += 1 + } + types := strings.Split(key, "/") + for t := range types { + t = strings.TrimSpace(t) + } + key = strings.Join(types, "/") + key = strings.ToLower(key) + if _, ok := caps[key] { + append(caps[key], fields) + } else { + caps[key] = fields + } + return caps, ln + } + +} + +func parseLine(ln) (string, Entry, error) { + outputFields := Entry{} + i := 0 + n := len(ln) + fields := make([]string, 0, 10) + for ;i < n; { + field, i = parseField(ln, i, n) + append(fields, field) + i += 1 + } + if len(fields) < 2 { + return "", outputFields,fmt.Errorf("Not enough fields present in line") + } + key, view := fields[0], fields[1] + outputFields["view"] = view + rest := make([]string,0,0) + if len(fields) > 2 { + rest = fields[2:] + } + for f := range rest { + var fkey, fvalue string + i = string.Index(f, r"=") + if i < 0 { + fkey = f + fvalue = "" + } else { + fkey = strings.TrimSpace(f[:i]) + fvalue = strings.TrimSpace(f[i+1:]) + } + if _, ok := outputFields[fkey]; !ok { + // If the key doesnt exist in the map, add it + outputFields[fkey] = fvalue + } + } + return key, outputFields, nil +} + +func parseField(ln string, i, n int) (string, int) { + // get one key-value pair from mailcap entry + start := i + for ;i < n; { + c := ln[i] + if c == ';' { + break + } else if c == '\\' { + i += 2 + } else { + i += 1 + } + } + return strings.TrimSpace(line[start:i]), i +} +