missing x bit
[compress.git] / compress.c
index d0d411a4a4dab9959d3eca178d142a8bc7f7bceb..4b91bbf02e73dfbd38626dc76889c31f734ba466 100644 (file)
@@ -1,95 +1,77 @@
 /* depend: */
 /* cflags: */
-/* linker: */
+/* linker: atoi.o code.o debug.o fdprintf.o */
 
-#include <assert.h>
-#include <getopt.h>
-#include <malloc.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stddef.h>
+#include "atoi.h"
+#include "code.h"
+#include "debug.h"
+#include "fdprintf.h"
 
 /* constants */
 
+#define BUFFER_SIZE 4096
+
 #define COMPRESS 1
 #define DECOMPRESS 2
 
-#define NB_CHARS 256
-#define BUFFER_SIZE 4096
-
-#define DEBUG 4
-#define INFO 3
-#define WARNING 2
-#define ERROR 1
+#ifndef O_RAW
+#define O_RAW 0
+#endif /* O_RAW */
 
 /* macros */
 
-#define CEIL(x, y) (((x) + (y) - 1) / (y))
-#define MIN(x, y) (((x) < (y)) ? (x) : (y))
-#define MAX(x, y) (((x) > (y)) ? (x) : (y))
-#define VERBOSE(level, statement...) do { if (level <= verbose) { statement; } } while(0)
-#define PRINTF(args...) do { fprintf (stdout, args); fflush (stdout); } while (0)
-
 /* gobal variables */
 
 char *progname = NULL;
-int verbose = 2;
 
 /* help function */
 
-void usage (int ret)
+int usage (int ret)
 {
-    FILE *fd = ret ? stderr : stdout;
-    fprintf (fd, "usage: %s\n", progname);
-    fprintf (fd, " -h : help message\n");
-    fprintf (fd, " -i <file>: input file\n");
-    fprintf (fd, " -o <file>: output file\n");
-    fprintf (fd, " -v : verbose level (%d)\n", verbose);
-
-    exit (ret);
+    int fd = ret ? stdfderr : stdfdout;
+    fdprintf (fd, "usage: %s\n", progname);
+    fdprintf (fd, " -h : help message\n");
+    fdprintf (fd, " -i <file>: input file\n");
+    fdprintf (fd, " -o <file>: output file\n");
+    fdprintf (fd, " -v : verbose level (%d)\n", verbose);
+
+    return ret;
 }
 
 /* create occurence table */
 
 int *create_table (char *filename)
 {
-    char buffer[BUFFER_SIZE] = {0};
+    byte_t buffer[BUFFER_SIZE] = {0};
     int nbread;
-    int *table = NULL;
-    FILE *fid = NULL;
+    static int table[NB_BYTES] = {0};
+    int fid = 0;
 
-    VERBOSE (DEBUG, PRINTF ("start create occurence table\n"));
-
-    /* memory allocation */
-    table = (int *) calloc (NB_CHARS, sizeof (int));
-    if (table == NULL) {
-        VERBOSE (ERROR, printf ("can't allocate memory\n"));
-        return NULL;
-    }
-    VERBOSE (INFO, printf ("memory allocated\n"));
+    VERBOSE (DEBUG, PRINTOUT ("start creating occurence table\n"));
 
     /* open file */
-    fid = fopen (filename, "rb");
-    if (fid == NULL) {
-        VERBOSE (ERROR, printf ("can't open file '%s'\n", filename));
-        free (table);
+    fid = open (filename, O_RDONLY|O_RAW);
+    if (fid == -1) {
+        VERBOSE (ERROR, PRINTERR ("can't open file '%s'\n", filename));
         return NULL;
     }
-    VERBOSE (INFO, printf ("file '%s' opened\n", filename));
+    VERBOSE (INFO, PRINTOUT ("file '%s' opened\n", filename));
 
     /* read file */
-    while (!feof (fid)) {
-        nbread = fread (buffer, 1, BUFFER_SIZE, fid);
-        VERBOSE (DEBUG, PRINTF ("nbread: %d\n", nbread));
+    while ((nbread = read (fid, buffer, BUFFER_SIZE)) > 0) {
+        VERBOSE (DEBUG, PRINTOUT ("nbread: %d\n", nbread));
         while (nbread--) {
             table[(int)buffer[nbread]]++;
         }
     }
 
     /* close file */
-    fclose (fid);
+    close (fid);
 
-    VERBOSE (DEBUG, PRINTF ("end create occurence table\n"));
+    VERBOSE (DEBUG, PRINTOUT ("end creating occurence table\n"));
 
     return table;
 }
@@ -100,54 +82,37 @@ void print_occ_table (int *table)
 {
     int i;
 
-    printf ("Occurence table\n");
-    for (i = 0; i < NB_CHARS; i++) {
+    PRINTOUT ("Occurence table\n");
+    for (i = 0; i < NB_BYTES; i++) {
         if (table[i]) {
-            printf ("0x%02x '%c': %d\n", i, ((i < 32) || (i > 127)) ? '.' : i, table[i]);
+            PRINTOUT ("0x%02x '%c': %d\n", i, ((i < 32) || (i > 127)) ? '.' : i, table[i]);
         }
     }
 }
 
-/* leaf structure */
-
-typedef struct _leaf_t
-{
-    struct _leaf_t *left;
-    struct _leaf_t *right;
-    int occ;
-    char c;
-} leaf_t;
-
 /* initialize forest */
 
 leaf_t **init_forest (int *table)
 {
-    leaf_t **leafs = NULL;
+    static leaf_t *leafs[NB_BYTES] = {0};
     int nb_leafs = 0;
     int i, l;
 
-    VERBOSE (DEBUG, PRINTF ("start initiliazing forest\n"));
+    VERBOSE (DEBUG, PRINTOUT ("start initiliazing forest\n"));
 
     /* count number of leafs */
-    for (i = 0; i < NB_CHARS; i++) {
+    for (i = 0; i < NB_BYTES; i++) {
         if (table[i] > 0) {
             nb_leafs++;
         }
     }
 
-    /* allocate memory */
-    leafs = (leaf_t **) calloc (nb_leafs + 1, sizeof (leaf_t *));
-    if (leafs == NULL) {
-        VERBOSE (ERROR, printf ("can't allocate memory\n"));
-        return NULL;
-    }
-
     /* initialize leafs */
-    for (i = 0, l = 0; i < NB_CHARS; i++) {
+    for (i = 0, l = 0; i < NB_BYTES; i++) {
         if (table[i] > 0) {
-            leafs[l] = (leaf_t *) calloc (1, sizeof (leaf_t));
+            leafs[l] = getleaf (1);
             if (leafs[l] == NULL) {
-                VERBOSE (ERROR, printf ("can't allocate memory\n"));
+                VERBOSE (ERROR, PRINTERR ("can't allocate memory\n"));
                 return NULL;
             }
             leafs[l]->occ = table[i];
@@ -156,7 +121,7 @@ leaf_t **init_forest (int *table)
         }
     }
 
-    VERBOSE (DEBUG, PRINTF ("end initiliazing forest\n"));
+    VERBOSE (DEBUG, PRINTOUT ("end initiliazing forest\n"));
 
     return leafs;
 }
@@ -167,10 +132,11 @@ leaf_t *create_tree (leaf_t **leafs)
 {
     leaf_t *branch = NULL;
     int nb_leafs = 0;
-    int last, ante;
+    int last = -1;
+    int ante;
     int i, j;
 
-    VERBOSE (DEBUG, PRINTF ("start creating tree\n"));
+    VERBOSE (DEBUG, PRINTOUT ("start creating tree\n"));
 
     /* count number of leafs */
     while (leafs[nb_leafs] != NULL) {
@@ -204,12 +170,12 @@ leaf_t *create_tree (leaf_t **leafs)
 
         /* create branch */
         if ((last == -1) || (ante == -1)) {
-            VERBOSE (ERROR, printf ("error during tree building\n"));
+            VERBOSE (ERROR, PRINTERR ("error during tree building\n"));
             return NULL;
         }
-        branch = (leaf_t *) calloc (1, sizeof (leaf_t));
+        branch = getleaf (1);
         if (branch == NULL) {
-            VERBOSE (ERROR, printf ("can't allocate memory\n"));
+            VERBOSE (ERROR, PRINTERR ("can't allocate memory\n"));
             return NULL;
         }
         branch->left = leafs[last];
@@ -219,65 +185,42 @@ leaf_t *create_tree (leaf_t **leafs)
         leafs[ante] = NULL;
     }
 
-    VERBOSE (DEBUG, PRINTF ("end creating tree\n"));
-
-    return leafs[last];
-}
-
-/* free tree */
+    VERBOSE (DEBUG, PRINTOUT ("end creating tree\n"));
 
-void free_tree (leaf_t *root) {
-    if (root) {
-        if (root->left) {
-            free_tree (root->left);
-        }
-        if (root->right) {
-            free_tree (root->right);
-        }
-        free (root);
-    }
+    return (last != -1) ? leafs[last] : NULL;
 }
 
-/* code structure */
-
-typedef struct {
-    char code[NB_CHARS - 1 + 1];
-} code_t;
-
 /* explore tree */
 
 void explore_tree (code_t *table, leaf_t *root, char *code, int index)
 {
+
+    VERBOSE (DEBUG, PRINTOUT ("start exploring code tree\n"));
+
     if ((root->left == NULL) && (root->right == NULL)) {
-        strcpy ((char *)(table + (int)(root->c)), code);
+        codcpy ((char *)(table + (int)(root->c)), sizeof (code_t), code);
     }
     else {
-        strcpy (code + index, "1");
+        codcpy (code + index, sizeof (code_t), "1");
         explore_tree (table, root->left, code, index + 1);
-        strcpy (code + index, "0");
+        codcpy (code + index, sizeof (code_t), "0");
         explore_tree (table, root->right, code, index + 1);
     }
+
+    VERBOSE (DEBUG, PRINTOUT ("end exploring code tree\n"));
 }
 
 /* create code table */
-
 code_t *create_code (leaf_t *root)
 {
-    code_t *table = NULL;
+    static code_t table[NB_BYTES] = {0};
     code_t code = {0};
 
-    VERBOSE (DEBUG, PRINTF ("start creating code table\n"));
-
-    /* allocate table */
-    table = (code_t *) calloc (NB_CHARS, sizeof (code_t));
-    if (table == NULL) {
-        VERBOSE (ERROR, printf ("can't allocate memory\n"));
-        return NULL;
-    }
+    VERBOSE (DEBUG, PRINTOUT ("start creating code table\n"));
 
     explore_tree (table, root, (char *)&code, 0);
 
-    VERBOSE (DEBUG, PRINTF ("end creating code table\n"));
+    VERBOSE (DEBUG, PRINTOUT ("end creating code table\n"));
 
     return table;
 }
@@ -289,207 +232,435 @@ void print_code_table (code_t *codes)
     char *code;
     int i;
 
-    printf ("Code table\n");
-    for (i = 0; i < NB_CHARS; i++) {
+    PRINTOUT ("Code table\n");
+    for (i = 0; i < NB_BYTES; i++) {
         code = (char *)(codes + i);
-        if (strlen (code) == 0) {
+        if (codlen (code) == 0) {
             continue;
         }
-        printf ("0x%02x '%c': %s\n", i, ((i < 32) || (i > 127)) ? '.' : i, code);
+        PRINTOUT ("0x%02x '%c': %s\n", i, ((i < 32) || (i > 127)) ? '.' : i, code);
     }
 }
 
 /* encode header and code table */
 
-char *encode_header_table (code_t *codes, int *occ)
+byte_t *encode_header_table (code_t *codes, int *occ)
 {
-    unsigned char buffer[NB_CHARS * (NB_CHARS - 1) / 2 / 8 + NB_CHARS + 2] = {0};
-    char bits[(NB_CHARS - 1) + 8 + 1] = {0};
+    static byte_t buffer[NB_BYTES * (NB_BYTES - 1) / 2 / 8 + NB_BYTES + 6] = {0};
+    char bits[(NB_BYTES - 1) + 8 + 1] = {0};
     char *code;
-    unsigned char *header = buffer;
+    byte_t *header = buffer;
     int i, j, length, mode;
     int nb = 0;
     int size = 0;
 
-    VERBOSE (DEBUG, PRINTF ("start encoding header and code table\n"));
+    VERBOSE (DEBUG, PRINTOUT ("start encoding header and code table\n"));
 
     /* mode 1 or 2 */
-    for (i = 0; i < NB_CHARS; i++) {
+    for (i = 0; i < NB_BYTES; i++) {
         code = (char *)(codes + i);
-        if (strlen (code) > 0) {
+        if (codlen (code) > 0) {
             nb++;
-            size += strlen (code) * occ[i];
+            size += codlen (code) * occ[i];
         }
     }
-    mode =  (NB_CHARS < 2 * nb + 1) ? 1 : 2;
-    VERBOSE (DEBUG, PRINTF ("nb chars: %d\n", nb));
-    VERBOSE (DEBUG, PRINTF ("mode: %d\n", mode));
-    VERBOSE (DEBUG, PRINTF ("size: %d\n", size));
+    mode = (NB_BYTES < 2 * nb + 1) ? 1 : 2;
+    VERBOSE (DEBUG, PRINTOUT ("nb chars: %d\n", nb));
+    VERBOSE (DEBUG, PRINTOUT ("mode: %d\n", mode));
+    VERBOSE (DEBUG, PRINTOUT ("size: %d\n", size));
+    VERBOSE (DEBUG, PRINTOUT ("rem: %d\n", size % 256));
 
     /* header */
-    strcpy ((char *)header, (mode == 1) ? "MZ1   " : "MZ2   ");
+    codcpy ((char *)header, sizeof (buffer), (mode == 1) ? "MZ1   " : "MZ2   ");
     header += 6;
 
     /* size */
     switch (mode) {
     case 1:
-        for (i = 0; i < NB_CHARS; i++) {
+        for (i = 0; i < NB_BYTES; i++) {
             code = (char *)(codes + i);
-            *(header++) = (unsigned char) strlen (code);
+            *(header++) = (byte_t) codlen (code);
         }
         break;
     case 2:
-        *(header++) = (unsigned char)(nb - 1);
-        for (i = 0; i < NB_CHARS; i++) {
+        *(header++) = (byte_t)(nb - 1);
+        for (i = 0; i < NB_BYTES; i++) {
             code = (char *)(codes + i);
-            if (strlen (code) > 0) {
-                *(header++) = (unsigned char)i;
-                *(header++) = (unsigned char) strlen (code);
+            if (codlen (code) > 0) {
+                *(header++) = (byte_t) i;
+                *(header++) = (byte_t) codlen (code);
             }
         }
         break;
     }
 
     /* bits */
-    for (i = 0; i < NB_CHARS; i++) {
+    for (i = 0; i < NB_BYTES; i++) {
         code = (char *)(codes + i);
-        if (strlen (code) > 0) {
-            strcat (bits, code);
-            while (strlen (bits) > (8 - 1)) {
+        if (codlen (code) > 0) {
+            codcat (bits, sizeof (code_t), code);
+            while (codlen (bits) > (8 - 1)) {
                 for (j = 0; j < 8; j++) {
                     *header <<= 1;
                     if (bits[j] == '1') {
                         (*header)++;
                     }
                 }
-                strcpy (bits, bits + 8);
+                codcpy (bits, sizeof (code_t), bits + 8);
                 header++;
             }
         }
     }
-    if (strlen (bits) > 0) {
-        for (j = 0; j < (int)strlen (bits); j++) {
+    if (codlen (bits) > 0) {
+        for (j = 0; j < (int)codlen (bits); j++) {
             *header <<= 1;
             if (bits[j] == '1') {
                 (*header)++;
             }
         }
+        for (j = (int)codlen (bits); j < 8; j++) {
+            *header <<= 1;
+        }
         header++;
     }
 
     /* length */
     length = (int)(header - buffer - 6);
-    VERBOSE (DEBUG, PRINTF ("lengh: %d %02x %02x\n", length, length >> 8, length & 0xff));
-    buffer[3] = (unsigned char)(length >> 8);
-    buffer[4] = (unsigned char)(length & 0xff);
-    buffer[5] = (unsigned char)(size % 8);
+    VERBOSE (DEBUG, PRINTOUT ("lengh: %d %02x %02x\n", length, length >> 8, length & 0xff));
+    buffer[3] = (byte_t)(length >> 8);
+    buffer[4] = (byte_t)(length & 0xff);
+    buffer[5] = (byte_t)(size % 256);
+    header = buffer;
 
-    /* allocation */
-    header = (unsigned char *) calloc (length + 6, 1);
-    memcpy (header, buffer, length + 6);
+    VERBOSE (DEBUG, PRINTOUT ("end encoding header and code table\n"));
 
-    VERBOSE (DEBUG, PRINTF ("end encoding header and code table\n"));
-
-    return (char *)header;
+    return header;
 }
 
 /* print header */
 
-void print_header (char *header)
+void print_header (byte_t *header)
 {
     int length, i;
 
-    length = ((unsigned char)(header[3]) << 8) + (unsigned char)(header[4]);
-    VERBOSE (DEBUG, PRINTF ("lengh: %d\n", length));
+    length = (header[3] << 8) + header[4];
+    VERBOSE (DEBUG, PRINTOUT ("lengh: %d\n", length));
     for (i = 0; i < length + 6; i++) {
-        printf ("%02x", (unsigned char)header[i]);
+        PRINTOUT ("%02x", header[i]);
     }
-    printf ("\n");
+    PRINTOUT ("\n");
 }
 
 /* write crompressed file */
 
-int write_compress (char *output, char *input, code_t *codes, char *header)
+int write_compress (char *output, char *input, code_t *codes, byte_t *header)
 {
-    char bufin[BUFFER_SIZE] = {0};
-    char bufout[BUFFER_SIZE] = {0};
-    char bits[(NB_CHARS - 1) + 8 + 1] = {0};
-    FILE *fin, *fout;
+    byte_t bufin[BUFFER_SIZE] = {0};
+    byte_t bufout[BUFFER_SIZE] = {0};
+    char bits[(NB_BYTES - 1) + 8 + 1] = {0};
+    int fin, fout;
     int length = 0;
-    int i, j, nbread;
-    char *pt;
+    int i, j, nbread, nbwrite;
+    byte_t *pt;
 
-    VERBOSE (DEBUG, PRINTF ("start writting compressed file\n"));
+    VERBOSE (DEBUG, PRINTOUT ("start writting compressed file\n"));
 
     /* open input file */
-    fin = fopen (input, "rb");
-    if (fin == NULL) {
-        VERBOSE (ERROR, printf ("can't open file '%s'\n", input));
+    fin = open (input, O_RDONLY|O_RAW);
+    if (fin == -1) {
+        VERBOSE (ERROR, PRINTERR ("can't open file '%s' for reading\n", input));
         return 1;
     }
-    VERBOSE (INFO, printf ("file '%s' opened\n", input));
+    VERBOSE (INFO, PRINTOUT ("file '%s' opened\n", input));
 
     /* open output file */
-    fout = fopen (output, "wb");
-    if (fin == NULL) {
-        VERBOSE (ERROR, printf ("can't open file '%s'\n", output));
+    fout = open (output, O_WRONLY|O_CREAT|O_RAW, 0700);
+    if (fout == -1) {
+        VERBOSE (ERROR, PRINTERR ("can't open file '%s' for writing\n", output));
+           close (fin);
         return 1;
     }
-    VERBOSE (INFO, printf ("file '%s' opened\n", output));
+    VERBOSE (INFO, PRINTOUT ("file '%s' opened\n", output));
 
     /* write header */
-    length = ((unsigned char)(header[3]) << 8) + (unsigned char)(header[4]);
-    VERBOSE (DEBUG, PRINTF ("lengh: %d\n", length));
-    fwrite (header, 1, length + 6, fout);
+    length = (header[3] << 8) + header[4];
+    VERBOSE (DEBUG, PRINTOUT ("lengh: %d\n", length));
+    nbwrite = write (fout, header, length + 6);
+    if (nbwrite != length + 6) {
+        VERBOSE (ERROR, PRINTERR ("can't write %d bytes in file '%s'\n", length + 6 - nbwrite, output));
+           close (fout);
+           close (fin);
+        return 1;
+    }
+
 
     /* write file */
     pt = bufout;
-    while (!feof (fin)) {
-        nbread = fread (bufin, 1, BUFFER_SIZE, fin);
-        VERBOSE (DEBUG, PRINTF ("nbread: %d\n", nbread));
+    while ((nbread = read (fin, bufin, BUFFER_SIZE)) > 0) {
+        VERBOSE (DEBUG, PRINTOUT ("nbread: %d\n", nbread));
         for (i = 0; i < nbread; i++) {
-            strcat (bits, (char *)(codes + bufin[i]));
-            while (strlen (bits) > (8 - 1)) {
+            codcat (bits, sizeof (code_t), (char *)(codes + bufin[i]));
+            while (codlen (bits) > (8 - 1)) {
                 for (j = 0; j < 8; j++) {
                     *pt <<= 1;
                     if (bits[j] == '1') {
                         (*pt)++;
                     }
                 }
-                strcpy (bits, bits + 8);
-                if (pt - bufout < BUFFER_SIZE) {
-                    pt++;
-                } else {
-                    fwrite (bufout, 1, BUFFER_SIZE, fout);
+                codcpy (bits, sizeof (code_t), bits + 8);
+                if (pt - bufout == BUFFER_SIZE - 1) {
+                    nbwrite = write (fout, bufout, BUFFER_SIZE);
+                    if (nbwrite != BUFFER_SIZE) {
+                        VERBOSE (ERROR, PRINTERR ("can't write %d bytes in file '%s'\n", BUFFER_SIZE - nbwrite, output));
+                           close (fout);
+                           close (fin);
+                        return 1;
+                    }
                     pt = bufout;
+                } else {
+                    pt++;
                 }
             }
         }
     }
-    if (strlen (bits) > 0) {
-        for (j = 0; j < (int)strlen (bits); j++) {
+    VERBOSE (DEBUG, PRINTOUT ("lastest bits : %d\n", codlen (bits)));
+    if (codlen (bits) > 0) {
+        for (j = 0; j < (int)codlen (bits); j++) {
             *pt <<= 1;
             if (bits[j] == '1') {
                 (*pt)++;
             }
         }
-        if (pt - bufout < BUFFER_SIZE) {
-            pt++;
-        } else {
-            fwrite (bufout, 1, BUFFER_SIZE, fout);
-            pt = bufout;
+        for (j = (int)codlen (bits); j < 8; j++) {
+            *pt <<= 1;
         }
         pt++;
     }
     if (pt != bufout) {
-        fwrite (bufout, 1, pt - bufout, fout);
+        VERBOSE (DEBUG, PRINTOUT ("last partial buffer written: %u\n", pt - bufout));
+        nbwrite = write (fout, bufout, pt - bufout);
+        if (nbwrite != pt - bufout) {
+            VERBOSE (ERROR, PRINTERR ("can't write %d bytes in file '%s'\n", pt - bufout - nbwrite, output));
+               close (fout);
+               close (fin);
+            return 1;
+        }
     }
     
     /* closing */
-    fclose (fin);
-    fclose (fout);
+    close (fin);
+    close (fout);
 
-    VERBOSE (DEBUG, PRINTF ("end writting compressed file\n"));
+    VERBOSE (DEBUG, PRINTOUT ("end writting compressed file\n"));
+
+    return 0;
+}
+
+/* read header */
+
+code_t *read_header (char *filename) {
+    static code_t table[NB_BYTES] = {0};
+    byte_t buffer[NB_BYTES * (NB_BYTES - 1) / 2 / 8 + NB_BYTES + 6] = {0};
+    byte_t *codes = NULL;
+    byte_t cur;
+    int lengths[NB_BYTES] = {0};
+    int fid;
+    int mode = 0;
+    int i, j, l, nb, size;
+
+    VERBOSE (DEBUG, PRINTOUT ("start reading header\n"));
+
+    /* open file */
+    fid = open (filename, O_RDONLY|O_RAW);
+    if (fid == -1) {
+        VERBOSE (ERROR, PRINTERR ("can't open file '%s'\n", filename));
+        return NULL;
+    }
+    VERBOSE (INFO, PRINTOUT ("file '%s' opened\n", filename));
+
+    /* read magic number */
+    nb = read (fid, buffer, 6);
+    VERBOSE (DEBUG, PRINTOUT ("nb, buffer: %d 0x%02x 0x%02x\n", nb, buffer[0], buffer[1]));
+    if ((nb == 6) && (buffer[0] == 'M') && (buffer[1] == 'Z')) {
+        mode = (buffer[2] == '1') ? 1 : (buffer[2] == '2') ? 2 : 0;
+        size = (buffer[3] << 8) + buffer[4];
+        VERBOSE (DEBUG, PRINTOUT ("mode, size: %d %d\n", mode, size));
+        if (size > NB_BYTES * (NB_BYTES - 1) / 2 / 8 + NB_BYTES) {
+            mode = 0;
+        } else {
+            nb = read (fid, buffer, size);
+            VERBOSE (DEBUG, PRINTOUT ("nb read: %d/%d\n", nb, size));
+            if (nb != size) {
+                mode = 0;
+            }
+        }
+    }
+    close (fid);
+    if (mode == 0) {
+        VERBOSE (ERROR, PRINTERR ("incorrect file\n"));
+        return NULL;
+    }
+
+    /* analyse header */
+    codes = buffer;
+    switch (mode) {
+    case 1:
+        for (i = 0; i < NB_BYTES; i++) {
+            lengths[i] = *(codes++);
+        }
+        break;
+    case 2:
+        nb = *(codes++) + 1;
+        VERBOSE (DEBUG, PRINTOUT ("nb codes: %d\n", nb));
+        for (i = 0; i < nb; i++) {
+            j = *(codes++);
+            lengths[j] = *(codes++);
+        }
+        break;
+    }
+    VERBOSE (DEBUG, for (i = 0; i < NB_BYTES; i++) if (lengths[i]) PRINTOUT ("%d: %d\n", i, lengths[i]));
+
+    /* check lengths */
+    for (i = 0, l = 0; i < NB_BYTES; i++) {
+        l += lengths[i];
+    }
+    if (((mode == 1) && (size - 256 != (l + 7) / 8)) ||
+        ((mode == 2) && (size - 2 * nb - 1 != (l + 7) / 8))) {
+        VERBOSE (ERROR, PRINTERR ("incorrect code table length: %d %d %d\n", size, nb, l));
+        return NULL;
+    }
+
+    /* decode code */
+    cur = *(codes++);
+    l = 8;
+    for (i = 0; i < NB_BYTES; i++) {
+        if (lengths[i] == 0) {
+            continue;
+        }
+        while (lengths[i]--) {
+            codcat ((char *)(table + i), sizeof (code_t), ((cur & 0x80) == 0) ? "0" : "1");
+            l--;
+            cur <<= 1;
+            if (l == 0) {
+                cur = *(codes++);
+                l = 8;
+            }
+        }
+    }
+
+    VERBOSE (DEBUG, PRINTOUT ("end reading header\n"));
+
+    return table;
+}
+
+/* write decompressed file */
+
+int write_decompress (char *output, char *input, code_t *codes)
+{
+    byte_t bufin[BUFFER_SIZE] = {0};
+    byte_t bufout[BUFFER_SIZE] = {0};
+    byte_t bufhea[MAX(NB_BYTES * (NB_BYTES - 1) / 2 / 8 + NB_BYTES + 6, BUFFER_SIZE)] = {0};
+    char bits[(NB_BYTES - 1) + 1] = {0};
+    int fin, fout;
+    int i, j, k, nb, size, nbwrite, rem;
+    int is_found;
+    int l = 0;
+    byte_t *pt;
+
+    VERBOSE (DEBUG, PRINTOUT ("start writing decompressed file\n"));
+
+    /* open file for reading */
+    fin = open (input, O_RDONLY|O_RAW);
+    if (fin == -1) {
+        VERBOSE (ERROR, PRINTERR ("can't open file '%s' for reading\n", input));
+        return 1;
+    }
+    VERBOSE (INFO, PRINTOUT ("file '%s' opened\n", input));
+
+    /* read magic number */
+    nb = read (fin, bufhea, 6);
+    if (nb != 6) {
+        VERBOSE (ERROR, PRINTERR ("can't read file\n"));
+        close (fin);
+        return 1;
+    }
+    size = (bufhea[3] << 8) + bufhea[4];
+    VERBOSE (DEBUG, PRINTOUT ("table size: %d\n", size));
+    rem = bufhea[5];
+    VERBOSE (DEBUG, PRINTOUT ("remainder: %d\n", rem));
+    nb = read (fin, bufhea, size);
+    if (nb != size) {
+        VERBOSE (ERROR, PRINTERR ("can't read file\n"));
+        close (fin);
+        return 1;
+    }
+
+    /* open file for writing */
+    fout = open (output, O_WRONLY|O_CREAT|O_RAW, 0700);
+    if (fout == -1) {
+        VERBOSE (ERROR, PRINTERR ("can't open file '%s' for writing\n", output));
+        close (fin);
+           return 1;
+    }
+    VERBOSE (INFO, PRINTOUT ("file '%s' opened\n", output));
+
+    /* write file */
+    pt = bufout;
+    while ((nb = read (fin, bufin, BUFFER_SIZE)) > 0) {
+        VERBOSE (DEBUG, PRINTOUT ("nbread: %d\n", nb));
+        for (i = 0; i < nb; i++) {
+            for (j = 0; j < 8; j++) {
+                codcat (bits, sizeof (bits), ((bufin[i] & 0x80) == 0) ? "0" : "1");
+                bufin[i] <<= 1;
+                l++;
+                VERBOSE (DEBUG, PRINTOUT ("bits: %d - %s\n", codlen (bits), bits));
+
+                /* look for correct code */
+                is_found = 0;
+                for (k = 0; (k < NB_BYTES) && (!is_found); k++) {
+                    if (codcmp ((char *)(codes + k), bits) == 0) {
+                        is_found = 1;
+                        VERBOSE (DEBUG, PRINTOUT ("found: %d\n", k));
+                        *pt= k;
+                        bits[0] = 0;
+                        if (pt - bufout == BUFFER_SIZE - 1) {
+                            VERBOSE (DEBUG, PRINTOUT ("nb buffer out: %u\n", (pt - bufout)));
+                            nbwrite = write (fout, bufout, BUFFER_SIZE);
+                            if (nbwrite != BUFFER_SIZE) {
+                                VERBOSE (ERROR, PRINTERR ("can't write %d bytes in file '%s'\n'", BUFFER_SIZE - nbwrite, output));
+                                close (fout);
+                                close (fin);
+                                return 1;
+                            }
+                            pt = bufout;
+                        } else {
+                            pt++;
+                        }
+                    }
+                }
+                if ((i == nb - 1) && (l % 256 == rem) && (nb != BUFFER_SIZE)) {
+                    VERBOSE (DEBUG, PRINTOUT ("break\n"));
+                    break;
+                }
+            }
+        }
+    }
+    if (pt != bufout) {
+        VERBOSE (DEBUG, PRINTOUT ("nb buffer out: %u\n", (pt - bufout)));
+        nbwrite = write (fout, bufout, pt - bufout);
+        if (nbwrite != pt - bufout) {
+            VERBOSE (ERROR, PRINTERR ("can't write %d bytes in file '%s'\n'", pt - bufout - nbwrite, output));
+            close (fout);
+            close (fin);
+            return 1;
+        }
+    }
+
+    /* close files */
+    close (fin);
+    close (fout);
+
+    VERBOSE (DEBUG, PRINTOUT ("end writing decompressed file\n"));
 
     return 0;
 }
@@ -504,15 +675,23 @@ int main (int argc, char *argv[])
     leaf_t **leafs = NULL;
     leaf_t *root = NULL;
     code_t *codes = NULL;
-    char *header = NULL;
+    byte_t *header = NULL;
     int mode = COMPRESS;
-    int rc = 0;
+    int rc = 1;
 
     progname = argv[0];
 
     int c;
-    VERBOSE (DEBUG, PRINTF ("start argument processing\n"));
-    while ((c = getopt(argc, argv, "cdhi:o:v:")) != EOF) {
+    char * arg;
+    VERBOSE (DEBUG, PRINTOUT ("start processing arguments\n"));
+    while (argc-- > 1) {
+        arg = *(++argv);
+        if (arg[0] != '-') {
+            PRINTERR ("%s: invalid option -- %s\n", progname, arg);
+            return usage (1);
+        }
+        c = arg[1];
+        VERBOSE (DEBUG, PRINTOUT ("option: %c\n", c));
         switch (c) {
         case 'c':
             mode = COMPRESS;
@@ -521,29 +700,32 @@ int main (int argc, char *argv[])
             mode = DECOMPRESS;
             break;
         case 'i':
-            VERBOSE (DEBUG, PRINTF ("-i\n"));
-            VERBOSE (DEBUG, PRINTF ("optarg: %s\n", optarg));
-            input = optarg;
+            input = (arg[2]) ? arg + 2 : (--argc > 0 ) ? *(++argv) : NULL;
+            VERBOSE (DEBUG, PRINTOUT ("input: %s\n", input));
             break;
         case 'o':
-            VERBOSE (DEBUG, PRINTF ("-o\n"));
-            output = optarg;
+            output = (arg[2]) ? arg + 2 : (--argc > 0 ) ? *(++argv) : NULL;
+            VERBOSE (DEBUG, PRINTOUT ("output: %s\n", output));
             break;
         case 'v':
-            VERBOSE (DEBUG, PRINTF ("-v\n"));
-            verbose = atoi (optarg);
-            VERBOSE (INFO, printf ("verbose: %d\n", verbose));
+            arg = (arg[2]) ? arg + 2 : (--argc > 0 ) ? *(++argv) : NULL;
+            if (arg == NULL) {
+                PRINTERR ("%s: missing verbose level\n", progname);
+                return usage (1);
+            }
+            verbose = atoi (arg);
+            VERBOSE (INFO, PRINTOUT ("verbose: %d\n", verbose));
             break;
         case 'h':
         default:
-            usage (c != 'h');
+            return usage (c != 'h');
         }
     }
-    if (argc - optind != 0) {
-        fprintf (stderr, "%s: invalid option -- %s\n", progname, argv[optind]);
-        usage (1);
+    if ((input == NULL) || (output == NULL)) {
+        PRINTERR ("%s: missing file\n", progname);
+        return usage (1);
     }
-    VERBOSE (DEBUG, PRINTF ("end argument processing\n"));
+    VERBOSE (DEBUG, PRINTOUT ("end processing arguments\n"));
 
     switch (mode) {
     case COMPRESS:
@@ -564,17 +746,13 @@ int main (int argc, char *argv[])
         rc = write_compress (output, input, codes, header);
         break;
     case DECOMPRESS:
-        rc = 1;
+        codes = read_header (input);
+        if (codes == NULL) break;
+        VERBOSE (INFO, print_code_table (codes));
+        rc = write_decompress (output, input, codes);
         break;
     }
 
-    /* clean everything */
-    if (header) free (header);
-    if (codes) free (codes);
-    if (root) free_tree (root);
-    if (leafs) free (leafs);
-    if (table) free (table);
-
     return rc;
 }
 
@@ -582,5 +760,10 @@ int main (int argc, char *argv[])
 // test: compress.exe -h | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
 // test: compress.exe -_ 2> /dev/null | awk 'END { if (NR == 0) { exit(0) } else exit (1) }'
 // test: compress.exe -_ 2>&1 | awk '/usage:/ { rc=1 } END { exit (1-rc) }'
+// test: compress.exe -c -i compress.c -o compress.mz
+// test: ls -sS1 compress.c compress.mz | tail -1 | grep compress.mz
+// test: compress.exe -d -i compress.mz -o tmp.c
+// test: cmp compress.c tmp.c
+// test: rm compress.mz tmp.c
 
-// vim: ts=4 sw=4 et
+/* vim: set ts=4 sw=4 et: */