/*
 * Head - Display first few lines of specified files.
 *
 * Version 37.1 =TP= 11-Feb-92
 *
 * Compile with SAS/C 5.10 and link without startup code:
 *      lc -cqfist -v -b0 -rr -O -ms Head
 *      blink Head.o to Head sd sc
 *      protect Head +p
 *
 * Copyright (c) 1992 Torsten Poulin
 *
 * Torsten Poulin
 * Banebrinken 99, 2, lejlighed 77
 * DK 2400  København NV
 * DENMARK
 */

/****** English:HEAD ******************************************************
*
*   FORMAT
*       HEAD [[FROM] <files|patterns>] [TO <file>] [NUMBER <n>]
*
*   TEMPLATE
*       FROM/M,TO/K,NUM=NUMBER/N/K
*
*   PURPOSE
*       To display first few lines of specified files.
*
*   SPECIFICATION
*       Head copies the first NUMBER lines of each FROM file to the
*       default output or the TO file if one is given.  If no FROM
*       files are given, Head copies lines from the default input.
*       The default number of lines is 10.
*
*       When more than one file is specified, the start of each
*       file looks like:
*
*               ==>filename<==
*
*   EXAMPLE
*       A way to display a set of short files, identifying each
*       one, is:
*
*           1> HEAD filename1 filename2 NUMBER 9999
*
*   SEE ALSO
*       CONCAT, TAIL, TYPE
*
*   UNIX EQUIVALENT
*       head(BU_CMD)
*
***************************************************************************
*
*/
/****** dansk:HEAD ********************************************************
*
*   FORMAT
*       HEAD [[FROM] <filer|mønstre>] [TO <fil>] [NUMBER <n>]
*
*   SKABELON
*       FROM/M,TO/K,NUM=NUMBER/N/K
*
*   FORMÅL
*       At vise de første få linjer af de angivne filer.
*
*   SPECIFIKATION
*       Head kopierer de første NUMBER linjer fra hver FROM-fil
*       til sit standardoutput eller, hvis en fil er angivet med
*       TO, til den.  Hvis ingen filer er angivet med FROM,
*       kopierer Head linjer fra sit standardinput.  Angives der
*       ikke noget antal linjer med NUMBER, vises 10.
*
*       Når mere end én fil angives, markeres begyndelsen af hver
*       fil således:
*
*               ==>filnavn<==
*
*   EKSEMPEL
*       En måde at vise en samling korte filer, med deres navne,
*       er:
*
*           1> HEAD filnavn1 filnavn2 NUMBER 9999
*
*   SE OGSÅ
*       CONCAT, TAIL, TYPE
*
*   TILSVARENDE I UNIX
*       head(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>
#endif

#define MAXNAMELEN 256L

#define TEMPLATE "FROM/M,TO/K,NUM=NUMBER/N/K"

#define OPT_FROM 0
#define OPT_TO   1
#define OPT_NUM  2


char const *version = "\0$VER: Head 37.1 (11.2.92) ©1992 Torsten Poulin";


static LONG Head(struct Library *SysBase, struct DosLibrary *DOSBase,
                 BPTR out, UBYTE *filename, LONG num, BOOL names);


LONG entrypoint(VOID)
{
    struct Library       *SysBase;
    struct DosBase       *DOSBase;
    struct RDArgs        *args;
    struct AnchorPath    *ap;
    LONG                 arg[3];
    LONG                 rc = RETURN_OK;
    LONG                 num = 10L;
    UBYTE                **name;
    UBYTE                *filename;
    BPTR                 out;
    LONG                 several = 0L;
    
    SysBase = *(struct Library **) 4L;
    if(!(DOSBase = (struct DosLibrary *) OpenLibrary("dos.library", 37L)))
        goto noDOS;
    
    arg[OPT_FROM] = arg[OPT_TO] = arg[OPT_NUM] = 0L;
    
    out = Output();
    
    if(args = ReadArgs(TEMPLATE, arg, NULL))
    {
        if(arg[OPT_NUM])
        {
            num = *(ULONG *) arg[OPT_NUM];
            if(num < 1L) 
                num = 1L;
        }
                
        if(arg[OPT_TO] && !(out = Open((UBYTE *) arg[OPT_TO], MODE_NEWFILE)))
        {
            LONG err = IoErr();
            PrintFault(err, (UBYTE *) arg[OPT_TO]);
            rc = RETURN_ERROR;
            goto openError;
        }
            
        if(arg[OPT_FROM])
        {
            name = (UBYTE **) arg[OPT_FROM];
            for(; *name; name++)
                ++several;
            if(several < 2)
                several = FALSE;
            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, "Head");
                    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, "Head");
                    rc = RETURN_FAIL;
                    break;
                }

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

                ap->ap_Strlen = MAXNAMELEN;

                if(!IsWild || MatchFirst(*name, ap) == 0)
                    do
                    {
                        if(IsWild)
                        {
                            several = TRUE;
                            filename = ap->ap_Buf;
                            if(ap->ap_Info.fib_DirEntryType > 0)
                                continue;
                        }
                        else
                            filename = *name;
                        if((rc = Head(SysBase, DOSBase, out, filename,
                                      num, (BOOL) several)))
                            break;
                    } while(IsWild == 1 && MatchNext(ap) == 0);
                if(IsWild)
                    MatchEnd(ap);
                FreeMem(ap, sizeof(struct AnchorPath) + MAXNAMELEN);
            }
            if(arg[OPT_TO])
                Close(out);
        }
        else
            rc = Head(SysBase, DOSBase, out, NULL, num, (BOOL) several);
 openError:
        FreeArgs(args);
    }
    else
    {
        LONG err = IoErr();
        PrintFault(err, "Head");
        rc = RETURN_ERROR;
    }
                
    CloseLibrary((struct DosBase *) DOSBase);
 noDOS:
    return rc;
}


static LONG Head(struct Library *SysBase, struct DosLibrary *DOSBase,
                 BPTR out, UBYTE *filename, LONG num, BOOL names)
{
    register UBYTE breakcheck = 0;
    LONG  c;
    ULONG count = 0;
    LONG  rc = RETURN_OK;
    BPTR  in = Input();
    
    if(filename)
    {
        if(names)
        {
            FPuts(out, "==>");
            FPuts(out, filename);
            FPuts(out, "<==\n");
        }
        if(!(in = Open(filename, MODE_OLDFILE)))
        {
            LONG err = IoErr();
            PrintFault(err, filename);
            return RETURN_ERROR;
        }
    }

    while((c = FGetC(in)) != -1 && count < num)
    {
        if(c == '\n')
            ++count;
        FPutC(out, c);

        if(!(breakcheck -= 8) && SetSignal(0L,0L) & SIGBREAKF_CTRL_C)
        {
            PrintFault(ERROR_BREAK, NULL);
            rc = RETURN_WARN;
            break;
        }
    }
    if(filename)
        Close(in);
    return rc;
}
