/*
 * Split - Split a file into pieces
 *
 * Version 1 =TP= ??-???-89  (Write some comments  ;-(  )
 *      Unix look-alike command
 * Version 37.2 =TP= 21-Jan-92 26-Jan-92
 * Compile with SAS/C 5.10 and link without startup code:
 *      lc -cqfist -v -b0 -rr -O -ms Split
 *      blink Time.o to Split sd sc
 *      protect Split +p
 *
 * Copyright (c) 1989, 1992 Torsten Poulin
 *
 * Note: The program do not check for errors while writing
 *       because the include files for SAS/C declared
 *       FPutC() to be VOID instead of LONG!!!!  ;-(
 *
 * Torsten Poulin
 * Banebrinken 99, 2, lejlighed 77
 * DK 2400  København NV
 * DENMARK
 */

/****** English:SPLIT ******************************************************
*
*   FORMAT
*       SPLIT [[FROM] <file>] [TO <name>] [NUMBER <n> [PARTS]]
*             [BYTES] [PROMPT]
*
*   TEMPLATE
*       FROM,TO/K,NUM=NUMBER/K/N,PARTS/S,BYTES/S,PROMPT/S
*
*   PURPOSE
*       To split a file into pieces.
*
*   SPECIFICATION
*       Split reads <file> and writes it in <n>-line pieces (default
*       1000 lines) onto a set of output files.  The name of the
*       first output file is <name> with aa appended, and so on
*       lexicographically, up to zz (a maximum of 676 files). <name>
*       is truncated to 28 characters if it is longer.  If no output
*       name is given, x is default.
*
*       If the BYTES switch is used, the input file will be split in
*       <n>-character pieces instead.  This is especially useful
*       if the input is a binary file.
*
*       The PARTS switch, which can only be used in conjunction with
*       the NUMBER option, splits the input file in the specified
*       number of parts.
*
*       The PARTS switch implies the BYTES switch.
*
*       If the PROMPT switch is specified, the user will be prompted
*       before each output file is written, to make it possible to
*       change disks.
*
*       If no input file is given then the default input is used
*       making it possible to use Split as the end of a pipe-line.
*       
*   EXAMPLE
*       If the file `myfile' is split up with a command like this:
*           1> SPLIT myfile TO splitfile.
*       it can be assembled again with the following command:
*           1> CONCAT splitfile.?? TO newfile SORT
*
*   WARNING
*       Do not use the PARTS switch if reading from a pipe as
*       it causes Split to attempt to determine the size of it's
*       input using the dos.library function Seek().  If you do
*       it chances are that you end up with lots of small files
*       typically containing one byte each.
*
*   SEE ALSO
*       CONCAT, JOIN, TYPE
*
***************************************************************************
*
*/
/****** dansk:SPLIT *******************************************************
*
*   FORMAT
*       SPLIT [[FROM] <fil>] [TO <navn>] [NUMBER <n> [PARTS]]
*             [BYTES] [PROMPT]
*
*   SKABELON
*       FROM,TO/K,NUM=NUMBER/K/N,PARTS/S,BYTES/S,PROMPT/S
*
*   FORMÅL
*       At splitte en fil i mindre stykker.
*
*   SPECIFIKATION
*       Split læser <fil> og udskriver den i <n>-linje stykker
*       (standard er 1000 linjer) som en gruppe filer.  Navnet
*       på den første uddatafil er <navn> efterfulgt af aa og
*       så fremdeles, leksikografisk, indtil zz (højst 676 filer).
*       <navn> forkortes til 28 tegn hvis det er længere.  Hvis
*       der ikke er angivet noget uddatanavn, bruges x.
*
*       Hvis kontakten BYTES bruges vil inddatafilen blive delt
*       i stykker på <n> tegn i stedet.  Dette er særlig nyttigt
*       hvis inddata er en binær fil.
*
*       Kontakten PARTS, der kun kan bruges sammen med argumentet
*       NUMBER, deler inddatafilen i det angivne antal dele.
*
*       Kontakten PARTS indebærer automatisk kontakten BYTES.
*
*       Hvis kontakten PROMPT angives vil brugeren blive spurgt
*       før hver uddatafil skrives for at muliggøre disketteskift.
*
*       Hvis ingen inddatafil er angivet læses fra standardinputtet,
*       hvilket gør det muligt at bruge Split som afslutningen på
*       en pipe.
*
*   EKSEMPEL
*       Hvis filen `minfil' splittes op med en kommando som
*           1> SPLIT minfil TO splitfil.
*       kan den samles igen med følgende kommando:
*           1> CONCAT splitfil.?? TO nyfil SORT
*
*   ADVARSEL
*       Brug ikke kontakten PARTS hvis der læses fra en pipe, da
*       den får Split til at forsøge at bestemme størrelsen af
*       sit input med funktionen Seek() fra dos.library.  Hvis
*       man gør det vil man sandsynligvis ende med en mængde
*       små filer med typisk én oktet i hver.
*
*   SE OGSÅ
*       CONCAT, JOIN, TYPE
*
***************************************************************************
*
*/


#include <exec/types.h>
#include <dos/dos.h>
#include <dos/dostags.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>
#include <string.h>

#define EOF (-1)

#define OPT_FROM   0
#define OPT_TO     1
#define OPT_NUM    2
#define OPT_PARTS  3
#define OPT_BYTES  4
#define OPT_PROMPT 5


char const *version = "\0$VER: Split 37.2 (26.1.92)\
 ©1989,92 Torsten Poulin";

LONG error(struct DosLibrary *, LONG);

LONG entrypoint(void)
{
    struct RDArgs     *args;
    struct Library    *UtilityBase;
    struct DosLibrary *DOSBase;
    struct Library    *SysBase;

    LONG     arg[6];
    LONG     rc = RETURN_OK;
    BPTR     in, out = NULL;
    LONG     c, cnt, totalfiles = 0;
    LONG     len = 1;
    UBYTE    name[31] = "x";
    ULONG    number = 1000L;
    register UBYTE breakcheck = 0;
    
    SysBase = *(struct Library **) 4L;
    if(!(DOSBase = (struct DosLibrary *) OpenLibrary("dos.library", 37L)))
    {
        rc = RETURN_FAIL;
        goto exit1;
    }
    if(!(UtilityBase = OpenLibrary("utility.library", 37L)))
    {
        rc = RETURN_FAIL;
        goto exit2;
    }

    arg[OPT_FROM]  = arg[OPT_TO]    = arg[OPT_NUM] =
    arg[OPT_PARTS] = arg[OPT_BYTES] = arg[OPT_PROMPT] = 0L;
    
    if(args = ReadArgs("FROM,TO/K,NUM=NUMBER/K/N,PARTS/S,BYTES/S,PROMPT/S",
                       arg, NULL))
    {
        if(arg[OPT_NUM])
        {
            number = *(ULONG *) arg[OPT_NUM];
            if(number < 1L)
                number = 1L;
        }

        if(arg[OPT_TO])
        {
            UBYTE *a = (UBYTE *) arg[OPT_TO];

            for(len = 0; len < 28 && a[len]; len++)
                name[len] = a[len];
            name[len] = '\0';
        }

        if(in = arg[OPT_FROM] ? Open((UBYTE *) arg[OPT_FROM], MODE_OLDFILE)
                              : Input())
        {
            name[len] = 'a';
            name[len + 1] = 'a' - 1;
            name[len + 2] = '\0';

            if((BOOL) arg[OPT_PARTS] && arg[OPT_NUM])
            {
                ULONG n = (ULONG) number;
                
                if(n > 676L)
                    n = 676L;
                Seek(in, 0L, OFFSET_END);
                number = Seek(in, 0L, OFFSET_BEGINNING);
                number = UDivMod32((ULONG) number, n) + 1L;
                arg[OPT_BYTES] = TRUE;
            }
            cnt = number;

            while((c = FGetC(in)) != EOF)
            {
                if(cnt == number && 676L > totalfiles++)
                {
                    cnt = 0;
                    if(name[len + 1] < 'z')
                        name[len + 1]++;
                    else
                    {
                        name[len + 1] = 'a';
                        name[len]++;
                    }
                    name[len + 2] = '\0';
                    if(out)
                        Close(out);
                    if((BOOL) arg[OPT_PROMPT])
                    {
                        BPTR console;
                        
                        Write(Output(), "Press RETURN to write ", 22L);
                        Write(Output(), name, strlen(name));
                        if(console = Open("CONSOLE:", MODE_OLDFILE))
                        {
                            FGetC(console);
                            Close(console);
                        }
                    }
                    if(!(out = Open(name, MODE_NEWFILE)))
                    {
                        rc = error(DOSBase, RETURN_ERROR);
                        goto openErr;
                    }
                }
                /* if( */   FPutC(out, c)  /* == EOF)
                {
                    rc = error(DOSBase, RETURN_FAIL);
                    goto writeErr;
                } 
                */;
                if((BOOL) arg[OPT_BYTES] || c == '\n')
                    cnt++;
                if(!(breakcheck -= 8) && SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
                {
                    PrintFault(ERROR_BREAK, NULL);
                    rc = RETURN_WARN;
                    break;
                }
	    }
 writeErr:
            Close(out);
 openErr:
            if(arg[OPT_FROM])
                Close(in);
        }
        else
            rc = error(DOSBase, RETURN_ERROR);

        FreeArgs(args);
    }
    else
        rc = error(DOSBase, RETURN_ERROR);

    CloseLibrary(UtilityBase);
 exit2:
    CloseLibrary((struct Library *) DOSBase);
 exit1:
    return rc;
}


LONG error(struct DosLibrary *DOSBase, LONG code)
{
    LONG err = IoErr();

    PrintFault(err, "Split");
    return code;
}
