/*
 * Unique - AmigaDOS subset of UNIX uniq.
 *
 * Version 1 =TP= 1989 - Generic version (UNIX style); highly portable
 * Version 2 =TP= 1989 - as above.
 * Version 3 (37.3) =TP= 9-Feb-92 - AmigaDOS 2.0 specific version.
 *
 * Compile with SAS/C v5.10b and link without startup code:
 *      lc -cqfist -v -b0 -rr -O -ms Unique
 *      blink Unique.o to Unique sd sc
 *      protect Unique +p
 *
 * Copyright (c) 1989, 1992 Torsten Poulin
 *
 * Torsten Poulin
 * Banebrinken 99, 2, lejlighed 77
 * DK 2400  København NV
 * DENMARK
 */

/****** English:UNIQUE ****************************************************
*
*   FORMAT
*       UNIQUE [[FROM] <file>] [[TO] <file>] [REPEATED][UNIQUE][COUNT]
*
*   TEMPLATE
*       FROM,TO,REPEATED/S,UNIQUE/S,COUNT/S
*
*   PURPOSE
*       To report repeated lines in a file.
*
*   SPECIFICATION
*       The command Unique reads the FROM file comparing adjacent
*       lines.  In the normal case, the second and succeeding copies
*       of repeated lines are removed; the remainder is written
*       on the TO file.  Note that repeated lines must be adjacent
*       to be found (see SORT).  If the UNIQUE switch is used, just
*       the lines that are not repeated in the original file are
*       output.  The REPEATED switch specifies that one copy of just
*       the repeated lines is to be written.  The normal mode ouput
*       is the union of the UNIQUE and REPEATED mode outputs.
*
*       The COUNT switch generates an output report with each line
*       preceeded by a count of the number of times it occured.
*
*       If no FROM file is given input is read from the default
*       input;  likewise if no TO file is given output is written
*       to the default output.
*
*   SEE ALSO
*       COMMON, COUNT, SORT
*
*   BUGS
*       Unique will fail to function correctly given input lines
*       longer than 512 characters.
*
*   UNIX EQUIVALENT
*       uniq(BU_CMD)
*
***************************************************************************
*
*/
/****** dansk:UNIQUE ******************************************************
*
*   FORMAT
*       UNIQUE [[FROM] <fil>] [[TO] <fil>] [REPEATED][UNIQUE][COUNT]
*
*   SKABELON
*       FROM,TO,REPEATED/S,UNIQUE/S,COUNT/S
*
*   FORMÅL
*       At rapportere gentagne linjer i en fil.
*
*   SPECIFIKATION
*       Kommandoen Unique læser filen FROM og sammenligner
*       tilstødende linjer.  I det normale tilfælde fjernes den
*       anden og efterfølgende kopier af gentagne linjer.
*       Resten af filen skrives til filen TO.  Bemærk at gentagne
*       linjer skal stå lige efter hinanden for at blive fundet
*       (se SORT).  Hvis kontakten UNIQUE bruges bliver kun de
*       linjer der ikke er gentaget i den oprindelige fil
*       udskrevet.  Kontakten REPEATED angiver at én kopi af kun
*       de gentagne linjer skal udskrives.  Den normale adfærd
*       er at udskrive foreningsmængden af hvad UNIQUE og
*       REPEATED ville producere.
*
*       Kontakten COUNT frembringer en uddatarapport hvor hver
*       linje er foranstillet antallet af gange den forekom.
*
*       Hvis argumentet FROM ikke er angivet, læses inddata fra
*       standardinputtet.  Ligeledes skrives uddata til standard-
*       outputtet hvis argumentet TO er udeladt.
*
*   SE OGSÅ
*       COMMON, COUNT, SORT
*
*   FEJL OG MANGLER
*       Unique vil ikke virke korrekt hvis nogle inddatalinjer
*       er længere end 512 tegn.
*
*   TILSVARENDE I UNIX
*       uniq(BU_CMD)
*
***************************************************************************
*
*/


#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/dosasl.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <stdio.h>
#include <string.h>

#ifdef __SASC
#include <pragmas/dos_pragmas.h>
#include <pragmas/exec_pragmas.h>
#pragma libcall UtilityBase Stricmp A2 9802
#endif

/* my include files are for early V36, so ... */
LONG Stricmp(char *, char *);

#define MAXLINE (512+1)
#define MAXNAME 50

#define TEMPLATE "FROM,TO,REPEATED/S,UNIQUE/S,COUNT/S"

#define OPT_FROM   0
#define OPT_TO     1
#define OPT_REPEAT 2
#define OPT_UNIQUE 3
#define OPT_COUNT  4

char const *version = "\0$VER: Unique 37.3 (9.2.92) ©1989,92 Torsten Poulin";

static BOOL ioerr(struct DosLibrary *DOSBase);
static LONG unique(struct Library *SysBase, struct DosLibrary *DOSBase,
                   struct Library *UtilityBase, BPTR in, BPTR out,
                   BOOL dflag, BOOL uflag, BOOL cflag);


LONG entrypoint(VOID)
{
    BPTR in, out;
    ULONG err;
    LONG rc = RETURN_OK;
    
    struct Library *SysBase;
    struct DosBase *DOSBase;
    struct Library *UtilityBase;
    struct RDArgs  *args;
    LONG           arg[5];
        
    SysBase = *(struct Library **) 4L;
    if(!(DOSBase = (struct DosLibrary *) OpenLibrary("dos.library", 37L)))
    {
        rc = RETURN_FAIL;
        goto noDOS;
    }
    if(!(UtilityBase = OpenLibrary("utility.library", 37L)))
    {
        rc = RETURN_FAIL;
        goto noUtility;
    }
    
    arg[OPT_FROM] = arg[OPT_TO] = 
    arg[OPT_REPEAT] = arg[OPT_UNIQUE] = arg[OPT_COUNT] = 0L;
    
    if(args = ReadArgs(TEMPLATE, arg, NULL))
    {
        if(arg[OPT_FROM])
        {
            if(!(in = Open((UBYTE *) arg[OPT_FROM], MODE_OLDFILE)))
            {
                err = IoErr();
                PrintFault(err, (UBYTE *) arg[OPT_FROM]);
                rc = RETURN_ERROR;
                goto errExit;
            }
        }
        else
            in = Input();

        if(arg[OPT_TO])
        {
            if(!(out = Open((UBYTE *) arg[OPT_TO], MODE_NEWFILE)))
            {
                err = IoErr();
                PrintFault(err, (UBYTE *) arg[OPT_TO]);
                rc = RETURN_ERROR;
                goto errExit;
            }
        }
        else
            out = Output();
        
        rc = unique(SysBase, DOSBase, UtilityBase, in, out,
                    (BOOL) arg[OPT_REPEAT],
                    (BOOL) arg[OPT_UNIQUE],
                    (BOOL) arg[OPT_COUNT]);

        if(arg[OPT_FROM])
            Close(in);
        if(arg[OPT_TO])
            Close(out);

        FreeArgs(args);
    }
    else
    {
        LONG err = IoErr();
        PrintFault(err, "Unique");
        rc = RETURN_ERROR;
    }
    
 errExit:
    CloseLibrary(UtilityBase);
 noUtility:
    CloseLibrary((struct Library *) DOSBase);
 noDOS:
    return rc;
}


static LONG unique(struct Library *SysBase, struct DosLibrary *DOSBase,
                   struct Library *UtilityBase, BPTR in, BPTR out,
                   BOOL dflag, BOOL uflag, BOOL cflag)
{
    ULONG count;
    UBYTE prevline[MAXLINE];
    UBYTE nextline[MAXLINE];
    UBYTE *temp;
    UBYTE *prev = prevline;
    UBYTE *next = nextline;
    
    if(dflag && uflag)
        dflag = uflag = FALSE;

    count = 1;
    if(FGets(in, prev, MAXLINE) == NULL)
    {
        if(ioerr(DOSBase))
            return RETURN_FAIL;
    }
    else
    {
        while(FGets(in, next, MAXLINE) != NULL)
        {

            if(Stricmp(prev, next) == 0)
                ++count;
            else if((!dflag && !uflag) ||
                    (count == 2 && !uflag) ||
                    (count == 1 && uflag))
            {
                if(cflag)
                    VFPrintf(out, "%10lu ", (LONG *) &count);
                FPuts(out, prev);
                count = 1;
            }
            else
                count = 1;
            temp = prev;
            prev = next;
            next = temp;

            if(SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
            {
                PrintFault(ERROR_BREAK, NULL);
                return RETURN_WARN;
            }
        }
        if(ioerr(DOSBase))
            return RETURN_FAIL;

        if((!dflag && !uflag) ||
           (count == 2 && !uflag) ||
           (count == 1 && uflag))
        {
            if(cflag)
                VFPrintf(out, "%10lu ", (LONG *) &count);
            FPuts(out, prev);
        }
    }
    return RETURN_OK;
}


static BOOL ioerr(struct DosLibrary *DOSBase)
{
    LONG err;
    
    if(!(err = IoErr()))
        return FALSE;
    PrintFault(err, "Unique");
    return TRUE;
}
