Header Files in C

C is a statically typed language. This means that programmers must specify the type of each variable used in the source code as well as the arguments and return types of functions.

For example, a variable can be defined as an integer in C using the code int myNumber;. This is called variable declaration and must be done before the variable is used in the code. The C complier checks to make sure all variables are declared before use and that the values we try to store in them match their type. If a variable is used without being declared first, the C compiler will throw an error during compilation. If we attempt to assign a value to a variable that doesn't match its type, like trying to assign a string value to a previously declared integer variable, the compiler will also throw an error.

Functions must also be declared before they are defined. Lets consider a simple function that adds two numbers:

int addNumbers(int x, int y) {
    return x + y;
}

This is the function definition, not the declaration. Note that we specified the return type of the function with the initial int, and we also specified the types of the two arguments x and y. If we try to define this function as written in a .c source file without declaring it first, the compiler will throw an error. To avoid this, we must declare the function first:

int addNumbers(int x, int y);

This is called the function prototype or the function signature. It tells the C compiler the function's name, return type, and argument types.

Oftentimes programmers want to re-use the same function across multiple .c source code files. This can be done by placing function signatures and variable declarations in .h header files. The header files can then be included into the .c source files as follows:

#include "headerFile.h"

In this way, the compiler is able to find the function signatures and variable declarations before encountering their implementations.

Baby Git Header Files

Luckily for us, Baby Git only has one header file. It is called cache.h. The purpose of this file is to define and include the required libraries, function signatures, and defaults for the Git .c programs to function. cache.h is included in all 7 of Baby Git's .c source code files including:

  • init-db
  • update-cache
  • cat-file
  • show-diff
  • write-tree
  • read-tree
  • commit-tree

Below find the code for the original version of cache.h, fully documented with inline comments describing how it works. This is an extract from our fully documented Baby Git codebase. (All the same licenses apply).

/*
 * Only run this code in this file if `CACHE_H` has not been
 * defined yet. This is to prevent multiple compilations of the
 * same code since it's included in multiple `.c` files.
 */
#ifndef CACHE_H 
#define CACHE_H         /* Define the token `CACHE_H`. */
#include /* Standard C library defining input/output tools. */ #include #include #include /* Standard C library defining `stat` tools. */ #include /* Standard C library for working with files. */ #include /* Standard C library for type definitions. */ #include /* Standard C library for library definitions. */ #include /* Standard C library for variable argument lists. */ #include /* Standard C library for system error numbers. */
#ifndef BGIT_WINDOWS #include /* Standard C library for memory management */ /* declarations. */ #else #include #include #include #include #endif
#include /* Include SHA hash tools from openssl library. */ #include /* Include compression tools from zlib library. */
/* * Linus Torvalds: Basic data structures for the directory cache. * * Linus Torvalds: NOTE NOTE NOTE! This is all in the native CPU byte format. * It's not even trying to be portable. It's trying to be efficient. It's * just a cache, after all. */
#ifdef BGIT_UNIX #define STAT_TIME_SEC( st, st_xtim ) ( (st)->st_xtim ## e ) #define STAT_TIME_NSEC( st, st_xtim ) ( (st)->st_xtim.tv_nsec )
#elif defined BGIT_DARWIN #define STAT_TIME_SEC( st, st_xtim ) ( (st)->st_xtim ## espec.tv_sec ) #define STAT_TIME_NSEC( st, st_xtim ) ( (st)->st_xtim ## espec.tv_nsec )
#elif defined BGIT_WINDOWS #define STAT_TIME_SEC( st, st_xtim ) ( (st)->st_xtim ## e ) #define STAT_TIME_NSEC( st, st_xtim ) 0 #endif
#ifndef BGIT_WINDOWS #define OPEN_FILE( fname, flags, mode ) open( fname, flags, mode ); #else #define OPEN_FILE( fname, flags, mode ) open( fname, flags | O_BINARY, mode ); #endif
/* This `CACHE_SIGNATURE` is hardcoded to be loaded into all cache headers. */ #define CACHE_SIGNATURE 0x44495243 /* Linus Torvalds: "DIRC" */
/* Template of the header structure that identifies a set of cache entries. */ struct cache_header { /* Constant across all headers, to validate authenticity. */ unsigned int signature; /* Stores the version of Git that created the cache. */ unsigned int version; /* The number of cache entries in the cache. */ unsigned int entries; /* The SHA1 hash that identifies the cache. */ unsigned char sha1[20]; };
/* * Template of a time structure for storing the time stamps of actions taken on * a file corresponding to a cache entry. For example, the time the file was * modified. For some info on file times, see: * https://www.quora.com/What-is-the-difference-between-mtime-atime-and-ctime */ struct cache_time { unsigned int sec; unsigned int nsec; };
/* * Template of the cache entry structure that stores information about the * corresponding user file in the working directory. */ struct cache_entry { struct cache_time ctime; /* Time of file's last status change. */ struct cache_time mtime; /* Time of file's last modification. */ unsigned int st_dev; /* Device ID of device containing the file. */
/* * The file serial number, which distinguishes this file from all * other files on the same device. */ unsigned int st_ino;
/* * Specifies the mode of the file. This includes information about the * file type and permissions. */ unsigned int st_mode; unsigned int st_uid; /* The user ID of the file’s owner. */ unsigned int st_gid; /* The group ID of the file. */ unsigned int st_size; /* The size of a regular file in bytes. */ unsigned char sha1[20]; /* The SHA1 hash of deflated blob object. */ unsigned short namelen; /* The filename or path length. */ unsigned char name[0]; /* The filename or path. */ };
/* * The following are declarations of external variables. They are defined in * the source code read-cache.c. */
/* The path to the object store. */ const char *sha1_file_directory; /* An array of pointers to cache entries. */ struct cache_entry **active_cache; /* The number of entries in the `active_cache` array. */ unsigned int active_nr; /* The maximum number of elements the active_cache array can hold. */ unsigned int active_alloc;
/* * If desired, you can use an environment variable to set a custom path to the * object store. */ #define DB_ENVIRONMENT "SHA1_FILE_DIRECTORY"
/* * The default path to the object store. */ #define DEFAULT_DB_ENVIRONMENT ".dircache/objects"
/* * These macros are used to calculate the size to be allocated to a cache * entry. */ #define cache_entry_size(len) ((offsetof(struct cache_entry, name) + (len) + 8) & ~7) #define ce_size(ce) cache_entry_size((ce)->namelen)
/* * See this link for details on this macro: * https://stackoverflow.com/questions/22090101/ * why-is-define-alloc-nrx-x163-2-macro-used-in-many-cache-h-files */ #define alloc_nr(x) (((x)+16)*3/2)
/* * The following are function prototypes. They are defined in the source file * read-cache.c. */
/* * Read the contents of the `.dircache/index` file into the `active_cache` * array. */ extern int read_cache(void);
/* * Linus Torvalds: Return a statically allocated filename matching the SHA1 * signature */ extern char *sha1_file_name(unsigned char *sha1);
/* Linus Torvalds: Write a memory buffer out to the SHA1 file. */ extern int write_sha1_buffer(unsigned char *sha1, void *buf, unsigned int size);
/* * Linus Torvalds: Read and unpack a SHA1 file into memory, write memory to a * SHA1 file. */ extern void *read_sha1_file(unsigned char *sha1, char *type, unsigned long *size); extern int write_sha1_file(char *buf, unsigned len);
/* Linus Torvalds: Convert to/from hex/sha1 representation. */ extern int get_sha1_hex(char *hex, unsigned char *sha1); /* Linus Torvalds: static buffer! */ extern char *sha1_to_hex(unsigned char *sha1);
/* Print usage message to standard error stream. */ extern void usage(const char *err);
#endif /* Linus Torvalds: CACHE_H */