20 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
20 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
21 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
21 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
22 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
22 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
23 */ |
23 */ |
24 |
24 |
25 int main() { |
25 #include "settings.h" |
|
26 |
|
27 #include <cstdlib> |
|
28 #include <cstdio> |
|
29 #include <cstring> |
|
30 #include <cerrno> |
|
31 |
|
32 static void print_help() { |
|
33 fputs( |
|
34 "Usage: fallusmeter [OPTION]... [PATH]...\n\n" |
|
35 "Options:\n" |
|
36 " -h, --help Print this help message\n" |
|
37 " -d, --depth <num> The search depth (default: 1, max: 255)\n" |
|
38 " -n, --no-pull Do not pull the repositories\n" |
|
39 " -s, --separate Output a separate heat map for each repository\n" |
|
40 " -y, --year <year> The year for which to create the heat map\n" |
|
41 " --hg <path> Path to hg binary (default: /usr/bin/hg)\n" |
|
42 " --git <path> Path to git binary (default: /usr/bin/git)\n\n" |
|
43 "Scans all specified paths recursively for Mercurial and Git repositories and\n" |
|
44 "creates a commit heat map for the specified \033[1myear\033[22m or the current year.\n" |
|
45 "By default, the recursion \033[1mdepth\033[22m is one, meaning that this tool assumes that\n" |
|
46 "each \033[1mpath\033[22m is either a repository or contains repositories as subdirectories.\n" |
|
47 "You can change the \033[1mdepth\033[22m to support other directory structures.\n\n" |
|
48 "When you do not specify \033[1m--no-pull\033[22m, this tool will execute the pull command\n" |
|
49 "(and for hg the update command) before retrieving the commit log, assuming\n" |
|
50 "to be on the default branch with \033[4mno uncommitted changes\033[24m.\n\n" |
|
51 "The default output format prints an HTML page to stdout. In all cases, a\n" |
|
52 "separate heat map is generated for each author, but all repositories are,\n" |
|
53 "accumulated unless the \033[1m--separate\033[22m option is specified.\n" |
|
54 , stderr); |
|
55 } |
|
56 |
|
57 static bool chk_arg(const char* arg, const char* opt1, const char* opt2) { |
|
58 return strcmp(arg, opt1) == 0 || (opt2 != nullptr && strcmp(arg, opt2) == 0); |
|
59 } |
|
60 |
|
61 template<typename T> |
|
62 static bool parse_unsigned(const char *str, T *result, unsigned long max) { |
|
63 char *endptr; |
|
64 errno = 0; |
|
65 unsigned long d = strtoul(str, &endptr, 10); |
|
66 if (*endptr != '\0' || errno == ERANGE) return true; |
|
67 if (d < max) { |
|
68 *result = d; |
|
69 return false; |
|
70 } else { |
|
71 return true; |
|
72 } |
|
73 } |
|
74 |
|
75 static int parse_args(fm::settings &settings, int argc, char *argv[]) { |
|
76 for (int i = 1; i < argc; i++) { |
|
77 if (chk_arg(argv[i], "-h", "--help")) { |
|
78 print_help(); |
|
79 return -1; |
|
80 } else if (chk_arg(argv[i], "-d", "--depth")) { |
|
81 if (i + 1 >= argc || parse_unsigned(argv[++i], &settings.depth, 256)) { |
|
82 fputs("missing or invalid depth\n", stderr); |
|
83 return -1; |
|
84 } |
|
85 } else if (chk_arg(argv[i], "-y", "--year")) { |
|
86 if (i + 1 >= argc || parse_unsigned(argv[++i], &settings.year, 9999)) { |
|
87 fputs("missing or invalid year\n", stderr); |
|
88 return -1; |
|
89 } |
|
90 } else if (chk_arg(argv[i], "-n", "--no-pull")) { |
|
91 settings.nopull = true; |
|
92 } else if (chk_arg(argv[i], "-s", "--separate")) { |
|
93 settings.separate = true; |
|
94 } else if (chk_arg(argv[i], "--hg", nullptr)) { |
|
95 if (i + 1 < argc) { |
|
96 settings.hg.assign(argv[++i]); |
|
97 } else { |
|
98 fputs("--hg is expecting a path\n", stderr); |
|
99 return -1; |
|
100 } |
|
101 } else if (chk_arg(argv[i], "--git", nullptr)) { |
|
102 if (i + 1 < argc) { |
|
103 settings.git.assign(argv[++i]); |
|
104 } else { |
|
105 fputs("--git is expecting a path\n", stderr); |
|
106 return -1; |
|
107 } |
|
108 } else if (argv[i][0] == '-') { |
|
109 fprintf(stderr, "Unknown option: %s\n", argv[i]); |
|
110 return -1; |
|
111 } else { |
|
112 settings.paths.emplace_back(argv[i]); |
|
113 } |
|
114 } |
|
115 |
|
116 if (settings.paths.empty()) { |
|
117 settings.paths.emplace_back("./"); |
|
118 } |
|
119 |
26 return 0; |
120 return 0; |
27 } |
121 } |
28 |
122 |
|
123 int main(int argc, char *argv[]) { |
|
124 fm::settings settings; |
|
125 |
|
126 if (parse_args(settings, argc, argv)) { |
|
127 return EXIT_FAILURE; |
|
128 } |
|
129 |
|
130 return EXIT_SUCCESS; |
|
131 } |
|
132 |