/*
 * Count - count lines, words, and characters.
 *
 * Version 37.3 =TP= 29-Jan-92
 *
 * Compile with SAS/C 5.10 and link without startup code:
 *      lc -cqfist -v -b0 -rr -O -ms Count
 *      blink Count.o to Count sd sc
 *      protect Count +p
 *
 * Copyright (c) 1992 Torsten Poulin
 *
 * Torsten Poulin
 * Banebrinken 99, 2, lejlighed 77
 * DK 2400  København NV
 * DENMARK
 */

/****** English:COUNT *****************************************************
*
*   FORMAT
*       COUNT [[FROM] <names|patterns>] [LINES] [WORDS] [CHARS]
*
*   TEMPLATE
*       FROM/M,LINES/S,WORDS/S,CHARS/S
*
*   PURPOSE
*       To count lines, words, and characters.
*
*   SPECIFICATION
*       Count counts lines, words, and characters in the named files,
*       or in the default input if no names appear.  It also keeps
*       a total count for all named files.  A word is a maximal
*       string of characters delimited by spaces, tabs, or new-lines.
*
*       The switches LINES, WORDS, and CHARS may be used to specify
*       that a subset of lines, words, and characters are to be
*       reported.  If no switches are given Count defaults to
*       all of them.
*
*       When names are specified on the command line, they will be
*       printed along with the counts.
*
***************************************************************************
*
*/
/****** dansk:COUNT *******************************************************
*
*   FORMAT
*       COUNT [[FROM] <navne|mønstre>] [LINES] [WORDS] [CHARS]
*
*   SKABELON
*       FROM/M,LINES/S,WORDS/S,CHARS/S
*
*   FORMÅL
*       At tælle linjer, ord og tegn.
*
*   SPECIFIKATION
*       Count tæller linjer, ord og tegn i de nævnte filer eller fra
*       sit standardinput hvis ingen navne er angivet.  Den laver
*       også en sluttotal for alle filerne.  Et ord er en maksimal
*       tegnfølge afgrænset af blank-, tabulerings- eller
*       ny-linjetegn.
*
*       Kontakterne LINES, WORDS og CHARS kan bruges til at angive
*       at en delmængde af linjer, ord og tegn skal rapporteres.
*       Hvis der ikke angives nogen kontakter antager Count at der
*       menes dem alle sammen.
*
*       Når filnavne angives på kommandolinjen udskrives de sammen
*       med optællingerne.
*
***************************************************************************
*
*/


#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>
#endif

#define MAXNAMELEN 256L


#define TEMPLATE "FROM/M,LINES/S,WORDS/S,CHARS/S"

#define OPT_FROM    0
#define OPT_LINES   1
#define OPT_WORDS   2
#define OPT_CHARS   3


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

static BOOL count(struct Library *, struct DosLibrary *, BPTR, UBYTE *,
                  BOOL, BOOL, BOOL, ULONG *, ULONG *, ULONG *);


LONG entrypoint(VOID)
{
    struct Library       *SysBase;
    struct DosBase       *DOSBase;
    struct RDArgs        *args;
    struct AnchorPath    *ap;
    LONG                 arg[4];
    LONG                 rc = RETURN_OK;
    UBYTE                **name;
    UBYTE                *filename;
    BPTR                 fp;
    ULONG                tl, tw, tc;
    ULONG                several = 0;
    
    SysBase = *(struct Library **) 4L;
    if(!(DOSBase = (struct DosLibrary *) OpenLibrary("dos.library", 37L)))
        goto noDOS;
    
    arg[OPT_FROM] = arg[OPT_LINES] = arg[OPT_WORDS] = arg[OPT_CHARS] = 0L;
    tl = tw = tc = 0L;
    
    if(args = ReadArgs(TEMPLATE, arg, NULL))
    {
        if(!arg[OPT_LINES] && !arg[OPT_WORDS] && !arg[OPT_CHARS])
            arg[OPT_LINES] = arg[OPT_WORDS] = arg[OPT_CHARS] = TRUE;
            
        if(arg[OPT_FROM])
        {
            name = (UBYTE **) arg[OPT_FROM];
            for(; *name; name++)
            {
                UBYTE *dummy;
                LONG  IsWild;
            
                /* Is it a pattern? */
                if(!(dummy = AllocMem(2 * strlen(*name) + 2, MEMF_PUBLIC)))
                {
                    PrintFault(ERROR_NO_FREE_STORE, "Count");
                    rc = RETURN_FAIL;
                    break;;
                }
                IsWild = ParsePattern(*name, dummy, 2 * strlen(*name) + 2);
                FreeMem(dummy, 2 * strlen(*name) + 2);
                if(IsWild == -1)
                {
                    LONG err = IoErr();
                    PrintFault(err, "Count");
                    rc = RETURN_FAIL;
                    break;
                }

                if(!(ap = AllocMem(sizeof(struct AnchorPath) + MAXNAMELEN,
                                   MEMF_PUBLIC | MEMF_CLEAR)))
                {
                    PrintFault(ERROR_NO_FREE_STORE, "Count");
                    rc = RETURN_FAIL;
                    break;
                }

                ap->ap_Strlen = MAXNAMELEN;

                if(!IsWild || MatchFirst(*name, ap) == 0)
                    do
                    {
                        BOOL success;
                        
                        if(IsWild)
                        {
                            filename = ap->ap_Buf;
                            if(ap->ap_Info.fib_DirEntryType > 0)
                                continue;
                        }
                        else
                            filename = *name;
                            
                        if(!(fp = Open(filename, MODE_OLDFILE)))
                        {
                            LONG err = IoErr();
                            PrintFault(err, filename);
                            continue;
                        }
                        several++;
                        success = count(SysBase, DOSBase, fp, filename,
                                        arg[OPT_LINES],
                                        arg[OPT_WORDS],
                                        arg[OPT_CHARS],
                                        &tl, &tw, &tc);
                        Close(fp);
                        if(!success)
                        {
                            several = 0L;
                            rc = RETURN_WARN;
                            break;
                        }
                    } while(IsWild == 1 && MatchNext(ap) == 0);
                if(IsWild)
                    MatchEnd(ap);
                FreeMem(ap, sizeof(struct AnchorPath) + MAXNAMELEN);

	    }
            if(several > 1)
            {
                if(arg[OPT_LINES]) VPrintf("%12ld", (LONG *) &tl);
                if(arg[OPT_WORDS]) VPrintf("%12ld", (LONG *) &tw);
                if(arg[OPT_CHARS]) VPrintf("%12ld", (LONG *) &tc);
                PutStr(" total\n");
            }            
        }
        else
            if(!count(SysBase, DOSBase, Input(), NULL,
                      arg[OPT_LINES], arg[OPT_WORDS], arg[OPT_CHARS],
                      &tl, &tw, &tc))
                rc = RETURN_WARN;

        FreeArgs(args);
    }
    else
    {
        LONG err = IoErr();
        PrintFault(err, "Count");
        rc = RETURN_ERROR;
    }
                
    CloseLibrary((struct DosBase *) DOSBase);
 noDOS:
    return rc;
}


static BOOL count(struct Library *SysBase, struct DosLibrary *DOSBase,
                  BPTR fp, UBYTE *filename,
                  BOOL lines, BOOL words, BOOL chars,
                  ULONG *tl, ULONG *tw, ULONG *tc)
{
    ULONG   nc, nl, nw;
    BOOL    inword;
    LONG    c;
    register UBYTE breakcheck = 0;
       
    nc = nl = nw = 0L;
    inword = FALSE;
    
    while((c = FGetC(fp)) != -1L)
    {
        if(!(breakcheck -= 4) && SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
        {
            PrintFault(ERROR_BREAK, NULL);
            return FALSE;
        }

        if(c == '\n')
            nl++;
        nc++;
        if(c == ' ' || c == '\t' || c == '\n') 
            inword = FALSE;
        else if(!inword)
        {
            inword = TRUE;
            nw++;
        }
    }
    
    *tl += nl;
    *tw += nw;
    *tc += nc;
        
    if(lines) VPrintf("%12ld", (LONG *) &nl);
    if(words) VPrintf("%12ld", (LONG *) &nw);
    if(chars) VPrintf("%12ld", (LONG *) &nc);
    if(filename)
    {
        FPutC(Output(), ' ');
        PutStr(filename);
    }
    FPutC(Output(), '\n');
    return TRUE;
}
