
/* bu.c - Break up a UCSD volume into files */
/* Copyright 1990 by John Foust */
/* July 13, 1990 */

#include <stdio.h>
#include <string.h>

#include "diskdecl.h"

#include "ucsddecl.h"
#define PRIMARY
#include "ucsdvars.h"


/* Convert UCSD text to regular text */

void convUCSDTextToASCII( FILE *infp, FILE *outfp, long size )
{
int each;
int ch;


    while ( (size > 0) && (ch=getc(infp)) != EOF) {

        size --;

        /* Handle indents at front of line */
        if (ch == DLE) {

            /* Get indent amount, where 32 means indent 0 */
            ch = getc( infp );
            size --;
            if (ch >= 32) {
                ch = ch - 32;
                for (each=0; each<ch; each++) {
                   putc( ' ', outfp );
                }
            }
        }
        /* If a CR, output LF */
        else if (ch == 0x0D) {
            /* On the PC, emit a CR/LF sequence at the end of lines */
#if defined(_MSC_VER)
            putc( '\r', outfp );
#endif
            /* On other machines, emit just LF */
            putc( '\n', outfp );
        }
        /* Skip nulls, but output everything else */
        else if (ch != 0) {
            putc( ch, outfp );
        }
    }
}


/* Handy read/write buffer */

char wrbuf[ UCSD_BLK_SIZE ];


/* File descriptions */

ucsdDirEntry dir[ UCSD_MAX_DIR_ENTRIES ];     /* 77 * 26 bytes = 2002 */


/* */

void saveAsBinary( FILE *infp, FILE *outfp, ucsdDirEntry *eachEntry, int skip )
{
long actualBytes;
long skipSize;
long each;


    actualBytes = (eachEntry->lastBlk - eachEntry->firstBlk) * UCSD_BLK_SIZE 
                 + eachEntry->lastByte;

    /* Read past the one header block */
    /* or Read past the two EDITOR header blocks */
    skipSize = (long) (skip * UCSD_BLK_SIZE);
    if (fseek( infp, skipSize, SEEK_CUR ) != 0) {
        puts( "Seek failed." );
    }
    actualBytes -= skipSize;

    /* Read-write the bytes that follow */
    for (each=0; each<actualBytes; each++) {
        if (fread( &wrbuf[0], (size_t) 1, (size_t) 1, infp ) != 1) {
            printf( "Read of byte %hd failed\n", each );
        }
        fwrite( &wrbuf[0], (size_t) 1, (size_t) 1, outfp );
    }

#ifdef UNUSED
    /* Read-write subsequent blocks, except first ones we skipped,
       and on the last block, only write the number of bytes really there */
    for (bk=eachEntry->firstBlk + skip; bk<eachEntry->lastBlk; bk++) {
        if (fread( &wrbuf[0], (size_t) UCSD_BLK_SIZE, (size_t) 1, infp ) != 1) {
            printf( "Read of block %hd failed\n", bk );
        }
        fwrite( &wrbuf[0], (size_t) UCSD_BLK_SIZE, (size_t) 1, outfp );
    }
#endif
}


/* Options for file contents translation */

#define XLATE_FORCE_TEXT     1   /* -text : Force all files through text translation */
#define XLATE_BELIEVE_EXTENT 2   /* -ext  : Believe .text filename extensions (default) */
#define XLATE_BELIEVE_KIND   3   /* -kind : Believe file type  (default) */
#define XLATE_AS_RAW         4   /* -raw  : Perform no translation */
#define XLATE_AS_CODE        5   /* -code : Perform no translation, but skip one header */
#define XLATE_AS_DATA        6   /* -data : Perform no translation, but skip two headers */


/* Burst files from disk image files */

int main( int argc, char **argv )
{
USHORT lerr;
short each;
short freespace;
short bk;
char inname[128];
FILE *infp;
FILE *outfp;
ucsdDirRoot dirRoot;
ucsdDirEntry *eachEntry;
char newname[16];
int translate;
BOOL lowercase;
BOOL setTime;
int len;
BOOL hasTextExtension;


    infp = NULL;
    outfp = NULL;

    /* Defaults */
    /* Believe .text extensions when found */
    translate = XLATE_BELIEVE_EXTENT;
    lowercase = FALSE;
    setTime = TRUE;

    if (argc == 1) {
        puts( "Bursts files from UCSD P-System disk images" );
        puts( "Usage: bu imagefilename [options]" );
        puts( "       [-raw]   Perform no translation" );
        puts( "       [-code]  Perform no translation, but skip one header" );
        puts( "       [-data]  Perform no translation, but skip two headers" );
        puts( "       [-ext]   Trust extension .TEXT to be as valid as file type (default)" );
        puts( "       [-kind]  Believe file type (default)" );
     /* puts( "       [-text]  Force translation of all files as text files" ); */
     /* puts( "       [-lower] Force filenames to lowercase" ); */
        goto out;
    }

    /* Grab disk image filename */
    if (argc > 1) {
        strcpy( inname, argv[1] );
    }

    /* Look for options */
    if (argc > 2) {
        int each;

        for (each=2; each<argc; each++) {

            /* Don't translate anything */
            if (strcmp( argv[each], "-raw" ) == 0) {
                translate = XLATE_AS_RAW;
            }

            /* Skip two blocks, then save as raw */
            if (strcmp( argv[each], "-data" ) == 0) {
                translate = XLATE_AS_DATA;
            }

            /* Skip one block, then save as raw */
            if (strcmp( argv[each], "-code" ) == 0) {
                translate = XLATE_AS_CODE;
            }

            /* Trust .TEXT extensions to be as valid as file type (default) */
            if (strcmp( argv[each], "-ext" ) == 0) {
                translate = XLATE_BELIEVE_EXTENT;
            }

            /* Treat all as text */
            if (strcmp( argv[each], "-text" ) == 0) {
                translate = XLATE_FORCE_TEXT;
            }

            /* make lowercase filenames */
            if (strcmp( argv[each], "-lower" ) == 0) {
                lowercase = TRUE;
            }
        }
    }

    /* Open input file from command line */
    printf( "Bursting UCSD disk image file '%s'\n\n", inname );
    infp = fopen( inname, "rb" );
    if (infp == NULL) {
        puts( "Couldn't open disk image file." );
        goto out;
    }

    /* Read volume info */
    if ((lerr=ucsdReadVolInfo( infp, &dirRoot )) != TE_NOERROR) {
        puts( "Failed on volume info." );
        goto out;
    }

    /* Read file directory info */
    if ((lerr=ucsdReadFileInfo( infp, dir )) != TE_NOERROR) {
        puts( "Failed on file directory info." );
        goto out;
    }

    /* Calculate free space on volume */
    freespace = ucsdCalcFreeSpace( &dirRoot, dir );

    /* Print volume info header */
    if ((lerr=ucsdPrintVolInfo( &dirRoot, freespace )) != TE_NOERROR) {
        goto out;
    }

    /* Burst each entry */
    outfp = NULL;
    eachEntry = dir;
    for (each=0; each<dirRoot.numFiles; each++) {

        /* Convert each filename from Pascal string to C string */
        unPascalStr( &eachEntry->fileName[0] );

        /* Start with the raw UCSD filename */
        strcpy( newname, eachEntry->fileName );

        /* Notice if the filename has ".TEXT" extension, as opposed to ->fileKind */
        hasTextExtension = FALSE;
        len = strlen( eachEntry->fileName );
        if (len > 4) {
            if (strcmp( &eachEntry->fileName[ len - 5 ], ".TEXT" ) == 0) {
                hasTextExtension = TRUE;
            }
        }

        /* Could make an option to force filenames to lower-case */
        if (lowercase == TRUE) {
            strLower( newname );
        }

        /* Translate the filename to something DOS-compatible */
        /* Translate the filename to something WinNT-compatible */
        /* For example, UCSD allows '/' in filenames */
        /* makeDOSname( newname ); */
        makeWinNTname( newname );

        /* If we changed the name, we might want to show them the new name */
        printf( "Making '%-15s'", eachEntry->fileName );
        if (strcmp( newname, eachEntry->fileName) != 0) {
            printf( " as '%-15s'", newname );
        }

        /* Print info about file as we make it */
        printf( " %s", ucsdTypeStr( eachEntry->fileKind ) );
        printf( "  %2hd-%s-%2hd", 
                ucsdDAY(eachEntry->lastAccess),
                ucsdMonthStr( (short) (ucsdMONTH(eachEntry->lastAccess)) ),
                ucsdYEAR(eachEntry->lastAccess) );
        printf( "  %4hd %4hd\n", eachEntry->firstBlk, eachEntry->lastBlk );

        /* Open the output file */
        outfp = fopen( newname, "wb" );
        if (outfp == NULL) {
            printf( "Couldn't open file '%s' (skipped)\n", newname );
            /* goto out; */
        }

        if (outfp != NULL) {

            /* Move to the beginning of this file */
            if (fseek( infp, (long) eachEntry->firstBlk * UCSD_BLK_SIZE, SEEK_SET ) != 0) {
                puts( "Seek failed." );
                goto out;
            }

            /* Perform no translation of all files */
            if (translate == XLATE_AS_RAW) {
                saveAsBinary( infp, outfp, eachEntry, 0 );
            }
            /* Skip one header, but save as raw data */
            else if (translate == XLATE_AS_CODE) {
                saveAsBinary( infp, outfp, eachEntry, 1 );
            }
            /* Skip two .TEXT-style header blocks, but save rest as raw data */
            else if (translate == XLATE_AS_DATA) {
                saveAsBinary( infp, outfp, eachEntry, 2 );
            }
            /* Convert text files from UCSD compressed format to ASCII */
            /* Do this if we're forcing everything as text, or if the file kind
            says it's text, or if we want to believe the file extension */
            else if (eachEntry->fileKind == FK_TEXT
                || translate == XLATE_FORCE_TEXT
                || (hasTextExtension == TRUE && translate == XLATE_BELIEVE_EXTENT)) {

                /* Read past the two EDITOR header blocks */
                if (fseek( infp, (long) 2 * UCSD_BLK_SIZE, SEEK_CUR ) != 0) {
                    puts( "Seek failed." );
                    goto out;
                }

                /* Filter the rest of the file */
                convUCSDTextToASCII( infp, outfp, 
                    (long) UCSD_BLK_SIZE * (eachEntry->lastBlk - eachEntry->firstBlk - 2) );
            }
            /* Code */
            else if (eachEntry->fileKind == FK_CODE) {

                /* For each block, read-write header plus all data */
                for (bk=eachEntry->firstBlk; bk<eachEntry->lastBlk+1; bk++) {

                    if (fread( &wrbuf[0], (size_t) UCSD_BLK_SIZE, (size_t) 1, infp ) != 1) {
                        puts( "Read failed" );
                        goto out;
                    }
                    fwrite( &wrbuf[0], (size_t) UCSD_BLK_SIZE, (size_t) 1, outfp );
                }
            }
            /* Else just write the binary data */
            else {
                saveAsBinary( infp, outfp, eachEntry, 0 );
            }

            /* Close the file */
            fclose( outfp );
            outfp = NULL;

            /* Set timestamp of file */
            if (setTime == TRUE) {
                ucsdSetTimeStamp( newname, eachEntry );
            }

        } /* if we could open it */

        eachEntry ++;

    } /* for each entry */

out:
    if (infp != NULL) {
        fclose( infp );
        infp = NULL;
    }
    if (outfp != NULL) {
        fclose( outfp );
        outfp = NULL;
    }

    return 0;
}

