add settings and CLI

Tue, 21 Jan 2025 18:09:14 +0100

author
Mike Becker <universe@uap-core.de>
date
Tue, 21 Jan 2025 18:09:14 +0100
changeset 1
9bf126bc825c
parent 0
b4b281ef2d0f
child 2
7384ebae6b7c

add settings and CLI

src/Makefile file | annotate | diff | comparison | revisions
src/main.cpp file | annotate | diff | comparison | revisions
src/settings.h file | annotate | diff | comparison | revisions
--- a/src/Makefile	Sat Jan 11 13:45:38 2025 +0100
+++ b/src/Makefile	Tue Jan 21 18:09:14 2025 +0100
@@ -40,7 +40,7 @@
 
 FORCE:
 
-../build/main.o: main.cpp
+../build/main.o: main.cpp settings.h
 	@echo "Compiling $<"
 	$(CXX) -o $@ $(CXXFLAGS)  -c $<
 
--- a/src/main.cpp	Sat Jan 11 13:45:38 2025 +0100
+++ b/src/main.cpp	Tue Jan 21 18:09:14 2025 +0100
@@ -22,7 +22,111 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-int main() {
+#include "settings.h"
+
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include <cerrno>
+
+static void print_help() {
+    fputs(
+        "Usage: fallusmeter [OPTION]... [PATH]...\n\n"
+        "Options:\n"
+        "   -h, --help             Print this help message\n"
+        "   -d, --depth <num>      The search depth (default: 1, max: 255)\n"
+        "   -n, --no-pull          Do not pull the repositories\n"
+        "   -s, --separate         Output a separate heat map for each repository\n"
+        "   -y, --year <year>      The year for which to create the heat map\n"
+        "       --hg <path>        Path to hg binary (default: /usr/bin/hg)\n"
+        "       --git <path>       Path to git binary (default: /usr/bin/git)\n\n"
+        "Scans all specified paths recursively for Mercurial and Git repositories and\n"
+        "creates a commit heat map for the specified \033[1myear\033[22m or the current year.\n"
+        "By default, the recursion \033[1mdepth\033[22m is one, meaning that this tool assumes that\n"
+        "each \033[1mpath\033[22m is either a repository or contains repositories as subdirectories.\n"
+        "You can change the \033[1mdepth\033[22m to support other directory structures.\n\n"
+        "When you do not specify \033[1m--no-pull\033[22m, this tool will execute the pull command\n"
+        "(and for hg the update command) before retrieving the commit log, assuming\n"
+        "to be on the default branch with \033[4mno uncommitted changes\033[24m.\n\n"
+        "The default output format prints an HTML page to stdout. In all cases, a\n"
+        "separate heat map is generated for each author, but all repositories are,\n"
+        "accumulated unless the \033[1m--separate\033[22m option is specified.\n"
+        , stderr);
+}
+
+static bool chk_arg(const char* arg, const char* opt1, const char* opt2) {
+    return strcmp(arg, opt1) == 0 || (opt2 != nullptr && strcmp(arg, opt2) == 0);
+}
+
+template<typename T>
+static bool parse_unsigned(const char *str, T *result, unsigned long max) {
+    char *endptr;
+    errno = 0;
+    unsigned long d = strtoul(str, &endptr, 10);
+    if (*endptr != '\0' || errno == ERANGE) return true;
+    if (d < max) {
+        *result = d;
+        return false;
+    } else {
+        return true;
+    }
+}
+
+static int parse_args(fm::settings &settings, int argc, char *argv[]) {
+    for (int i = 1; i < argc; i++) {
+        if (chk_arg(argv[i], "-h", "--help")) {
+            print_help();
+            return -1;
+        } else if (chk_arg(argv[i], "-d", "--depth")) {
+            if (i + 1 >= argc || parse_unsigned(argv[++i], &settings.depth, 256)) {
+                fputs("missing or invalid depth\n", stderr);
+                return -1;
+            }
+        } else if (chk_arg(argv[i], "-y", "--year")) {
+            if (i + 1 >= argc || parse_unsigned(argv[++i], &settings.year, 9999)) {
+                fputs("missing or invalid year\n", stderr);
+                return -1;
+            }
+        } else if (chk_arg(argv[i], "-n", "--no-pull")) {
+            settings.nopull = true;
+        } else if (chk_arg(argv[i], "-s", "--separate")) {
+            settings.separate = true;
+        } else if (chk_arg(argv[i], "--hg", nullptr)) {
+            if (i + 1 < argc) {
+                settings.hg.assign(argv[++i]);
+            } else {
+                fputs("--hg is expecting a path\n", stderr);
+                return -1;
+            }
+        } else if (chk_arg(argv[i], "--git", nullptr)) {
+            if (i + 1 < argc) {
+                settings.git.assign(argv[++i]);
+            } else {
+                fputs("--git is expecting a path\n", stderr);
+                return -1;
+            }
+        } else if (argv[i][0] == '-') {
+            fprintf(stderr, "Unknown option: %s\n", argv[i]);
+            return -1;
+        } else {
+            settings.paths.emplace_back(argv[i]);
+        }
+    }
+
+    if (settings.paths.empty()) {
+        settings.paths.emplace_back("./");
+    }
+
     return 0;
 }
 
+int main(int argc, char *argv[]) {
+    fm::settings settings;
+
+    if (parse_args(settings, argc, argv)) {
+        return EXIT_FAILURE;
+    }
+
+    return EXIT_SUCCESS;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/settings.h	Tue Jan 21 18:09:14 2025 +0100
@@ -0,0 +1,48 @@
+/* Copyright 2025 Mike Becker. All rights reserved.
+*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#ifndef SETTINGS_H
+#define SETTINGS_H
+
+#include <string>
+#include <vector>
+
+namespace fm {
+
+constexpr static short SETTINGS_CURRENT_YEAR = 0;
+
+struct settings {
+    std::string hg{"/usr/bin/hg"};
+    std::string git{"/usr/bin/git"};
+    std::vector<std::string> paths;
+    unsigned char depth = 1;
+    bool nopull = false;
+    bool separate = false;
+    unsigned short year = SETTINGS_CURRENT_YEAR;
+};
+
+}
+
+#endif //SETTINGS_H

mercurial