Here is an update to my previous thread [which you can find here][1]  (thanks @JS1 for the brilliant advice!). Again, I'd very much appreciate any advice or comments about the structure/logic of the program, style of the code, or reusability of the features. I'm a novice and really eager to learn from people here, so fire away!

*Edit: I'm not sure whether I'm dealing with the eof properly in fread_twelve(). The last code is padded to 16 bits, and I don't think I'm reading it properly because feof never returns true... Would really appreciate any tips!*

*Edit2: I'm also eager to hear how I can balance the heap. Valgrind says I have "778 allocs, 354 frees" on one run, but I thought I'd freed all my allocations!*


  [1]: http://tinyurl.com/ncvgdjf

**lzw.h :** 

    /**
     *	file: lzw.h
     *
     *	function declarations for lzw decompression algorithm.
     */
    
    #ifndef LZW_H
    #define LZW_H
    
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef uint8_t byte;
    
    /**
     *	Frees all blocks between 'from' and 'to' (inclusive) in the dict.
     */
    void free_dict (byte* dict[], int from, int to);
    
    /**
     *	Enters the first 256 ASCII values into dict, and their respective sizes into sizes.
     */
    void load_dict_defaults (byte* dict[], int sizes[]);
    
    /**
     *	Reads 12 bits from source and returns them as an int.
     */
    int fread_twelve(FILE* source);
    
    /**
     *	LZW decompression of source to dest.
     *	Returns 0 if successful, a positive integer otherwise.
     */
    int decompress (FILE* source, FILE* dest);
    
    #endif // LZW_H


**decompress.c :**

        /**
         *	file: decompress.c
         *
         *	implementation of LZW decompression algorithm using 12-bit fixed-width compression format.
         *
         *	usage: decompress source output
         */
        
        #include "lzw.h"
        #include <stdio.h>
        #include <stdlib.h>
        
        int main (int argc, char* argv[])
        {	
            // check for correct number of args
            if (argc != 3)
            {
                printf("Usage: decompress source output\n");
                return 1;
            }
        	
        	// open compressed file
        	FILE* source = fopen(argv[1], "rb");
        	if (source == NULL)
        	{
        		printf("Error: cannot open %s\n", argv[1]);
        		return 1;
        	}
        	
        	// open destination file
        	FILE* dest = fopen(argv[2], "wb");
        	if (dest == NULL)
        	{
        		printf("Error: cannot open %s\n", argv[2]);
        		return 1;
        	}
        	
        	// decompress
        	if (decompress(source, dest) == 1)
        	{
        		printf("Decompression failed\n");
        		fclose(source);
        		fclose(dest);
        		return 1;
        	}
        	else
        	{
        		fclose(source);
        		fclose(dest);
        		return 0;
        	}
        }

**lzw.c :**

    /**
     *	file: lzw.c
     *	
     *	Implementation of LZW decompression algorithm using 12-bit fixed-width compression format.
     *
     */
    
    #include "lzw.h"
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdbool.h>
    
    byte* dictionary[4096];
    int dict_entry_sizes[4096];
    
    /**
     *	Enters the first 256 ASCII values into dict.
     */
    void load_dict_defaults (byte* dict[], int sizes[])
    {
    	byte* temp;
    	byte c = 0;
    	for (int i = 0; i < 256; i++)
    	{
    		temp = malloc(1);
    		temp[0] = c++;
    		dict[i] = temp;
    		dict_entry_sizes[i] = 1;
    	}
    }
    
    /**
     *	Frees all blocks between 'from' and 'to' (inclusive) in the dict.
     */
    void free_dict (byte* dict[], int from, int to)
    {
    	for (int i = from; i <= to; i++)
    	{
    		free(dict[i]);
    	}
    }
    
    /**
     *	Reads 12 bits from source and returns them as an int.
     *	Returns 0 on an error or EOF.
     */
    int fread_twelve(FILE* source)
    {
    	int value;
    	static bool left = true;
    	static byte prevByte = 0;
    	
    	byte buf[2];
    	
    	if (left)
    	{
    		if (fread(buf, 1, 2, source) != 2) 
    		{
    			return 0;
    		}
    
    		if (feof(source))	 // we're at a 16-bit end!	
    		{
    			value = (buf[0] << 8) | buf[1];
    		}
    		else
    		{
    			value = (buf[0] << 4) | (buf[1] >> 4);
    		}
    		left = false;
    		prevByte = buf[1];
    	}
    	else
    	{
    		if (fread(buf, 1, 1, source) != 1) 
    		{
    			return 0;
    		}
    		value = ((prevByte & 0x0F) << 8) | buf[0];
    		left = true;
    	}
    	return value;
    }
    
    /**
     *	LZW decompression of source to dest.
     *	Returns 0 if successful, a positive integer otherwise.
     */
    int decompress (FILE* source, FILE* dest)
    {
    	load_dict_defaults(dictionary, dict_entry_sizes);
    	
    	int dictPos = 256;
    		
    	int prevCode, currCode;
    	byte* prevString, *currString;
    	
    	// read in the first character
    	currCode = fread_twelve(source);
    	size_t size = dict_entry_sizes[currCode];
    	currString = malloc(size + 1); //+1 for upcoming char
    	if (currString == NULL)
    	{
    		fprintf(stderr, "decompress: error assigning currString.\n");
    		return 1;
    	}
    	memcpy(currString, dictionary[currCode], size);
    	fwrite(currString, size, 1, dest);
    	prevCode = currCode;
    	prevString = currString;
    
    	// read in the rest of the characters
    	size_t oldSize;
    	while ( (currCode = fread_twelve(source)) && currCode )
    	{		
    		if (currCode > dictPos)
    		{
    			fprintf(stderr, "decompress: currCode out of dictionary range.\n");
    			return 1;
    		}
    		
    		oldSize = dict_entry_sizes[prevCode];
    		
    		if (currCode < dictPos)		// code in dict
    		{
    			size = dict_entry_sizes[currCode];
    			currString = malloc(size + 1); // +1 for adding char on next loop
    			if (currString == NULL)
    			{
    				fprintf(stderr, "decompress: error assigning currString.\n");
    				return 1;
    			}
    			memcpy(currString, dictionary[currCode], size);
    			fwrite(currString, size, 1, dest);
    
    			memcpy(prevString + oldSize, currString, 1);
    			dictionary[dictPos] = prevString;
    			dict_entry_sizes[dictPos++] = oldSize + 1;
    					
    			// restart dict if full. 
    			if (dictPos >= 4096)
    			{
    				free_dict(dictionary, 256, 4095);
    				dictPos = 256;
    			}
    		}
    		else	// code not in dict until this pass
    		{
    			memmove(prevString + oldSize, prevString, 1);	// memmove allows overlap
    			size = oldSize + 1;
    			fwrite(prevString, size, 1, dest);
    			dictionary[dictPos] = prevString;
    			dict_entry_sizes[dictPos++] = size;	
    			currString = malloc(size + 1);	// +1 for adding char on next loop
    			if (currString == NULL)
    			{
    				fprintf(stderr, "decompress: error assigning currString.\n");
    				return 1;
    			}
    			memcpy(currString, prevString, size);
    		}
    		prevCode = currCode;
    		prevString = currString;
    	}
    	
    	free(prevString);
    	free_dict(dictionary, 0, dictPos-1);
    	
    	return 0;
    }