#ident "@(#)glib.c	1.5 91/04/01 XGRASP"
/*-
 * glib.c - GRASP graphics librarian.
 *
 * Copyright (c) 1991 by Patrick J. Naughton
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * This file is provided AS IS with no warranties of any kind.  The author
 * shall have no liability with respect to the infringement of copyrights,
 * trade secrets or any patents by this file or any part thereof.  In no
 * event will the author be liable for any lost revenue or profits or
 * other special, indirect and consequential damages.
 *
 * Comments and additions should be sent to the author:
 *
 *                     Patrick J. Naughton
 *                     Sun Microsystems
 *                     2550 Garcia Ave, MS 10-20
 *                     Mountain View, CA 94043
 *                     (415) 336-1080
 *
 */

#include <stdio.h>
#include <sys/types.h>

char       *pname;
extern char *strrchr();

typedef struct {
    char        fname[14];
    long        offset;
    long        len;
    char       *data;
}           FilenameStruct;

void
error(s1, s2, s3)
    char       *s1, *s2, *s3;
{
    fprintf(stderr, s1, pname, s2, s3);
    exit(1);
}

u_int
GetWord(fp)
    FILE       *fp;
{
    u_char      b1 = (u_char) getc(fp);
    u_char      b2 = (u_char) getc(fp);

    return (u_int) (b1 + b2 * 256);
}

u_int
GetLong(fp)
    FILE       *fp;
{
    u_char      b1 = (u_char) getc(fp);
    u_char      b2 = (u_char) getc(fp);
    u_char      b3 = (u_char) getc(fp);
    u_char      b4 = (u_char) getc(fp);
    return (u_int) (b1 + b2 * 256 + b3 * 256 * 256 + b4 * 256 * 256 * 256);
}

void
PutWord(fp, w)
    FILE       *fp;
    unsigned int w;
{
    unsigned char b1 = w & 255;
    unsigned char b2 = (w >> 8) & 255;
    putc(b1, fp);
    putc(b2, fp);
}

void
PutLong(fp, l)
    FILE       *fp;
    unsigned int l;
{
    unsigned char b1 = l & 255;
    unsigned char b2 = (l >> 8) & 255;
    unsigned char b3 = (l >> 16) & 255;
    unsigned char b4 = (l >> 24) & 255;

    putc(b1, fp);
    putc(b2, fp);
    putc(b3, fp);
    putc(b4, fp);
}

void
extractfile(in, dir, i)
    FILE       *in;
    FilenameStruct *dir;
    int         i;
{
    int         len;
    char       *data;
    FILE       *out;

    fseek(in, dir[i].offset, 0);
    len = GetLong(in);
    len &= 0x00ffffff;
    data = (char *) malloc(len);
    if (!data)
	error("%s: not enough memory\n");
    fread(data, len, 1, in);
    out = fopen(dir[i].fname, "w");
    if (!out)
	error("%s: error writing: %s\n", dir[i].fname);
    fwrite(data, len, 1, out);
    fclose(out);
    free(data);
}

void
writefile(fname, dir, nfiles)
    char       *fname;
    FilenameStruct *dir;
    int         nfiles;
{
    FILE       *out;
    int         i;
    long        offset = 2 + (nfiles + 1) * (13 + 4);

    out = fopen(fname, "w");
    PutWord(out, (nfiles + 1) * (13 + 4));
    for (i = 0; i < nfiles; i++) {
	PutLong(out, offset);
	offset += dir[i].len + 4;
	fwrite(dir[i].fname, 13, 1, out);
    }
    PutLong(out, 0);
    fwrite("             ", 13, 1, out);
    for (i = 0; i < nfiles; i++) {
	PutLong(out, dir[i].len);
	fwrite(dir[i].data, dir[i].len, 1, out);
    }
    fclose(out);
}

int
findname(dir, nfiles, s)
    FilenameStruct *dir;
    int         nfiles;
    char       *s;
{
    int         i;

    if (dir)
	for (i = 0; i < nfiles; i++)
	    if (!strcmp(dir[i].fname, s))
		return i;
    return -1;
}


usage()
{
    error("usage: %s [-dstuea] libname [files...]\n", NULL);
}

main(argc, argv)
    int         argc;
    char       *argv[];
{
    int         i;
    FILE       *in;
    FILE       *out;
    char       *libfname;
    char       *opfname;
    int         nfiles = 0;
    FilenameStruct *dir = 0;
    int         thefile = -1;
    int         c;
    int         dflg = 0;
    int         sflg = 0;
    int         tflg = 0;
    int         uflg = 0;
    int         eflg = 0;
    int         aflg = 0;

    extern char *optarg;
    extern int  optind;

    pname = argv[0];

    if (argc == 1)
	usage();
    while ((c = getopt(argc, argv, "hd:s:t:u:e:a:")) != -1)
	switch (c) {
	case 'h':
	    printf("\nusage: glib [-dstuea] libname [files...]\n");
	    printf("\n        where the following commands are supported:\n\n");
	    printf("\t-d   delete file from library\n");
	    printf("\t-s   extended file list\n");
	    printf("\t-t   quick file list\n");
	    printf("\t-u   update (add) file to library\n");
	    printf("\t-e   extract file from library\n");
	    printf("\t-a   extract all files from library\n\n");
	    exit(0);
	case 'd':
	    if (uflg || eflg || aflg)
		usage();
	    else
		dflg++;
	    libfname = optarg;
	    break;
	case 's':
	    if (tflg)
		usage();
	    else
		sflg++;
	    libfname = optarg;
	    break;
	case 't':
	    if (sflg)
		usage();
	    else
		tflg++;
	    libfname = optarg;
	    break;
	case 'u':
	    if (dflg || eflg || aflg)
		usage();
	    else
		uflg++;
	    libfname = optarg;
	    break;
	case 'e':
	    if (dflg || uflg || aflg)
		usage();
	    else
		eflg++;
	    libfname = optarg;
	    break;
	case 'a':
	    if (dflg || uflg || eflg)
		usage();
	    else
		aflg++;
	    libfname = optarg;
	    break;
	default:
	    usage();
	}

    if (!libfname)
	usage();

    in = fopen(libfname, "r");
    if (!in && !uflg)
	error("%s: %s not found!\n", libfname);

    if (in) {
	nfiles = (GetWord(in) / 17) - 1;
	dir = (FilenameStruct *) malloc(nfiles * sizeof(FilenameStruct));
	for (i = 0; i < nfiles; i++) {
	    dir[i].offset = GetLong(in);
	    fread(dir[i].fname, 13, 1, in);
	    dir[i].fname[13] = 0;
	    if (dflg || eflg) {
		if (!strcmp(dir[i].fname, argv[optind]))
		    thefile = i;
	    }
	}

	if ((dflg || eflg) && thefile == -1)
	    error("%s: %s not found in %s.\n", argv[optind], libfname);

	if (sflg || tflg || dflg || uflg) {
	    if (sflg)
		printf("## filename.ext\toffset\tsize\n%s",
		       "-- ------------\t------\t----\n");
	    for (i = 0; i < nfiles; i++) {
		fseek(in, dir[i].offset, 0);
		dir[i].len = GetLong(in);
		dir[i].len &= 0x00ffffff;
		if (sflg)
		    printf("%2d %s\t%d\t%d\n",
			   i, dir[i].fname, dir[i].offset, dir[i].len);
		else if (tflg)
		    printf("%s%c", dir[i].fname,
			   (i + 1) % 4 && i + 1 < nfiles ? '\t' : '\n');

		if (dflg || uflg) {
		    dir[i].data = (char *) malloc(dir[i].len);
		    if (!dir[i].data)
			error("%s: not enough memory\n");
		    fread(dir[i].data, dir[i].len, 1, in);
		}
	    }
	}
    }
    if (eflg) {
	extractfile(in, dir, thefile);
	fclose(in);
    } else if (aflg) {
	for (i = 0; i < nfiles; i++)
	    extractfile(in, dir, i);
	fclose(in);
    } else if (dflg) {
	fclose(in);
	nfiles--;
	for (i = thefile; i < nfiles; i++)
	    memcpy(&dir[i], &dir[i + 1], sizeof(FilenameStruct));
	writefile(libfname, dir, nfiles);
    } else if (uflg) {

	if (in)
	    fclose(in);
	for (i = optind; i < argc; i++) {
	    char       *s = argv[i];
	    char       *fn = strrchr(s, '/');
	    int         n;
	    int         idx;

	    fn = (fn == 0) ? s : fn + 1;
	    n = strlen(fn);
	    if (n > 13)
		error("%s: '%s' is too long, must be < 13 chars.\n", s);

	    idx = findname(dir, nfiles, fn);

	    if (idx == -1) {
		nfiles++;
		if (!dir)
		    dir = (FilenameStruct *) malloc(sizeof(FilenameStruct));
		else
		    dir = (FilenameStruct *) realloc(dir,
					     nfiles * sizeof(FilenameStruct));
		idx = nfiles - 1;
	    } else
		free(dir[idx].data);
	    memset(dir[idx].fname, 0, 14);
	    memcpy(dir[idx].fname, fn, n);
	    in = fopen(s, "r");
	    if (!in)
		error("%s: %s not found!\n", s);

	    fseek(in, 0L, 2);	/* eof */
	    dir[idx].len = ftell(in);
	    fseek(in, 0L, 0);	/* bof */
	    dir[idx].data = (char *) malloc(dir[idx].len);
	    if (!dir[idx].data)
		error("%s: not enough memory\n");
	    fread(dir[idx].data, dir[idx].len, 1, in);
	    fclose(in);
	}
	writefile(libfname, dir, nfiles);
    }
    exit(0);
}
