/*
*   This file released to public domain by the copyright holders 
*   including ArrayComm, Inc.
*   Author: Ted Merrill
*/

/*  extract.c -- extracts part of a file or stdin */
/*
*   Usage:
*       <sourcefile extract nbytes1 outfile1 nbytes2 outfile2 ...
*
*       where
*           the first nbytes1 bytes are placed in outfile1,
*           the next nbytes2 bytes are placed in outfile2,
*       and
*           an nbytes specification may be "all" to
*           place all remaining bytes into the corresponding outfile,
*       and
*           a file spec may be "-" to write the output
*           to the std output, or "" to skip the next nbytes,
*           or may be of the form "+filename" to indicate that
*           the file should be appended to if it exists.
*               
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>



#define STDIN 0
#define STDOUT 1

#define BUFSIZE 8192

/*-E-*/
int main( argc, argv )
        int argc;
        char **argv;
{
    char *progname;

    if ( argc == 1 )
    {
        fprintf(stderr, "Use -h for help.\n");
        exit(0);
    }
    else
    if ( ! strcmp( argv[1], "-h" ) )
    {   /* help message */
printf("   Usage:\n");
printf("        <sourcefile extract nbytes1 outfile1 nbytes2 outfile2 ...\n");
printf("\n");
printf("        where\n");
printf("            the first nbytes1 bytes are placed in outfile1,\n");
printf("            the next nbytes2 bytes are placed in outfile2,\n");
printf("        and\n");
printf("            an nbytes specification may be `all' to\n");
printf("            place all remaining bytes into the corresponding outfile,\n");
printf("        and\n");
printf("            a file spec may be `-' to write the output\n");
printf("            to the std output, or `' to skip the next nbytes,\n");
printf("            or may be of the form `+filename' to indicate that\n");
printf("            the file should be appended to if it exists.\n");
        exit(0);
    }

    progname = argv[0];
    argv++;

    /* loop thru the nbyte/file spec pairs: */
    while ( argv[0] && argv[1] )
    {
        int nbytes;
        int outfile;

        /* nbytes = "all" for all the remainder of input */
        if ( strcmp( "all", argv[0] ) == 0 )
        {
            nbytes = -1;        /* flag value */
        }
        /* nbytes = decimal numeral */
        else
        {
            if ( sscanf(argv[0], "%i", &nbytes) != 1 || nbytes <= 0 )
            if ( nbytes <= 0 )
            {
                fprintf( stderr, "%s: Bad nbytes count: %s.\n",
                                        progname, argv[0] );
                exit(1);
            }
        }

        /* file = "-" for std output */
        if ( strcmp( "-", argv[1] ) == 0 )
        {
            outfile = STDOUT;
        }
        /* file = "" (null string) for throw away */
        else
        if ( argv[1][0] == '\0' )
        {
            outfile = -1;       /* skip these bytes */
        }
        /* file = +filename for append/create file */
        else
        if ( argv[1][0] == '+' )
        {
            outfile = open(argv[1]+1, O_WRONLY | O_CREAT, 0666 );
            if ( outfile == -1 )
            {
                fprintf( stderr, "%s: File Create Error: %s.\n",
                                        progname, argv[1] );
                exit(1);
            }
            lseek( outfile, 0, 2 );     /* goto end of file */
        }
        /* file = filename for truncate/create file */
        else
        {
            outfile = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0666);
            if ( outfile == -1 )
            {
                fprintf( stderr, "%s: File Create Error: %s.\n",
                                        progname, argv[1] );
                exit(1);
            }
        }

        /* loop thru buffer reads */
        while ( nbytes != 0 )
        {               /* loop thru this file */
            int toread;
            int nread;
            int nwrite;
            char buf[BUFSIZE];

            toread = BUFSIZE;
            /* don't read more than remaining nbytes */
            if ( (nbytes > 0 )  /* not flag value */
                && ( toread > nbytes ) )  toread = nbytes;
            /* always read from std input */
            nread = read( STDIN, buf, toread );
            /* read returns negative for error */
            if ( nread < 0 )
            {
                fprintf( stderr, "%s: Error Reading Input.\n",
                                        progname );
                exit(1);
            }
            /* read may read less than requested but > 0 */
            else
            if ( nread > 0 )
            {
                /* if we are not skipping bytes, write what was read */
                if ( outfile != -1 )    /* a real file */
                {
                    nwrite = write( outfile, buf, nread );
                    /* error if we don't write all the bytes */
                    if ( nwrite != nread )
                    {
                        fprintf( stderr, "%s: File Write Error: %s.\n",
                                        progname, argv[1] );
                        exit(1);
                    }
                }
            }
            /* read returns 0 only on end of file */
            else
            {
                if ( nbytes == -1 )     /* flag value for write all */
                    goto done;          /* then ok, we are done */
                fprintf( stderr, "%s: Premature End of Input.\n",
                                        progname );
                exit(1);
            }
            /* count down the bytes read */
            if ( nbytes > 0 )   /* not a flag value */
                nbytes -= nread;        /* account for bytes read */
        }
        /* next args */
        argv += 2;      /* next file */
    }
    /* success */
    done:
    exit( 0 );
    return 0;
}
