1// Copyright (c) 2014-2018 Bitmark Inc.
2// Use of this source code is governed by an ISC
3// license that can be found in the LICENSE file.
4
5// replacement for os.Exit(N) that runs defers
6//
7// Original article: http://soniacodes.wordpress.com/2011/04/28/deferred-functions-and-an-exit-code/
8//
9// This is modified version to pass an integer status to os.Exit
10//
11// usage: as the first line of main add:
12//   defer exitwithstatus.HandleFatal()
13//
14// to exit with a particular integer value:
15//   exitwithstatus.Exit(42)
16package exitwithstatus
17
18import (
19	"fmt"
20	"os"
21	"path/filepath"
22)
23
24type fatal struct {
25	err interface{}
26}
27
28// Exit with the integer status value
29//
30// e.g to exit calling all defers with the value three: exitwithstatus.Exit(3)
31func Exit(status int) {
32	panic(fatal{status})
33}
34
35// This must be the first defer in the program
36//
37// i.e. the first line of main must be:  defer exitwithstatus.Handle()
38func Handler() {
39	if err := recover(); err != nil {
40		if status, ok := err.(fatal); ok {
41			s := status.err.(int)
42			os.Exit(s)
43		}
44		panic(err) // this is some other kind of panic so pass it up the chain
45	}
46}
47
48// print a message to stderr and exit wit error status
49func Message(message string, args ...interface{}) {
50
51	m := "failed"
52
53	// if a blank message print a simple usage message
54	if message == "" {
55
56		// tidy up the program name
57		programName := filepath.Base(os.Args[0])
58
59		m = fmt.Sprintf("Use %s -h to show usage", programName)
60	} else {
61
62		// user supplied message
63		m = fmt.Sprintf(message, args...)
64	}
65
66	// print to stderr with final '\n'
67	fmt.Fprintln(os.Stderr, m)
68
69	Exit(1)
70}
71