/*
 * SONIXPEEK.C             (C) Copyright Eddy Carroll, 1988
 *
 * Scans one or more Aegis Sonix files building a list of unique
 * instruments used within those files. At the end, a list of the
 * instruments found is printed (can be redirected to a file if
 * desired).
 *
 * Usage: SonixPeek {-i} {-n} {-ofile} {-xdirectory} filename
 *
 * If filename is a sonix file, then only that file is scanned. If
 * filename is a directory, then all the files in that directory which
 * contain Sonix files are scanned.
 * 
 * If the -i (interactive) flag is present, then after each file is
 * found, the user is asked whether it should be included or not when
 * building the list of instruments.
 *
 * If the -o flag is present, then the list of instruments is stored
 * in the specified file, rather than being displayed on the screen.
 *
 * If the -x flag is present, then the output is given in the form
 * of an executable file which consists of a line for each instrument
 * found of the form COPY <instrument> TO <directory>, where <directory>
 * is given immediately after the -x switch.
 *
 * If the -n flag is present, then the list of instruments found in each
 * file, when a directory is being searched, is not printed.
 *
 * Note: Compile with Aztec C V3.20 or later. Should also compile with
 *       Lattice C.
 */

#include <libraries/dos.h>

#define EOL      '\012'
#define EOF      '\037'
#define TAB      '\011'
#define CR       '\015'

#define MAXINS 1000   /* Only 200 instruments allowed */
#define MAXFILENUM 200 /* No more than 200 scores allowed */

/* IFF defines */

#define MakeID(a,b,c,d) (long)((long)(a)<<24 | (long)(b)<<16 | (c)<<8 | (d))
#define FORM MakeID('F','O','R','M')
#define SMUS MakeID('S','M','U','S')
#define NAME MakeID('N','A','M','E')
#define SNX1 MakeID('S','N','X','1')
#define INS1 MakeID('I','N','S','1')
#define TRAK MakeID('T','R','A','K')

#define roundup(a) ((a + 1L) & ~1L)


int interactive = 0  ;  /* TRUE if interactive selection of files			*/
int multi = 0;			/* TRUE if a directory specified instead of file 	*/
int supress = 0;		/* TRUE if listing of instruments is supressed		*/
char *dirname = NULL;	/* Directory to copy to for -x option				*/
char *rname = NULL;		/* Real filename of directory or single file		*/
char *suffix[] =
	{".ss", ".instr"};	/* For creating 'copy' batch file					*/

LONG Examine(), ExNext(), Read(), Write();
LONG Lock(), CurrentDir();

char *malloc();
int numins = 1;				/* Current number of instruments	*/
char *ins[MAXINS];			/* Room for 1000 instruments		*/
int numfiles = 0;			/* Current number of files 			*/
char *files[MAXFILENUM];	/* Room for 200 files				*/

struct FileInfoBlock *stdin, *stdout; /* File pointers */
struct FileInfoBlock *Open(), *Output(), *Input(), *infile, *outfile;
struct FileInfoBlock myfib;


/*
 * Output string to file
 *
 */

void fprint(f, s)
struct FileInfoBlock *f;
char *s;
{
	char *p;
	for (p = s; *p; p++)
		;
	Write(f, s, (long)(p - s));
}


/*
 * Output string to stdout
 *
 */

void print(s)
char *s;
{
	fprint(stdout,s);
}


/*
 * Abort program due to error
 *
 */

void abort(string,code)
char *string;
int code;
{
	print(string);
	if (outfile != stdout && outfile)
		Close(outfile);
	exit(code);
}

#define P print

/*
 * Prints help message to standard output
 *
 */
void help()
{
P("\nSonixPeek Instrument Lister (C) Eddy Carroll, March 1988\n\n");
P("Usage: SonixPeek {-i} {-n} {-ofile} {-xdirectory} filename\n\n");
P("Filename is the Aegis Sonix file for which the instruments are to\n");
P("be listed. If it is a directory, then all the Sonix files in\n");
P("that directory are checked, and a sorted list of the instruments\n");
P("contained in all of them is prepared. The flags operate as follows:\n\n");
P(" -i  For a directory search, get user to verify whether or not\n");
P("     to include each Sonix file (interactively).\n\n");
P(" -n  Don't list instruments as they are found (for directories only).\n\n");
P(" -o  Redirect sorted output to named file.\n\n");
P(" -x  Format output as an execute file which will copy all the\n");
P("     instruments found to the named directory\n\n");
}


/*** Start of actual program ***/

main(argc, argv)
int argc;
char *argv[];
{

	void addinstrument(), skip(), dumpins();
	char ch, charin(), ch1, ch2;
	int argc2, i;
	LONG mylock, oldlock;
	char *fname, nextfile[40], *p;

	stdin = Input();
	outfile = stdout = Output();
	infile = 0;

	/* Scan command line for possible switches */

	for (argc2 = 1; argc2 < argc && *argv[argc2] == '-'; argc2++) {
		switch (tolower(*(argv[argc2]+1))) {

		case 'i':	interactive = 1;
					break;

		case 'n':   supress = 1;
					break;

		case 'o':	if ((outfile = Open(argv[argc2]+2,MODE_NEWFILE)) == 0)
						abort("Can't open output file\n", 20);
					break;

		case 'x':	dirname = argv[argc2] + 2;
					break;

		default:	print("Unknown option ");
					print(argv[argc2]);
					print("ignored\n");
					break;
		}
	}

	/* If missing filename, or filename == '?', print instructions */

	if (argc2 != (argc-1) || *argv[argc2] == '?') {
		help();
		abort(NULL,0);
	}

	fname = argv[argc2];

	if ((mylock = Lock(fname,ACCESS_READ)) == 0L)
		abort("Can't open input file.\n",20);

	Examine(mylock,&myfib);
	rname = malloc(strlen(myfib.fib_FileName)+1);
	strcpy(rname,myfib.fib_FileName); /* Save filename for later */

	if (myfib.fib_DirEntryType > 0L) {
		multi = 1;
		oldlock = CurrentDir(mylock);
		while (ExNext(mylock, &myfib))
			if (myfib.fib_DirEntryType < 0L)
				scan(&(myfib.fib_FileName)); 
		oldlock = CurrentDir(oldlock);
	} else
		scan(fname);

	dumpins();

	if (outfile != stdout)
		Close(outfile);

	UnLock(mylock);
	exit(0);
}

/*
 * Scans filename for instruments. If found, they are added to the
 * list of instruments already in memory. Returns 0 for success, -1
 * if invalid file.
 *
 */

int scan(filename)
char *filename;
{
	LONG header[3];
	char instr[50];
	char response[50];

	if ((infile = Open(filename,MODE_OLDFILE)) == 0L) {
		print("Can't open file ");
		print(filename);
		print(" for input\n");
		return(-1);
	}

	if (Read(infile,header,8L) != 8L || header[0] != FORM) {
		Close(infile);
		return(-1);
	}

	if (Read(infile,header,4L) != 4L || header[0] != SMUS) {
		Close(infile);
		return(-1);
	}

	if (interactive) {
		print("Include file ");
		print(filename);
		print(" (CR = no)? ");
		Read(stdin, response, 50L);
		if (tolower(*response) != 'y')
			return(-1);
	} else if (multi && !supress) {
		print("\nFile ");
		print(filename);
		print(":\n");
	}

	files[numfiles] = malloc(strlen(filename) + 1);
	strcpy(files[numfiles++], filename);

	while (Read(infile,header,8L) == 8L) {
		if (header[0] != INS1) {
			skip(infile,header[1]);
		} else {
			skip(infile,4L); /* skip position of instrument parameter */
			(void)Read(infile, instr, roundup(header[1]) - 4L);
			instr[header[1] - 4L] = '\0';
			addinstrument(instr);
		}
	}
	Close(infile);
	return(0);
}

void skip(file,size)
struct FileInfoBlock *file;
LONG size;
{
	char s[256];
	LONG len = 1L;
	size = roundup(size);
	while (size > 256L && len) {
		len = Read(file, s, 256L);
		size -= 256L;
	}
	(void)Read(file, s, size);
}


/*
 * Adds instrument of length len into list of instruments, but only
 * if its not already present in the list.
 *
 */

void addinstrument(instr)
char *instr;
{
	int pos, i;
	if (pos = matchins(instr)) {
		for (i = numins++; i > pos; i--)
			ins[i] = ins[i-1];
		ins[pos] = malloc(strlen(instr)+1);
		strcpy(ins[pos], instr);
	}
	if (multi && !supress) {
		print("    ");
		print(instr);
		print("\n");
	}
}

/*
 * Compares string p to string s, ignoring case of alpha chars.
 * Returns -ve if p < s, 0 if p = s, +ve if p > s.
 *
 */

int mystrcmp(p,s)
char *p, *s;
{
	while (*p && *s && tolower(*p) == tolower(*s))
		p++, s++;
	return(tolower(*p) - tolower(*s));
}

/*
 * Searches instrument array for a match with given instrument. 
 * Returns 0 if found, else position in array to insert new element.
 *
 */

int matchins(instr)
char *instr;
{
	int i, z;
	char *p, *s;

	if (numins < 2)
		return (1); /* Must be first entry */

	for (i = 1; i < numins; i++) {
		if ((z = mystrcmp(instr, ins[i])) <= 0) {
			if (z)
				return(i); /* If less, insert here */
			else
				return(0); /* If equal, don't insert  */
		}
	}
	return (i); /* Must be at end of list, so return last item */
}

/*
 * Dumps instrument list to outfile
 *
 */

void dumpins()
{
	int tofile, i, j;

	if (numins <= 1) {
		print("No instruments found.\n");
		return;
	}

	if (numfiles == 0)
		return;

	fprint(outfile,";\n; Sorted list of instruments\n");
	fprint(outfile,   "; --------------------------\n;\n");
	if (numfiles == 1) {
		fprint(outfile, "; Taken from file ");
		fprint(outfile, files[0]);
		fprint(outfile, "\n");
	} else {
		fprint(outfile,"; Taken from these files:\n;\n");
		for (i = 0; i < numfiles; i++) {
			fprint(outfile, ";     ");
			fprint(outfile,files[i]);
			fprint(outfile,"\n");
		}
	}
	fprint(outfile,";\n");

	for (i = 1; i < numins; i++) {
		for (j = (dirname != 0); j >= 0; j--) {
			if (dirname) {
				fprint(outfile, "copy ");
			} else {
				fprint(outfile,"      ");
			}
			fprint(outfile,ins[i]);
			if (dirname) {
				fprint(outfile,suffix[j]);
				fprint(outfile, " to ");
				fprint(outfile, dirname);
			}
			fprint(outfile,"\n");
		}
	}
}
