/*
 *  Count - Counts lines, words, and characters.
 *  Copyright (C) 1989, 1991, 1992, 1993 Torsten Poulin
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  The author can be contacted by s-mail at
 *    Torsten Poulin
 *    Banebrinken 99, 2, 77
 *    DK-2400 Copenhagen NV
 *    DENMARK
 *
 * $Id: Count.c,v 37.9 93/06/20 18:52:28 Torsten Rel $
 * $Log:	Count.c,v $
 * Revision 37.9  93/06/20  18:52:28  Torsten
 * Now does its own buffering (32k) in order to speed things up.
 * The effect is quite impressive :-)
 * 
 * Revision 37.8  93/03/01  12:49:37  Torsten
 * Changed all occurrences of "struct DosBase *" to "struct DosLibrary *"
 * 
 * Revision 37.7  93/02/22  18:15:07  Torsten
 * Changed to be linked with the support library
 * 
 * Revision 37.6  93/02/19  18:04:51  Torsten
 * Fixed minor bug in the return code check in entrypoint().
 * 
 * Revision 37.5  93/02/19  02:53:58  Torsten
 * Major rewrite; totally restructured the code.
 * 
 * Revision 37.4  93/02/12  13:19:15  Torsten
 * Moved the source to RCS.
 * Reformatted source.
 * Replaced exec.library/SetSignal() with dos.library/CheckSignal().
 * 
 */

#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>
#ifdef __SASC
#include <pragmas/dos_pragmas.h>
#include <pragmas/exec_pragmas.h>
#endif
#include "tastlib.h"
#include "count_rev.h"

#define PROGNAME "Count"
#define TEMPLATE "FILE=FROM/M,LINES/S,WORDS/S,BYTES=CHARS/S"
#define OPT_FROM    0
#define OPT_LINES   1
#define OPT_WORDS   2
#define OPT_CHARS   3
#define BUFLEN 32768L

char const versionID[] = VERSTAG;
char const copyright[] = "$COPYRIGHT:©1989,1991,1992,1993 Torsten Poulin$";

typedef struct {
  struct DosLibrary *DOSBase;
  BOOL lines;
  BOOL words;
  BOOL chars;
  ULONG tl;
  ULONG tw;
  ULONG tc;
  ULONG several;
  UBYTE buffer[BUFLEN];
} Global;

LONG count(UBYTE *filename, Global *global);


LONG entrypoint(VOID)
{
  struct DosLibrary *DOSBase;
  struct RDArgs *args;
  Global *global;
  LONG arg[4];
  LONG rc = RETURN_OK;

  if (!(DOSBase = (struct DosLibrary *) OpenLibrary("dos.library", 37L)))
    return RETURN_FAIL;

  if (!(global = AllocVec(sizeof(Global), MEMF_PUBLIC | MEMF_CLEAR)))
  {
    PrintFault(ERROR_NO_FREE_STORE, PROGNAME);
    rc = RETURN_FAIL;
    goto OutOfMemory;
  }
  global->DOSBase = DOSBase;

  arg[OPT_FROM] = arg[OPT_LINES] = arg[OPT_WORDS] = arg[OPT_CHARS] = 0L;

  if (!(args = ReadArgs(TEMPLATE, arg, NULL)))
  {
    printerror(PROGNAME, global);
    rc = RETURN_ERROR;
  }
  else
  {
    if (!arg[OPT_LINES] && !arg[OPT_WORDS] && !arg[OPT_CHARS])
      arg[OPT_LINES] = arg[OPT_WORDS] = arg[OPT_CHARS] = TRUE;

    global->lines = (BOOL) arg[OPT_LINES];
    global->words = (BOOL) arg[OPT_WORDS];
    global->chars = (BOOL) arg[OPT_CHARS];

    if (!arg[OPT_FROM])
      rc = count(NULL, global);
    else
      rc = foreach((UBYTE **) arg[OPT_FROM], count, global);
    FreeArgs(args);

    if (rc == ERROR_BREAK)
    {
      PrintFault(ERROR_BREAK, NULL);
      rc = RETURN_WARN;
    }
    else if (rc == ERROR_NO_FREE_STORE)
    {
      PrintFault(ERROR_NO_FREE_STORE, PROGNAME);
      rc = RETURN_FAIL;
    }
    else if (rc != RETURN_OK)
      printerror(PROGNAME, global);
    else if (global->several > 1)
    {
      if (global->lines) VPrintf("%12ld", (LONG *) &global->tl);
      if (global->words) VPrintf("%12ld", (LONG *) &global->tw);
      if (global->chars) VPrintf("%12ld", (LONG *) &global->tc);
      PutStr(" total\n");
    }
  }
  FreeVec(global);
 OutOfMemory:
  CloseLibrary((struct Library *) DOSBase);
  return rc;
}


LONG count(UBYTE *filename, Global *global)
{
  struct DosLibrary *DOSBase = global->DOSBase;
  UBYTE *bufp, *bufend;
  LONG  rlen;
  ULONG nc = 0L, nl = 0L, nw = 0L;
  BOOL  inword = FALSE;
  BPTR  fp;
  LONG  c;

  bufp = bufend = global->buffer;
  global->several++;
  if (!filename)
    fp = Input();
  else if (!(fp = Open(filename, MODE_OLDFILE)))
  {
    PutStr("Cannot open ");
    printerror(filename, global);
    return RETURN_WARN;
  }

  for (;;) {
    if (bufp >= bufend) {
      bufp = global->buffer;
      if ((rlen = Read(fp, bufp, BUFLEN)) <= 0)
        break; /* error or EOF */
      bufend = global->buffer + rlen;
      nc += rlen;

      if (CheckSignal(SIGBREAKF_CTRL_C)) {
        if (filename)
	  Close(fp);
        return ERROR_BREAK;
      }
    }

    c = *bufp++;
    if (c == '\n')
      nl++;
    if (c == ' ' || c == '\t' || c == '\n')
      inword = FALSE;
    else if (!inword)
    {
      inword = TRUE;
      nw++;
    }
  }

  global->tl += nl;
  global->tw += nw;
  global->tc += nc;

  if (global->lines) VPrintf("%12ld", (LONG *) &nl);
  if (global->words) VPrintf("%12ld", (LONG *) &nw);
  if (global->chars) VPrintf("%12ld", (LONG *) &nc);
  if (filename)
  {
    PutStr(" ");
    PutStr(filename);
    Close(fp);
  }
  PutStr("\n");
  return RETURN_OK;
}
