diff --git a/Makefile b/Makefile index bd1837442555a07d42035c231a90e1aa63618830..64348f9a873f078dcc57306cce2c36ea757d47c6 100644 --- a/Makefile +++ b/Makefile @@ -8,28 +8,24 @@ LIBS = -lssl -lcrypto BIN = bin # Get source and object -SRCS = $(filter-out $(wildcard mdtest.c), $(wildcard *.c */*.c)) +SRCS = $(filter-out mdtest.c, $(wildcard *.c */*.c)) OBJS = $(addprefix $(BIN)/, $(SRCS:.c=.o)) -SRCS_TEST = $(filter-out $(wildcard main.c */main.c gfx/*), $(wildcard *.c */*.c)) -OBJS_TEST = $(addprefix $(BIN)/, $(SRCS_TEST:.c=.o)) + +.PHONY: digest libdigest all clean # Create the target -main: $(OBJS) - $(CC) $(CFLAGS) -o $(BIN)/$@ $^ $(LIBS) $(LDFLAGS) - ./$(BIN)/$@ $(ARGS) +digest: $(OBJS) + $(CC) $(CFLAGS) -o $(BIN)/digest $(addprefix $(BIN)/, $(notdir $^)) $(LIBS) $(LDFLAGS) -# Convert the source in object, but before all, run `$(BIN)` aka mkdir +# Convert the source in object $(BIN)/%.o: %.c - mkdir -p $(@D) - $(CC) $(CFLAGS) -o $@ -c $< $(LDFLAGS) + mkdir -p $(BIN) + $(CC) $(CFLAGS) -o $(BIN)/$(notdir $@) -c $< $(LDFLAGS) # Echo the source and object values help: @echo "src: $(SRCS)" @echo "obj: $(OBJS)" - @echo "obj_test: $(OBJ_TEST)" clean: rm -rf $(BIN) - -.PHONY: test-draw help clean main diff --git a/digest/digest.c b/digest/digest.c index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3c19f31a09370c67cff8cd216943aa36e7f268b9 100644 --- a/digest/digest.c +++ b/digest/digest.c @@ -0,0 +1,99 @@ +#include "digest.h" + +#define BUF_MAXSIZE 4096 + +//========================= +// PRIVATE +//========================= + +/** + * @brief Stringify the given bytes digest + * + * @param md_len + * @param md_value + * @return char* + */ +static char *stringify_digest(unsigned int md_len, unsigned char md_value[]) { + size_t hexlen = 2; + size_t outdigestlen = md_len * hexlen; + + char *digest = malloc(outdigestlen + 1); + char *hex = digest; + + for (size_t i = 0; i < md_len; i++) { + hex += sprintf(hex, "%02x", md_value[i]); + } + + return digest; +} + +/** + * @brief Initialize the EVP context + * + * @param digest_method + * @return EVP_MD_CTX* + */ +EVP_MD_CTX *EVP_context_init(char *digest_method) { + EVP_MD_CTX *mdctx; + const EVP_MD *md; + + if (digest_method == NULL) { + fprintf(stderr, "Digest method not provided.\n"); + exit(ENOMSG); + } + + md = EVP_get_digestbyname(digest_method); + if (md == NULL) { + fprintf(stderr, "Digest method not found.\n"); + exit(ENOPROTOOPT); + } + + mdctx = EVP_MD_CTX_new(); + EVP_DigestInit_ex2(mdctx, md, NULL); + + return mdctx; +} + +//========================= +// PUBLIC +//========================= + +char *str_digest(char *foo, char *digest_method) { + EVP_MD_CTX *mdctx = EVP_context_init(digest_method); + unsigned char md_value[EVP_MAX_MD_SIZE]; + unsigned int md_len; + + EVP_DigestUpdate(mdctx, foo, strlen(foo)); + EVP_DigestFinal_ex(mdctx, md_value, &md_len); + EVP_MD_CTX_free(mdctx); + + return stringify_digest(md_len, md_value); +} + +char *file_digest(char *filename, char *digest_method) { + EVP_MD_CTX *mdctx = EVP_context_init(digest_method); + unsigned char md_value[EVP_MAX_MD_SIZE]; + unsigned int md_len; + + FILE *f; + char buf[BUF_MAXSIZE]; + + // case of STDIN + if (filename == NULL) { + f = stdin; + } else { + if ((f = fopen(filename, "r")) == NULL) { + perror(filename); + exit(ENOENT); + } + } + + while (fgets(buf, BUF_MAXSIZE, f) != NULL) { + EVP_DigestUpdate(mdctx, buf, strlen(buf)); + } + + EVP_DigestFinal_ex(mdctx, md_value, &md_len); + EVP_MD_CTX_free(mdctx); + + return stringify_digest(md_len, md_value); +} diff --git a/digest/digest.h b/digest/digest.h index cb35c12962a75cf9f0fc76bdca3fab38fafef7c1..815f112425acb3e426bfeef90d6e719ee5dccf54 100644 --- a/digest/digest.h +++ b/digest/digest.h @@ -1,4 +1,26 @@ #ifndef DIGEST_H #define DIGEST_H +#include <errno.h> +#include <openssl/evp.h> +#include <string.h> + +/** + * @brief Make the digest of the given string + * + * @param foo + * @param digest_method + * @return char* + */ +char *str_digest(char *foo, char *digest_method); + +/** + * @brief Make the digest of the given file + * + * @param filename If null, read STDIN + * @param digest_method + * @return char* + */ +char *file_digest(char *filename, char *digest_method); + #endif // DIGEST_H diff --git a/main.c b/main.c index b24433f7e9b44c1818127001a149d094cad3ee2d..e57b25b68e7fe2c2c932be3a4f01a56e9a6f3c6b 100644 --- a/main.c +++ b/main.c @@ -1,15 +1,79 @@ #include <errno.h> #include <stdio.h> #include <stdlib.h> -#include <unistd.h> #include "digest/digest.h" #include "options/opt.h" +/** + * @brief Join an array of string + * @see https://stackoverflow.com/a/4681415 + * + * @param foo + * @param count + * @return char* + */ +char *join(char *foo[], int count) { + char *out = NULL; + size_t total_len = 0; + + for (int i = 0; i < count; i++) + total_len += strlen(foo[i]); + total_len += count - 1; // seperator + total_len++; // string terminator + + out = malloc(total_len); + out[0] = '\0'; + + for (int i = 0; i < count; i++) { + strcat(out, foo[i]); + if (i < (count - 1)) + strcat(out, " "); + } + + return out; +} + int main(int argc, char *argv[]) { - for (int i = 1; i < argc; i++) { - printf("%s\n", argv[i]); + opt *opts = verify_options(argc, argv); + char *digest; + + switch (opts->flag) { + case W_FLAG: + for (int i = 0; i < opts->extra_count; i++) { + digest = str_digest(opts->extra[i], opts->digest_method); + + printf("%s\t%s\n", digest, opts->extra[i]); + free(digest); + } + break; + + case F_FLAG: + for (int i = 0; i < opts->extra_count; i++) { + digest = file_digest(opts->extra[i], opts->digest_method); + + printf("%s\t%s\n", digest, opts->extra[i]); + free(digest); + } + break; + + case S_FLAG: + digest = file_digest(NULL, opts->digest_method); + + printf("%s\n", digest); + free(digest); + break; + + default: + char *foo = join(opts->extra, opts->extra_count); + digest = str_digest(foo, opts->digest_method); + + printf("%s\t%s\n", digest, foo); + free(digest); + break; } + discard_options(opts); + exit(EXIT_SUCCESS); } diff --git a/options/opt.c b/options/opt.c index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1d96720c9318ad8ba548e531d45f38c6d01d7c59 100644 --- a/options/opt.c +++ b/options/opt.c @@ -0,0 +1,63 @@ +#include "opt.h" + +//========================= +// PUBLIC +//========================= + +opt *verify_options(int argc, char *argv[]) { + int option; + opt *verified = malloc(sizeof(opt)); + verified->flag = 0; + verified->digest_method = NULL; + + while ((option = getopt(argc, argv, "wfst:")) != -1) { + // Argument list too long + if (verified->flag != 0 && option != 't') { + printf("Cannot choose mulitple options.\n"); + exit(E2BIG); + } + + switch (option) { + case 'w': + verified->flag = 1 << 0; + break; + + case 'f': + verified->flag = 1 << 1; + break; + + case 's': + verified->flag = 1 << 2; + break; + + case 't': + // optarg hold the value of the current option + verified->digest_method = optarg; + break; + + // Invalid argument + default: + exit(EINVAL); + break; + } + } + + if (verified->digest_method == NULL) { + verified->digest_method = "SHA1"; + } + + verified->extra_count = argc - optind; + verified->extra = malloc(sizeof(char *) * verified->extra_count); + // optind not found in vscode, but the variable simply store the index + // of the first non-argument term passed to the arguments list. + for (int i = optind; i < argc; i++) { + verified->extra[i - optind] = argv[i]; + } + + return verified; +} + +void discard_options(opt *o) { + free(o->extra); + free(o); +} diff --git a/options/opt.h b/options/opt.h index 7bc9bafbee7f41f4bce4b4b51c77e4c028dd816e..c858e387377f9a7674266b39928bc286c2b08c09 100644 --- a/options/opt.h +++ b/options/opt.h @@ -1,4 +1,42 @@ #ifndef OPT_H #define OPT_H +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#define W_FLAG 1 << 0 +#define F_FLAG 1 << 1 +#define S_FLAG 1 << 2 + +typedef struct _opt { + char flag; + char **extra; + int extra_count; + char *digest_method; // default SHA1 +} opt; + +/** + * @brief Verify passed options, and returns the corresponding flag. + * + * -w : 0x1 + * -f : 0x2 + * -s : 0x4 + * + * other : error + * + * @param argc + * @param argv + * @return opt + */ +opt *verify_options(int argc, char *argv[]); + +/** + * @brief Discard the given options + * + * @param o + */ +void discard_options(opt *o); + #endif // OPT_H