/* @(#) unblu.c       2.2 18/06/89  */
/*************************************************************************
 **                                                                     **
 **  Name    :  unblu                                                   **
 **  Author  :  Marcel J.E. Mol                                         **
 **  Date    :  10/05/88              (first release)                   **
 **  Version :  2.20                                                    **
 **  Files   :  unblu.c         Main source file                        **
 **                                                                     **
 **  ------------------------- Revision List -------------------------  **
 **  Ver   Date       Name                   Remarks                    **
 **  1.00  10/05/88   Marcel Mol             Raw copy of a basic program**
 **  2.00  03/06/88   Marcel Mol             Rewrite after blu info     **
 **                                          was send to the net        **
 **  2.10  18/06/88   Marcel Mol             Added filetype texts       **
 **  2.20  23/09/88   Marcel Mol             Show mod and creation time **
 **                                                                     **
 **  =================================================================  **
 **                                                                     **
 **  Compile as follows: cc unblu.c -Oso unblu                          **
 **                                                                     **
 **  Usage: unblu [-vh] <arcfile> [<files-toextract>]                   **
 **                                                                     **
 ************************************************************************/

 /************************************************************************
 **                                                                     **
 **  NAME                                                               **
 **     unblu -   Extract files from an apple binary ][ archive.        **
 **                                                                     **
 **                                                                     **
 **  SYNOPSIS                                                           **
 **     unblu [-vh] arcfile [files-to-extract]                          **
 **                                                                     **
 **                                                                     **
 **  DESCRIPTION                                                        **
 **     Unblu list the contents of a binary ][ archive (the -v option)  **
 **     or extracts the listed files from the archive (without the      **
 **     -v option). If no files are listed, the comlete archive is      **
 **     extracted or listed.                                            **
 **     The -h option gives a help message on the usage of unblu.       **
 **                                                                     **
 **  AUTHOR                                                             **
 **     Marcel J.E. Mol                                                 **
 **                                                                     **
 **  BUGS                                                               **
 **     Mail bugs to:      marcel@duteca.tudelft.nl                     **
 **                                                                     **
 ************************************************************************/

char * copyright = "@(#) unblu.c  2.2 18/06/89  (c) M.J.E. Mol";
#include <stdio.h>
#include <fcntl.h>
#include "filetype.h"

#define BUFSIZE 128                 /* Blu block length */
#define DEBUG

/* Global variables */
char * progname;
char * blufile;
int    verbose   = 0;


/* Function declarations */
void main ();
void unblu ();
void process_file ();
void extract_file ();
void print_global_info ();
int  want ();
void usage ();
void printhelp ();


void main(argc, argv)
int     argc;
char ** argv;
{
    int  flag;				/* Flag for getopt */
    int  bfd;				/* File descriptor for blu file */
    extern int optind;			/* For getopt */
    extern char * optarg;		/* For getopt */

    progname = argv[0];
    while ((flag = getopt(argc, argv, "vh")) != EOF) { /* Process options */
        switch (flag) {
            case 'v': verbose = 1;	/* Want only listing of archive */
                      break;
            case 'h': usage();		/* Give help */
		      printhelp();
                      exit(0);
            default : fprintf(stderr, "%s: skipping unkown flag %c.\n",
                                    progname, flag);
                      break;
        }
    }

    if (optind >= argc) {		/* No archive given */
        usage();
        exit(1);
    }

    blufile = argv[optind++];		/* Get archive name */
    if ((bfd = open(blufile, O_RDONLY)) == -1) {
        perror(blufile);
        exit(2);
    }

    unblu(bfd, &argv[optind]);		/* Process wanted files */

    close(bfd);

    exit(0);

} /* main */


/*
 * unblu -- process a binary II file fd, and process the filenames 
 *          listed in wated. If wanted is \0, all files are processed.
 */
void unblu(fd, wanted)
int     fd;
char ** wanted;
{
    unsigned char buf[BUFSIZE];
    int  firstblock = 1;	/* First block needs special processing */
    int  tofollow = 1;		/* Files to follow in the archive */
    int  n;

    while (tofollow && ((n = read(fd, buf, BUFSIZE)) != -1)) {
						/* If there is a header block */
	if (n != BUFSIZE) {
	    fprintf(stderr, "%s: %s file size is broken\n", progname, blufile);
	    exit(1);
	}
        if ((buf[0] != 10) || (buf[1] != 71) ||
            (buf[2] != 76) || (buf[18] != 2)) { 
            fprintf(stderr, "%s: %s not a binary II file\n", progname, blufile);
            exit(1);
        }
	tofollow = buf[127];		/* How many files to follow */
        if (firstblock && verbose) {
            print_global_info(buf);
            firstblock = 0;
        }
        process_file(fd, buf, wanted);          /* process the file for it    */
    }
    return;

} /* unblu */


/*
 * process_file -- retrieve or print file information of file given
 *                 in buf
 */
void process_file(fd, buf, wanted)
int     fd;
unsigned char *  buf;
char ** wanted;
{
    int tf;
    int ftype;
    int dflags;
    int fnamelen;
    int filelen;
    char fname[64];
    int nblocks;
    int modyear, modmonth, modday;
    int creyear, cremonth, creday;
    int modhour, modsec;
    int crehour, cresec;

    /*
     * Get fileinfo
     */
    ftype =  buf[4];				/* File type */
    fnamelen =  buf[23];			/* filename */
    strncpy(fname, &buf[24], fnamelen);
    fname[fnamelen] = '\0';
    dflags =  buf[125];				/* Data flags */
    tf =  buf[127];				/* Number of files to follow */
    filelen = buf[20] + (buf[21]<<8) + (buf[22]<<16);/* Calculate file length */
    nblocks = (filelen + BUFSIZE-1) / BUFSIZE;
    modyear = buf[11] >>1;
    modday  = buf[10] & 0x1f;
    modmonth= ((buf[11] & 0x01) << 3) + (buf[10] >> 5);
    modhour = buf[13] & 0x1f;
    modsec  = buf[12] & 0x3f;
    creyear = buf[15] >>1;
    creday  = buf[14] & 0x1f;
    cremonth= ((buf[15] & 0x01) << 3) + (buf[14] >> 5);
    crehour = buf[17] & 0x1f;
    cresec  = buf[16] & 0x3f;


    if (*wanted == NULL || want(fname, wanted)) {
        if (verbose) {			        /* print file information */
	    printf("%-15s  %3s", fname, filetypes[ftype]);
	    printf(" %02d/%02d/%02d(%02d:%02d)", modyear, modmonth, modday,
						 modhour, modsec);
	    printf(" - %02d/%02d/%02d(%02d:%02d)", creyear, cremonth, creday,
						   crehour, cresec);
	    printf(" %5d bytes ", filelen);
	    if (dflags == 0)
		printf("stored");
	    else {
		if (dflags & 128) {
	            printf("squeezed");
	        }
	        if (dflags & 64) {
	            printf("encrypted");
	        }
	        if (dflags & 1) 
	            printf("packed");
	    }
	    putchar('\n'); 

	    if (ftype != 15)  			/* If not a directory */
                lseek(fd, BUFSIZE*nblocks, 1);  /* Seek to next file */
        }
	else if (ftype != 15) 
            extract_file(fd, fname, filelen);
    }
    else if (ftype != 15)  			/* If not a directory */
        lseek(fd, BUFSIZE*nblocks, 1);          /* Seek to next file */

    return;

} /* process_file */


/*
 * extract_file -- extract file fname from the archive fd. Fname
 *                 comtains filelen bytes.
 */
void extract_file(fd, fname, filelen)
int    fd;
char * fname;
int    filelen;
{
    int ofd;
    int n;
    unsigned char buf[BUFSIZE];

    printf("x - %s\n", fname);
    if ((ofd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, 0644)) == -1) {
						/* Open (PRODOS) file */
        perror(fname);
        exit(1);
    }

    while (filelen > 0) {
        if ((n = read(fd, buf, BUFSIZE)) != BUFSIZE) { /* Read 128 bytes */
	    fprintf(stderr, "%s: %s file size is broken\n", progname, blufile);
	    exit(1);
	}
        write(ofd, buf, (filelen >= BUFSIZE ? BUFSIZE : filelen));
        filelen -= BUFSIZE;
    }

    close(ofd); 				/* Close (PRODOS) file */
    return;

} /* extract_file */



/*
 * print_global_info -- print global information of the binary II file
 */
void print_global_info(buf)
unsigned char * buf;
{
    int disk_blocks;

    disk_blocks = buf[117] + (buf[118]<<8) + (buf[119]<<16) + (buf[120]<<24);
    printf("%s, by Blu %d, ", blufile, buf[126]);
    printf("%d files, using %d blocks\n", buf[127]+1, disk_blocks);
    return;

} /* print_global_info */



/*
 * want -- return 1 if name exists in array wantlist,
 *         else return 0
 */
want(name, wantlist)
char *  name;
char ** wantlist;
{
    while (*wantlist != NULL) {
        if (strcmp(name, *wantlist++) == NULL)
            return(1);
    }
    return(0);

} /* want */



void usage()
{
    fprintf(stderr, "usage: %s [-hv] <blufile> [<filetoextract> ...]\n",
                        progname);
    return;

} /* usage */



void printhelp()
{
    fprintf(stderr, "\t-h show this help.\n");
    fprintf(stderr, "\t-v show contents of archive.\n");
}
