diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c35f54f..9f1e34f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,6 +1,9 @@ on: workflow_dispatch: + pull_request: + branches: [ main ] push: + branches: [ main ] jobs: check_build: diff --git a/.github/workflows/flake-check.yml b/.github/workflows/flake-check.yml index 5df2226..d59d5f9 100644 --- a/.github/workflows/flake-check.yml +++ b/.github/workflows/flake-check.yml @@ -1,6 +1,9 @@ on: workflow_dispatch: + pull_request: + branches: [ main ] push: + branches: [ main ] paths: - "**.nix" - "**.lock" diff --git a/.github/workflows/fmt-check.yml b/.github/workflows/fmt-check.yml index f39423d..d41ce66 100644 --- a/.github/workflows/fmt-check.yml +++ b/.github/workflows/fmt-check.yml @@ -1,6 +1,9 @@ on: workflow_dispatch: + pull_request: + branches: [ main ] push: + branches: [ main ] paths: - "**.c" - "**.h" @@ -26,4 +29,4 @@ jobs: | xargs \ | tee /dev/stderr \ | wc -c) - [ $out -ge 1 ] && exit 1 || exit 0 + [ $out -gt 1 ] && exit 1 || exit 0 diff --git a/.gitignore b/.gitignore index e12f4d8..ca890b5 100644 --- a/.gitignore +++ b/.gitignore @@ -90,3 +90,4 @@ result # config .cache compile_commands.json +.pre-commit-config.yaml diff --git a/commons.mk b/commons.mk index 1193c31..d9a1baf 100644 --- a/commons.mk +++ b/commons.mk @@ -4,7 +4,7 @@ CFLAGS := @$/compile_flags.txt CFLAGS += -ffunction-sections -fdata-sections CFLAGS += -Wp,-U_FORTIFY_SOURCE -CFLAGS += -iquote src +CFLAGS += -iquote $/src LDFLAGS := -fwhole-program -flto LDFLAGS += -Wl,--gc-sections diff --git a/flake.lock b/flake.lock index 39b28ea..e2bf973 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,21 @@ { "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" @@ -18,6 +34,27 @@ "type": "github" } }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1712963716, @@ -34,10 +71,53 @@ "type": "github" } }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1710695816, + "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "614b4613980a522ba49f0d194531beddbb7220d3", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": [ + "flake-utils" + ], + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1712897695, + "narHash": "sha256-nMirxrGteNAl9sWiOhoN5tIHyjBbVi5e2tgZUgZlK3Y=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "40e6053ecb65fcbf12863338a6dcefb3f55f1bf8", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, "root": { "inputs": { "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks" } }, "systems": { diff --git a/flake.nix b/flake.nix index d8f4229..d739ca4 100644 --- a/flake.nix +++ b/flake.nix @@ -2,12 +2,20 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; + pre-commit-hooks = { + url = "github:cachix/git-hooks.nix"; + inputs = { + nixpkgs.follows = "nixpkgs"; + flake-utils.follows = "flake-utils"; + }; + }; }; outputs = { self, nixpkgs, flake-utils, + pre-commit-hooks, }: flake-utils.lib.eachSystem [ "aarch64-darwin" @@ -16,11 +24,27 @@ "x86_64-linux" ] (system: let pkgs = nixpkgs.legacyPackages.${system}; - in { + in rec { formatter = pkgs.alejandra; + checks = let + pre-commit-check = pre-commit-hooks.lib.${system}.run { + src = ./.; + hooks = { + alejandra.enable = true; + clang-format.enable = true; + }; + }; + in + if (builtins.elem system flake-utils.lib.defaultSystems) + then {inherit pre-commit-check;} + else {}; + devShells.default = pkgs.mkShell { - # inputsFrom = pkgs.lib.attrsets.attrValues packages; + inherit (checks.pre-commit-check) shellHook; + + hardeningDisable = ["fortify"]; + inputsFrom = pkgs.lib.attrsets.attrValues packages; packages = with pkgs; [ bear python3Packages.compiledb diff --git a/src/echo/echo.c b/src/echo/echo.c index e6bc26d..8038e12 100644 --- a/src/echo/echo.c +++ b/src/echo/echo.c @@ -7,10 +7,7 @@ #define VERSION "1.0.0" #define AUTHOR "Hoorad Farrokh (proh14)" -#define print_version() \ - do { \ - printf("%s\nversion: %s\nby: %s\n", NAME, VERSION, AUTHOR); \ - } while (0) +#include "version_info.h" int isoctal(int c) { return (c >= '0' && c <= '7'); } diff --git a/src/getopt.c b/src/getopt.c new file mode 100644 index 0000000..ae24e4e --- /dev/null +++ b/src/getopt.c @@ -0,0 +1,96 @@ +#include "./getopt.h" +#include +#include + +char *optarg = NULL; +int optind = 1, opterr = 0, optopt = '\0'; + +static void getopt_printerr(const char *msg) { + if (opterr) { + fprintf(stderr, "%s", msg); + } +} + +static int getopt_in(char d, const char *str) { + int i = 0; + while (str[i] != '\0') { + if (d == str[i] && str[i] != ':') { + return i; + } + i++; + } + return 0; +} + +static void getopt_exchange(char *argv[], int i, int j) { + char *tmp = argv[i]; + argv[i] = argv[j]; + argv[j] = tmp; +} + +int getopt(int argc, char **argv, const char *optstring) { + int c; + static char *nextchar = NULL; + static int coropt = 1; + if (!coropt) { + coropt = 1; + } + + if (coropt >= argc || argv[coropt] == NULL) { + return -1; + } + + while (argv[coropt] && argv[coropt][0] != '-') { + coropt++; + } + + if (nextchar == NULL || *nextchar == '\0') { + if (coropt >= argc) { + return -1; + } + nextchar = &argv[coropt][1]; + } + + int idx; + if (!((idx = getopt_in(*nextchar, optstring)) >= 0)) { + getopt_printerr("invalid option\n"); + optopt = *nextchar; + return '?'; + } + + c = *nextchar++; + if (*nextchar == '\0') { + coropt++; + nextchar = NULL; + } + + if (optstring[idx + 1] != ':') { + coropt--; + optind++; + goto exit; + } + + if (nextchar != NULL && *nextchar != '\0') { + coropt++; + } + + if (coropt >= argc || argv[coropt][0] == '-') { + getopt_printerr("option requires an argument\n"); + optopt = *nextchar; + return '?'; + } + + optarg = argv[coropt]; + +exit: { + int i = coropt; + int j = coropt - 1; + while (j >= 0 && argv[j][0] != '-') { + getopt_exchange(argv, i, j); + i--; + j--; + } +} + optind++; + return c; +} diff --git a/src/getopt.h b/src/getopt.h new file mode 100644 index 0000000..d697f17 --- /dev/null +++ b/src/getopt.h @@ -0,0 +1,23 @@ +// Reimplementation of GNU getopt +#ifndef _GETOPT_H_ +#define _GETOPT_H_ + +struct option { + const char *name; + int has_arg; + int *flag; + int val; +}; + +extern char *optarg; +extern int optind, opterr, optopt; + +int getopt(int argc, char **argv, const char *optstring); + +int getopt_long(int argc, char *const argv[], const char *optstring, + const struct option *longopts, int *longindex); + +int getopt_long_only(int argc, char *const argv[], const char *optstring, + const struct option *longopts, int *longindex); + +#endif // _GETOPT_H_ diff --git a/src/ls/ls_main.c b/src/ls/ls_main.c index 50b8ceb..d50813a 100644 --- a/src/ls/ls_main.c +++ b/src/ls/ls_main.c @@ -8,10 +8,7 @@ #define VERSION "1.0.0" #define AUTHOR "Yohann Boniface (Sigmanificient)" -#define print_version() \ - do { \ - printf("%s\nversion: %s\nby: %s\n", NAME, VERSION, AUTHOR); \ - } while (0) +#include "version_info.h" static const char FLAGLIST[] = "alRdrt"; static char DEFAULT_LOCATION[] = "."; @@ -62,8 +59,10 @@ int main(int argc, char **argv) { int err = 0; for (int i = 0; argv[i] != NULL; i++) - if (!strcmp(argv[i], "--version")) - return printf(VERSION), EXIT_SUCCESS; + if (!strcmp(argv[i], "--version")) { + print_version(); + return EXIT_SUCCESS; + } flags = compose_flaglist(argc, argv); db.entries = malloc(db.size * sizeof(*db.entries)); if (db.entries == NULL) diff --git a/src/rmdir/rmdir.c b/src/rmdir/rmdir.c index b373dc3..fcb715f 100644 --- a/src/rmdir/rmdir.c +++ b/src/rmdir/rmdir.c @@ -1,19 +1,16 @@ +#include +#include +#include #include -#include #include -#include -#include +#include #include -#include #define NAME "rmdir (canoutils)" #define VERSION "1.0.0" #define AUTHOR "tim-tm" -#define print_version() \ - do { \ - printf("%s\nversion: %s\nby: %s\n", NAME, VERSION, AUTHOR); \ - } while (0) +#include "version_info.h" bool ignore_fail = false; bool parents = false; @@ -41,10 +38,11 @@ int rm_dir(char *dirname) { int n = 0; struct dirent *dent; while ((dent = readdir(dir)) != NULL) { - if (++n > 2) break; + if (++n > 2) + break; } closedir(dir); - + if (n <= 2) { // Directory is empty (the two entries are '.' and '..') if (remove(dirname) != 0) { fprintf(stderr, "Failed to remove '%s': %s\n", dirname, strerror(errno)); @@ -60,17 +58,19 @@ int rm_dir(char *dirname) { } void strip_off_slash(char *str) { - if (str == NULL) return; + if (str == NULL) + return; size_t str_len = strlen(str); - if (str[str_len-1] == '/') { - str[str_len-1] = '\0'; + if (str[str_len - 1] == '/') { + str[str_len - 1] = '\0'; } } int main(int argc, char **argv) { if (argc <= 1) { - fprintf(stderr, "Not enough arguments.\nSee rmdir --help for more information.\n"); + fprintf(stderr, + "Not enough arguments.\nSee rmdir --help for more information.\n"); return 1; } @@ -87,17 +87,18 @@ int main(int argc, char **argv) { return rm_dir(argv[1]); } - for (int i = 1; i < argc-1; ++i) { + for (int i = 1; i < argc - 1; ++i) { if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--parents") == 0) { parents = true; - } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) { + } else if (strcmp(argv[i], "-v") == 0 || + strcmp(argv[i], "--verbose") == 0) { verbose = true; } else if (strcmp(argv[i], "--ignore-fail-on-non-empty") == 0) { ignore_fail = true; } } - char *str = argv[argc-1]; + char *str = argv[argc - 1]; if (parents) { strip_off_slash(str); @@ -108,10 +109,11 @@ int main(int argc, char **argv) { if (strip == NULL) { i = 0; } else { - i = strip-str+1; + i = strip - str + 1; } - if (rm_dir(str) != 0) return 1; + if (rm_dir(str) != 0) + return 1; str[i] = '\0'; } return 0; @@ -119,4 +121,3 @@ int main(int argc, char **argv) { return rm_dir(str); } } - diff --git a/src/version_info.h b/src/version_info.h new file mode 100644 index 0000000..9a911f9 --- /dev/null +++ b/src/version_info.h @@ -0,0 +1,13 @@ +#ifndef VERSION_INFO_H +#define VERSION_INFO_H + +#if !defined(NAME) || !defined(VERSION) || !defined(AUTHOR) +#error "missing NAME, VERSION or AUTHOR definition" +#endif + +#define print_version() \ + do { \ + printf("%s\nversion: %s\nby: %s\n", NAME, VERSION, AUTHOR); \ + } while (0) + +#endif