/* ----------------------------------------------------------------------- *
 *   
 *   Copyright 1996-2009 The NASM Authors - All Rights Reserved
 *   See the file AUTHORS included with the NASM distribution for
 *   the specific copyright holders.
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following
 *   conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided
 *     with the distribution.
 *     
 *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ----------------------------------------------------------------------- */

/* rdflib - manipulate RDOFF library files (.rdl) */

/*
 * an rdoff library is simply a sequence of RDOFF object files, each
 * preceded by the name of the module, an ASCII string of up to 255
 * characters, terminated by a zero.
 *
 * When a library is being created, special signature block is placed
 * in the beginning of the file. It is a string 'RDLIB' followed by a
 * version number, then int32_t content size and a int32_t time stamp.
 * The module name of the signature block is '.sig'.
 *
 *
 * There may be an optional directory placed on the end of the file.
 * The format of the directory will be 'RDLDD' followed by a version
 * number, followed by the length of the directory, and then the
 * directory, the format of which has not yet been designed.
 * The module name of the directory must be '.dir'.
 *
 * All module names beginning with '.' are reserved for possible future
 * extensions. The linker ignores all such modules, assuming they have
 * the format of a six uint8_t type & version identifier followed by int32_t
 * content size, followed by data.
 */

#include "compiler.h"

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <inttypes.h>
#include <time.h>
#include <inttypes.h>

/* functions supported:
 *   create a library	(no extra operands required)
 *   add a module from a library (requires filename and name to give mod.)
 *   replace a module in a library (requires given name and filename)
 *   delete a module from a library (requires given name)
 *   extract a module from the library (requires given name and filename)
 *   list modules
 */

const char *usage =
    "usage:\n"
    "  rdflib x libname [extra operands]\n\n"
    "  where x is one of:\n"
    "    c - create library\n"
    "    a - add module (operands = filename module-name)\n"
    "    x - extract               (module-name filename)\n"
    "    r - replace               (module-name filename)\n"
    "    d - delete                (module-name)\n" "    t - list\n";

/* Library signature */
const char *rdl_signature = "RDLIB2", *sig_modname = ".sig";

char **_argv;

#define _ENDIANNESS 0           /* 0 for little, 1 for big */

static void int32_ttolocal(int32_t *l)
{
#if _ENDIANNESS
    uint8_t t;
    uint8_t *p = (uint8_t *)l;

    t = p[0];
    p[0] = p[3];
    p[3] = t;
    t = p[1];
    p[1] = p[2];
    p[2] = p[1];
#else
    (void)l;             /* placate optimizers */
#endif
}

static char copybytes(FILE * fp, FILE * fp2, int n)
{
    int i, t = 0;

    for (i = 0; i < n; i++) {
        t = fgetc(fp);
        if (t == EOF) {
            fprintf(stderr, "rdflib: premature end of file in '%s'\n",
                    _argv[2]);
            exit(1);
        }
        if (fp2)
            if (fputc(t, fp2) == EOF) {
                fprintf(stderr, "rdflib: write error\n");
                exit(1);
            }
    }
    return (char)t;             /* return last char read */
}

static int32_t copyint32_t(FILE * fp, FILE * fp2)
{
    int32_t l;
    int i, t;
    uint8_t *p = (uint8_t *)&l;

    for (i = 0; i < 4; i++) {   /* skip magic no */
        t = fgetc(fp);
        if (t == EOF) {
            fprintf(stderr, "rdflib: premature end of file in '%s'\n",
                    _argv[2]);
            exit(1);
        }
        if (fp2)
            if (fputc(t, fp2) == EOF) {
                fprintf(stderr, "rdflib: write error\n");
                exit(1);
            }
        *p++ = t;
    }
    int32_ttolocal(&l);
    return l;
}

int main(int argc, char **argv)
{
    FILE *fp, *fp2 = NULL, *fptmp;
    char *p, buf[256], c;
    int i;
    int32_t l;
    time_t t;
    char rdbuf[10];

    _argv = argv;

    if (argc < 3 || !strncmp(argv[1], "-h", 2)
        || !strncmp(argv[1], "--h", 3)) {
        fputs(usage, stdout);
        exit(1);
    }

    switch (argv[1][0]) {
    case 'c':                  /* create library */
        fp = fopen(argv[2], "wb");
        if (!fp) {
            fprintf(stderr, "rdflib: could not open '%s'\n", argv[2]);
            perror("rdflib");
            exit(1);
        }
        fwrite(sig_modname, 1, strlen(sig_modname) + 1, fp);
        fwrite(rdl_signature, 1, strlen(rdl_signature), fp);
        l = sizeof(t = time(NULL));
        fwrite(&l, sizeof(l), 1, fp);
        fwrite(&t, 1, l, fp);
        fclose(fp);
        break;

    case 'a':                  /* add module */
        if (argc < 5) {
            fprintf(stderr, "rdflib: required parameter missing\n");
            exit(1);
        }
        fp = fopen(argv[2], "ab");
        if (!fp) {
            fprintf(stderr, "rdflib: could not open '%s'\n", argv[2]);
            perror("rdflib");
            exit(1);
        }

        fp2 = fopen(argv[3], "rb");
        if (!fp2) {
            fprintf(stderr, "rdflib: could not open '%s'\n", argv[3]);
            perror("rdflib");
            exit(1);
        }

        p = argv[4];
        do {
            if (fputc(*p, fp) == EOF) {
                fprintf(stderr, "rdflib: write error\n");
                exit(1);
            }
        } while (*p++);

        while (!feof(fp2)) {
            i = fgetc(fp2);
            if (i == EOF) {
                break;
            }

            if (fputc(i, fp) == EOF) {
                fprintf(stderr, "rdflib: write error\n");
                exit(1);
            }
        }
        fclose(fp2);
        fclose(fp);
        break;

    case 'x':
        if (argc < 5) {
            fprintf(stderr, "rdflib: required parameter missing\n");
            exit(1);
        }
    case 't':
        fp = fopen(argv[2], "rb");
        if (!fp) {
            fprintf(stderr, "rdflib: could not open '%s'\n", argv[2]);
            perror("rdflib");
            exit(1);
        }

        fp2 = NULL;
        while (!feof(fp)) {
            /* read name */
            p = buf;
            while ((*(p++) = (char)fgetc(fp)))
                if (feof(fp))
                    break;

            if (feof(fp))
                break;

            fp2 = NULL;
            if (argv[1][0] == 'x') {
                /* check against desired name */
                if (!strcmp(buf, argv[3])) {
                    fp2 = fopen(argv[4], "wb");
                    if (!fp2) {
                        fprintf(stderr, "rdflib: could not open '%s'\n",
                                argv[4]);
                        perror("rdflib");
                        exit(1);
                    }
                }
            } else
                printf("%-40s ", buf);

            /* step over the RDOFF file, extracting type information for
             * the listing, and copying it if fp2 != NULL */

            if (buf[0] == '.') {

                if (argv[1][0] == 't')
                    for (i = 0; i < 6; i++)
                        printf("%c", copybytes(fp, fp2, 1));
                else
                    copybytes(fp, fp2, 6);

                l = copyint32_t(fp, fp2);

                if (argv[1][0] == 't')
                    printf("   %"PRId32" bytes content\n", l);

                copybytes(fp, fp2, l);
            } else if ((c = copybytes(fp, fp2, 6)) >= '2') {    /* version 2 or above */
                l = copyint32_t(fp, fp2);

                if (argv[1][0] == 't')
                    printf("RDOFF%c   %"PRId32" bytes content\n", c, l);
                copybytes(fp, fp2, l);  /* entire object */
            } else {
                if (argv[1][0] == 't')
                    printf("RDOFF1\n");
                /*
                 * version 1 object, so we don't have an object content
                 * length field.
                 */
                copybytes(fp, fp2, copyint32_t(fp, fp2));  /* header */
                copybytes(fp, fp2, copyint32_t(fp, fp2));  /* text */
                copybytes(fp, fp2, copyint32_t(fp, fp2));  /* data */
            }

            if (fp2)
                break;
        }
        fclose(fp);
        if (fp2)
            fclose(fp2);
        else if (argv[1][0] == 'x') {
            fprintf(stderr, "rdflib: module '%s' not found in '%s'\n",
                    argv[3], argv[2]);
            exit(1);
        }
        break;

    case 'r':                  /* replace module */
        argc--;
    case 'd':                  /* delete module */
        if (argc < 4) {
            fprintf(stderr, "rdflib: required parameter missing\n");
            exit(1);
        }

        fp = fopen(argv[2], "rb");
        if (!fp) {
            fprintf(stderr, "rdflib: could not open '%s'\n", argv[2]);
            perror("rdflib");
            exit(1);
        }

        if (argv[1][0] == 'r') {
            fp2 = fopen(argv[4], "rb");
            if (!fp2) {
                fprintf(stderr, "rdflib: could not open '%s'\n", argv[4]);
                perror("rdflib");
                exit(1);
            }
        }

        fptmp = tmpfile();
        if (!fptmp) {
            fprintf(stderr, "rdflib: could not open temporary file\n");
            perror("rdflib");
            exit(1);
        }

        /* copy library into temporary file */
        fseek(fp, 0, SEEK_END); /* get file length */
        l = ftell(fp);
        fseek(fp, 0, SEEK_SET);
        copybytes(fp, fptmp, l);
        rewind(fptmp);
        freopen(argv[2], "wb", fp);

        while (!feof(fptmp)) {
            /* read name */
            p = buf;
            while ((*(p++) = (char)fgetc(fptmp)))
                if (feof(fptmp))
                    break;

            if (feof(fptmp))
                break;

            /* check against desired name */
            if (!strcmp(buf, argv[3])) {
                fread(p = rdbuf, 1, sizeof(rdbuf), fptmp);
                l = *(int32_t *)(p + 6);
                fseek(fptmp, l, SEEK_CUR);
                break;
            } else {
                fwrite(buf, 1, strlen(buf) + 1, fp);    /* module name */
                if ((c = copybytes(fptmp, fp, 6)) >= '2') {
                    l = copyint32_t(fptmp, fp);    /* version 2 or above */
                    copybytes(fptmp, fp, l);    /* entire object */
                }
            }
        }

        if (argv[1][0] == 'r') {
            /* copy new module into library */
            p = argv[3];
            do {
                if (fputc(*p, fp) == EOF) {
                    fprintf(stderr, "rdflib: write error\n");
                    exit(1);
                }
            } while (*p++);

            while (!feof(fp2)) {
                i = fgetc(fp2);
                if (i == EOF) {
                    break;
                }
                if (fputc(i, fp) == EOF) {
                    fprintf(stderr, "rdflib: write error\n");
                    exit(1);
                }
            }
            fclose(fp2);
        }

        /* copy rest of library if any */
        while (!feof(fptmp)) {
            i = fgetc(fptmp);
            if (i == EOF) {
                break;
            }

            if (fputc(i, fp) == EOF) {
                fprintf(stderr, "rdflib: write error\n");
                exit(1);
            }
        }

        fclose(fp);
        fclose(fptmp);
        break;

    default:
        fprintf(stderr, "rdflib: command '%c' not recognized\n",
                argv[1][0]);
        exit(1);
    }
    return 0;
}
