diff --git a/main.go b/main.go index 54257db..8542e8d 100644 --- a/main.go +++ b/main.go @@ -42,7 +42,7 @@ func interactiveShell() { } func showQueue() { - entries, err := fetchQueueEntries() + queue, err := fetchQueue() if err != nil { fmt.Printf("Could not fetch queue entries: %s\n", err.Error()) os.Exit(1) @@ -50,27 +50,25 @@ func showQueue() { pagerReader, pagerWriter := io.Pipe() pagerDone := make(chan bool) go launchPager(pagerReader, pagerDone) - printQueueEntries(entries, pagerWriter) + printQueueEntries(queue, pagerWriter) pagerWriter.Close() // Wait for pager to be closed before continuing the main program <-pagerDone } -func fetchQueueEntries() ([]parser.QEntry, error) { - var entries []parser.QEntry - var err error +func fetchQueue() (parser.MailQ, error) { if settings["mailqCommandArgs"] == "" { - entries, err = readFromCmd(exec.Command(settings["mailqCommand"])) + return readFromCmd(exec.Command(settings["mailqCommand"])) } else { - entries, err = readFromCmd(exec.Command(settings["mailqCommand"], settings["mailqCommandArgs"])) + return readFromCmd(exec.Command(settings["mailqCommand"], settings["mailqCommandArgs"])) } - return entries, err } -func printQueueEntries(entries []parser.QEntry, writer io.Writer) { +func printQueueEntries(queue parser.MailQ, writer io.Writer) { + fmt.Fprintf(writer, "%d entries total (%d active, %d deferred, %d on hold)\n\n", len(queue.Entries), queue.NumActive, queue.NumDeferred, queue.NumHold) tabWriter := tabwriter.NewWriter(writer, 2, 2, 1, ' ', tabwriter.TabIndent) fmt.Fprintf(tabWriter, "| %s\t| %s\t| %s\t| %s\t| %s\t| %s\t| %s\t| %s\t| \n", "Date", "Id", "Status", "Size", "Sender", "#", "First Recipient", "Reason") - for _, entry := range entries { + for _, entry := range queue.Entries { _, writeError := fmt.Fprintf(tabWriter, "| %s\t| %s\t| %s\t| %d\t| %s\t| {%d}\t| %s\t| %s\t \n", entry.Date.Format(parser.SortableDateFormat), entry.Id, entry.Status, entry.Size, entry.Sender, len(entry.Recipients), entry.Recipients[0], entry.Reason) if writeError != nil { // A writeError is expected once the reader was closed @@ -96,17 +94,17 @@ func launchPager(reader *io.PipeReader, pagerDone chan<- bool) { } } -func readFromCmd(cmd *exec.Cmd) ([]parser.QEntry, error) { +func readFromCmd(cmd *exec.Cmd) (parser.MailQ, error) { stdout, err := cmd.StdoutPipe() if err != nil { - return nil, err + return parser.MailQ{}, err } if err := cmd.Start(); err != nil { - return nil, err + return parser.MailQ{}, err } result, resultError := parser.ParseMailQ(stdout) if err := cmd.Wait(); err != nil { - return nil, err + return parser.MailQ{}, err } return result, resultError } diff --git a/parser/parser.go b/parser/parser.go index ef2c063..9b4921e 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -4,7 +4,7 @@ import ( "bufio" "errors" "fmt" - "io" + "io" "regexp" "strconv" "strings" @@ -21,6 +21,14 @@ type QEntry struct { Reason string } +type MailQ struct { + NumTotal int + NumActive int + NumHold int + NumDeferred int + Entries []QEntry +} + const SortableDateFormat = "2006-01-02 15:04:05" func (m QEntry) ShortString() string { @@ -28,38 +36,37 @@ func (m QEntry) ShortString() string { } func (m QEntry) String() string { - var recipientSuffix string - if len(m.Recipients) > 1 { - recipientSuffix = ",..." - } + var recipientSuffix string + if len(m.Recipients) > 1 { + recipientSuffix = ",..." + } return fmt.Sprintf("[%s] %s <%s> -> {%d}<%s>%s (%s, %d bytes)", m.Date.Format(SortableDateFormat), m.Id, m.Sender, len(m.Recipients), m.Recipients[0], recipientSuffix, m.Status, m.Size) } func (m QEntry) DetailedString() string { - var reasonStr string - if m.Reason == "" { - reasonStr = "-/-" - } - return fmt.Sprintf("Id: %s\nDate: %s\nStatus: %s\nReason: %s\nSize: %d\nSender: %s\nRecipients: %s", m.Id, m.Date.Format(SortableDateFormat), m.Status, reasonStr, m.Size, m.Sender, strings.Join(m.Recipients, ", ")) + var reasonStr string + if m.Reason == "" { + reasonStr = "-/-" + } + return fmt.Sprintf("Id: %s\nDate: %s\nStatus: %s\nReason: %s\nSize: %d\nSender: %s\nRecipients: %s", m.Id, m.Date.Format(SortableDateFormat), m.Status, reasonStr, m.Size, m.Sender, strings.Join(m.Recipients, ", ")) } -func ParseMailQ(dataSource io.Reader) ([]QEntry, error) { +func ParseMailQ(dataSource io.Reader) (MailQ, error) { const dateFormat = "2006 Mon Jan _2 15:04:05" var messageIdStart = regexp.MustCompile("^[0-9A-Za-z]+[*!]? ") scanner := bufio.NewScanner(dataSource) - var line string - scanner.Scan() - line = scanner.Text() - if strings.HasPrefix(line, "Mail queue is empty") { + var line string + scanner.Scan() + line = scanner.Text() + if strings.HasPrefix(line, "Mail queue is empty") { // If mail queue is empty, there is nothing to do - return []QEntry{}, nil + return MailQ{}, nil } else if strings.HasPrefix(line, "-Queue ID-") == false { // Abort if input does not look like output from mailq(1) - return nil, errors.New("Sorry, this does not look like output from mailq(1).") + return MailQ{}, errors.New("Sorry, this does not look like output from mailq(1).") } + var queue MailQ var currentMail QEntry - var queueEntries []QEntry - for scanner.Scan() { // Read input line by line line := scanner.Text() @@ -70,12 +77,16 @@ func ParseMailQ(dataSource io.Reader) ([]QEntry, error) { } else if messageIdStart.MatchString(line) { // Handle line starting next mail in queue messageId := fields[0] + queue.NumTotal += 1 if strings.HasSuffix(messageId, "*") { + queue.NumActive += 1 currentMail.Status = "active" } else if strings.HasSuffix(messageId, "!") { currentMail.Status = "hold" + queue.NumHold += 1 } else { currentMail.Status = "deferred" + queue.NumDeferred += 1 } dateString := strconv.Itoa(time.Now().Year()) + " " + strings.Join(fields[2:6], " ") mailDate, _ := time.Parse(dateFormat, dateString) @@ -95,13 +106,13 @@ func ParseMailQ(dataSource io.Reader) ([]QEntry, error) { } else if len(fields) == 0 { // If the next line is empty, make sure to push current mail to list // and create a new struct for the next mail to process - queueEntries = append(queueEntries, currentMail) + queue.Entries = append(queue.Entries, currentMail) currentMail = QEntry{} } } if scanner.Err() != nil { // If the scanner failed, let our caller know. - return nil, errors.New("Something went wrong with reading from stdin. Sorry!") + return MailQ{}, errors.New("Something went wrong with reading from stdin. Sorry!") } - return queueEntries, nil + return queue, nil }