#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <openssl/evp.h>

#define BUFSIZE		65536

// Compile with:
// gcc -o md5 md5.c -lcrypto

int main(int argc, char *argv[])
{
  char buf[BUFSIZE];
  unsigned char md_value[EVP_MAX_MD_SIZE];
  int fd, i, r, md_len;
  EVP_MD_CTX *mdctx;
  const EVP_MD *md;

  if (argc < 2) {
    fprintf(stderr,"Usage: md5 <file>\n");
    exit(1);
  }

  // Adds all digests. Not much we can do about it:
  OpenSSL_add_all_digests();

  // Get digest by it's name:
  md = EVP_get_digestbyname("md5");
  // Allocates, initilizes and returns a MD context:
  mdctx = EVP_MD_CTX_create();
  // Set MD context to use the above named digest:
  EVP_DigestInit_ex(mdctx, md, NULL);
  // Or:
  // EVP_DigestInit_ex(mdctx, EVP_md5(), NULL);
  
  if ((fd = open(argv[1], O_RDONLY)) < 0) {
    perror("open");
    exit(1);
  }
  while((r = read(fd, buf, BUFSIZE)) > 0) {
    // Update digest with file data:
    EVP_DigestUpdate(mdctx, buf, r);
  }
  close(fd);

  // Finalize the digest and store result in md_value:
  EVP_DigestFinal_ex(mdctx, md_value, &md_len);
  printf("MD5 = ");
  for(i = 0; i < md_len; i++)
    printf("%02x", md_value[i]);
  printf("\n");
  
  // Frees up context created by EVP_MD_CTX_create():
  EVP_MD_CTX_destroy(mdctx);
  
  // Called once at exit:
  EVP_cleanup();
  return 0;
}
