/*  :ts=8 bk=0
 *
 * sift.c:	Takes any IFF file and tells you what's in it.
 *		Alpha Concept Prototype.
 *
 * Usage: sift -c		; For clipboard scanning
 *    or  sift <file>		; For DOS file scanning
 *
 * Reads the specified stream and prints and IFFCheck-like listing of the
 * contents of the IFF file, if any.  Stream is a DOS file for <file>
 * argument, or is the clipboard's primary clip for -c.
 * This program must be run from a CLI.
 *
 * Stuart Ferguson				8807.??
 * Leo L. Schwab (adjusted for new semantics)	8808.16
 * shf (adjusted some more, added clipboard)	8809.01
 * ewhac (more adjusting around)		8811.02
 * shf (adjusted for Dec 88 semantics)		8812.18
 * ewhac (severe commenting)			8902.23
 */
#include <exec/types.h>
#include <libraries/dosextens.h>
#include <iff/iffparse.h>

extern void	*OpenLibrary();
extern LONG	Open();

/*
 * English error messages for possible IFFERR_#? returns from various
 * IFF routines.  To get the index into this array, take your IFFERR code,
 * negate it, and subtract one.
 *  idx = -error - 1;
 */
char	*errormsgs[] = {
	"End of file (not an error).",
	"End of context (not an error).",
	"No lexical scope.",
	"Insufficient memory.",
	"Stream read error.",
	"Stream write error.",
	"Stream seek error.",
	"File is corrupt.",
	"IFF syntax error.",
	"Not an IFF file.",
	"Return to client.  You should never see this."
};

struct Library *IFFParseBase;


main (argc, argv)
int	argc;
char	**argv;
{
	struct IFF_File	*iff = NULL;
	long		error;
	short		cbio;

	/*
	 * Check to see if an filename argument was passed.
	 */
	if (argc < 2) {
		printf ("usage: %s [-c] <file>\n", argv[0]);
		goto die;
	}

	/*
	 * Check to see if were doing I/O to the Clipboard.
	 */
	cbio = (argv[1][0] == '-'  &&  argv[1][1] == 'c');

	if (!(IFFParseBase = OpenLibrary ("iffparse.library", 0L))) {
		puts ("Can't open iff parsing library.");
		goto die;
	}

	/*
	 * Allocate IFF_File structure.
	 */
	if (!(iff = AllocIFF())) {
		puts ("AllocIFF() failed.");
		goto die;
	}

	/*
	 * Internal support is provided for both AmigaDOS files, and the
	 * clipboard.device.  This bizarre 'if' statement performs the
	 * appropriate machinations for each case.
	 */
	if (cbio) {
		/*
		 * Set up IFF_File for Clipboard I/O.
		 */
		if (!(iff -> iff_Stream =
				(ULONG) OpenClipboard (PRIMARY_CLIP))) {
			puts ("Clipboard open failed.");
			goto die;
		}
		InitIFFasClip (iff);
		BeginCB_Read (iff);
	} else {
		/*
		 * Set up IFF_File for AmigaDOS I/O.
		 */
		if (!(iff -> iff_Stream = Open (argv[1], MODE_OLDFILE))) {
			puts ("File open failed.");
			goto die;
		}
		InitIFFasDOS (iff, IFFM_READ);
	}

	/*
	 * Start the IFF transaction.
	 */
	OpenIFF (iff);

	while (1) {
		/*
		 * The interesting bit.  IFFPARSE_RAWSTEP permits us to
		 * have precision monitoring of the parsing process, which
		 * is necessary if we wish to print the structure of an
		 * IFF file.  ParseIFF() with _RAWSTEP will return the
		 * following things for the following reasons:
		 *
		 * Return code:			Reason:
		 * 0				Entered new context.
		 * IFFERR_EOC			About to leave a context.
		 * IFFERR_EOF			Encountered end-of-file.
		 * <anything else>		A parsing error.
		 */
		error = ParseIFF (iff, IFFPARSE_RAWSTEP);

		/*
		 * Since we're only interested in when we enter a context,
		 * we "discard" end-of-context (_EOC) events.
		 */
		if (error == IFFERR_EOC)
			continue;
		else if (error)
			/*
			 * Leave the loop if there is any other error.
			 */
			break;

		/*
		 * Print out the current state of affairs.
		 */
		PrintTopChunk (iff);
	}

	/*
	 * If error was IFFERR_EOF, then the parser encountered the end of
	 * the file without problems.  Otherwise, we print a diagnostic.
	 */
	if (error == IFFERR_EOF)
		puts ("File scan complete.");
	else
		printf ("File scan aborted, error %ld: %s\n",
			error, errormsgs[-error - 1]);

die:
	if (iff) {
		/*
		 * Terminate the IFF transaction with the stream.  Free
		 * all associated structures.
		 */
		CloseIFF (iff);

		/*
		 * Close the stream itself.
		 */
		if (iff -> iff_Stream)
			if (cbio) {
				EndCB_Read (iff);
				CloseClipboard (iff -> iff_Stream);
			} else
				Close (iff -> iff_Stream);

		/*
		 * Free the IFF_File structure itself.
		 */
		FreeIFF (iff);
	}
	if (IFFParseBase)	CloseLibrary (IFFParseBase);
}


PrintTopChunk (iff)
struct IFF_File *iff;
{
	struct ContextNode	*top;
	short			i;
	char			idbuf[5];

	/*
	 * Get a pointer to the context node describing the current context.
	 */
	if (!(top = CurrentChunk (iff)))
		return;

	/*
	 * Print a series of dots equivalent to the current nesting depth of
	 * chunks processed so far.  This will cause nested chunks to be
	 * printed out indented.
	 */
	for (i = iff -> iff_Depth;  i--; )
		printf (". ");

	/*
	 * Print out the current chunk's ID and size.
	 */
	printf ("%s %ld ", IDtoStr (top -> cn_ID, idbuf), top -> cn_Size);

	/*
	 * Print the current chunk's type, with a newline.
	 */
	puts (IDtoStr (top -> cn_Type, idbuf));
}
