/*
** wordcount.c
**
** Counts Characters, Words and Lines
**
** $Id: wordcount.c,v 1.4 94/08/09 17:13:25 GF Exp Locker: GF $
**
**************************************************************************
**
** Copyright © 1994 by Gabriele Falcioni. All Rights Reserved.
**
** 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.
**
** For inquiries, comments, donations, bug reports, write to:
**
** Gabriele Falcioni
** via E. Cialdini, 50
** I-60122 Ancona
** ITALY
**
**************************************************************************
**
** WordCount: counts characters, words and lines
**
** REQUIREMENTS
**
** · "exec.library"		V37+
** · "dos.library"		V37+
** · "utility.library"	V37+
**
** · SAS DevSystem V6.x
** · Startup: "shellcres2.o"
** · Library: "filefinder.lib"
*/

#define _CMDNAME	"WordCount"
#define _VER_REV	"1.4"
#define _CREATION	"(21.05.94)"
#define _TEMPLATE "FILE/A/M,ALL/S,BUFSIZE/K/N"

/*
 * COMMAND
 * WordCount -- Count characters, words and lines.
 *
 * TEMPLATE
 * FILE/A/M,ALL/S,BUFSIZE/K/N
 *
 * DESCRIPTION
 * It counts the number of characters, words and lines contained in
 * a plain text file.
 *
 * ARGUMENTS
 * FILE/A/M    - file(s) to count. Standard AmigaDOS patterns allowed.
 * ALL/S       - search entire directories.
 * BUFSIZE/K/N - size (in bytes) of input buffer. Default is 2048 bytes.
 */

/* -------------------------------------------------------------------- */

#include <string.h>

#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/stdio.h>
#include <dos/dosasl.h>
#include <dos/rdargs.h>

#include <proto/dos.h>
#include <proto/exec.h>

#include <usrh/filefinder.h>

/* --- My favourite macros -------------------------------------------- */

#define STDMEM (MEMF_PUBLIC|MEMF_CLEAR)

#define ConStruct(type)		((type *)AllocMem(sizeof(type),STDMEM))
#define DiStruct(ptr)		FreeMem((ptr),sizeof(*(ptr)))
#define ClearStruct(ptr)	memset((ptr),0,sizeof(*(ptr)))

#ifndef FOREVER
#define FOREVER for (;;)
#endif

/* -------------------------------------------------------------------- */

/* this structure is used by CountWords() function */
struct WordCount {

	LONG nchars;
	LONG nwords;
	LONG nlines;

};

/* This is the default I/O buffers length */
#define BUFLEN 2048

/* a *slightly* nonstandard version string */
static UBYTE const EmbeddedVersionID[] = {
	"$VER: " _CMDNAME " " _VER_REV " " _CREATION
	" Copyright © 1994 by Gabriele Falcioni. FREE Software."
};

/* required by FileFinder functions */
struct Library *UtilityBase;

struct RDArgs *MyRdargs;
struct {
	UBYTE **file;
	ULONG all;
	ULONG *bufsize;
} MyArgs;

struct WordCount MyCount,TotalCount;

/* -------------------------------------------------------------------- */

LONG CountWords(BPTR file,UBYTE *iobufptr,LONG iobuflen,
	register struct WordCount *wc)
{
	LONG errcode;

	register UBYTE *scanner;
	register LONG   scanlen;
	register BOOL   new_word;

	ClearStruct(wc);
	new_word = TRUE;
	errcode = 0;

	FOREVER {

		scanner = iobufptr;

		if ((scanlen = Read(file,scanner,iobuflen)) <= 0) {
			if (scanlen < 0) errcode = IoErr();
			break;
		}

		if (CheckSignal(SIGBREAKF_CTRL_C)) {
			errcode = ERROR_BREAK;
			break;
		}

		do {

			switch (*scanner++) {

				case ' ':
				case '\t':
					new_word = TRUE;
					break;

				case '\n':
					new_word = TRUE;
					++wc->nlines;
					break;

				default:
					if (new_word) {
						new_word = FALSE;
						++wc->nwords;
					}
					break;
			}

			++wc->nchars;

		} while (--scanlen);

	}	/* end FOREVER loop */

	return errcode;
}

/* -------------------------------------------------------------------- */

/*
 * this is from file: "shellcres2.a" (cres-like custom startup module)
 *
 * Features:
 * · Smaller than standard one
 * · Makes C programs resident-able
 * · Permits Shell execution only
 * · Initializes SysBase and DOSBase
 * · Requires V37+ ROMs
 * (of course practical *only* for really small Shell utilities)
 *
 */

void late_exit(long);

/*
 * custom exit function: void early_exit(long exitcode)
 * this function calls the "shellcres2.o" exit code
 */

void early_exit(long code)
{
	if (UtilityBase) CloseLibrary(UtilityBase);

	late_exit(code);
}

/*
 * custom main function: void early_main(void)
 * called by "shellcres2.o" (custom startup module, see above)
 */

void __stdargs early_main(void)
{
	LONG   errcode;
	UBYTE *errstr;

	UBYTE *iobufptr;
	LONG   iobuflen;

	struct FileFinder *ff;
	LONG nfiles;

	if (!(UtilityBase = OpenLibrary("utility.library",37))) {
		Printf("Couldn't open \"%s\" V37+\n","utility.library");
		SetIoErr(ERROR_INVALID_RESIDENT_LIBRARY);
		early_exit(RETURN_FAIL);
	}

	if (MyRdargs = ReadArgs(_TEMPLATE,(LONG *)&MyArgs,NULL)) {

		iobuflen = (MyArgs.bufsize ? *MyArgs.bufsize : BUFLEN);
		if (iobufptr = AllocMem(iobuflen,MEMF_PUBLIC)) {

			if (ff = CreateFileFinder(
					FILEFT_Argv,	(LONG)MyArgs.file,
					FILEFT_All,		(LONG)MyArgs.all,
					TAG_END)) {

				nfiles = 0;

				errcode = 0;
				errstr  = NULL;

				while (FindFiles(ff)) {
					BPTR fh;

					if (!(fh = OpenFoundFile(ff,MODE_OLDFILE))) {
						errcode = IoErr();
						errstr  = "Can't open file";
						break;	/* exit loop */
					}

					errcode = CountWords(fh,iobufptr,iobuflen,&MyCount);
					Close(fh);

					if (errcode) break;	/* exit loop */

					Printf("%-25s %7ld chars,%7ld words,%7ld lines.\n",
						ff->ff_AP.ap_Info.fib_FileName,
						MyCount.nchars,
						MyCount.nwords,
						MyCount.nlines);

					TotalCount.nchars += MyCount.nchars;
					TotalCount.nwords += MyCount.nwords;
					TotalCount.nlines += MyCount.nlines;

					++nfiles;
				}

				if (!errcode && nfiles > 1)
					Printf("%-25s %7ld chars,%7ld words,%7ld lines.\n",
						"TOTAL",
						TotalCount.nchars,
						TotalCount.nwords,
						TotalCount.nlines);

				DeleteFileFinder(ff);
			} else {
				errcode = ERROR_NO_FREE_STORE;
				errstr  = "Couldn't allocate matcher buffer";
			}

			FreeMem(iobufptr,iobuflen);
		} else {
			errcode = ERROR_NO_FREE_STORE;
			errstr  = "Couldn't allocate input buffer";
		}

		FreeArgs(MyRdargs);
	} else {
		errcode = IoErr();
		errstr  = "Bad Args";
	}

	if (errcode) {
		PrintFault(errcode,errstr);
		early_exit(RETURN_FAIL);
	}

	early_exit(RETURN_OK);
}
