/* * SONIXPEEK.C by Eddy Carroll, April 1988 * ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Scans one or more Aegis Sonix music 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 {-h} {-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. * * The -h flag suppresses the printing of the header which is normally * present at the top of the list of instruments. * * 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 TO , where * 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. * * DISTRIBUTION * I retain copyright to the source and executable code for SonixPeek, but * it may be freely redistributed as long as no profit is made from it. * * Compiles under Lattice C V4.0 * * Note: Some of this code is messy. Have patience with it :-) * */ #include #include #include #include #include #include /* The following is a quick hack to avoid having to link with lc.lib */ #define strcat(p,q) (strcpy((p)+strlen(p),(q))) /* * New handler for Ctrl-C. Checks if CTRL-C received, and if it has, sets * the global CtrlC variable to true. */ int CtrlC; #define chkabort() (CtrlC |= ((SetSignal(0,0) & SIGBREAKF_CTRL_C))) #define tolower(c) ((c) >= 'A' && (c) <= 'Z' ? (c) + 'a' - 'A' : (c)) #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 */ #define MAXSTRING 256 /* Maximum length of a string */ #define INDENT (" ") /* Amount to indent instruments by */ /* IFF ID definitions */ #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; /* TRUE if interactive selection of files */ int multi; /* TRUE if a directory specified instead of file */ int supress; /* TRUE if listing of instruments is supressed */ int noheader; /* TRUE if no header ouput at start of instr. list */ char *dirname; /* Directory to copy to for -x option */ char *suffix[] = {".ss", ".instr"}; /* For creating 'copy' batch file */ LONG Examine(), ExNext(), Read(), Write(); BPTR Lock(), CurrentDir(); int numins; /* Current number of instruments */ int numfiles; /* Current number of files */ char *ins[MAXINS]; /* Room for 1000 instruments */ char *files[MAXFILENUM]; /* Room for 200 files */ BPTR stdin, stdout, stderr, infile, outfile; struct FileInfoBlock myfib; /* * The following lets us use our own version of malloc(). The only * additional requirements are that we open intuition.library first, * and call FreeRemember(&memkey,TRUE) just before exiting. */ struct Remember *memkey; /* Used for tracking memory */ #define malloc(n) (AllocRemember(&memkey,n,0L)) struct IntuitionBase *IntuitionBase; /* * Output string to file. Note that if CTRL-C has been typed, * output is suppressed. */ void fprint(f, s) BPTR f; char *s; { if (!CtrlC) { Write(f, s, strlen(s)); chkabort(); } } /* * Outputs 3 strings to file f (handy for printing "String" var "String") * The strings are concatenated first to ensure that they are printed * as a single unit, and won't be interrupted by ^C, or even just the * user pressing space, if the output is stdout or stderr. */ void fprint3(f,s1,s2,s3) BPTR f; char *s1,*s2,*s3; { static char buf[MAXSTRING * 3]; strcpy(buf,s1); strcat(buf,s2); strcat(buf,s3); fprint(f,buf); } /* * print() and print3() are similar to fprint() and fprint3(), but * they output directly to stderr. */ #define print(s) fprint(stderr,s) #define print3(s1,s2,s3) fprint3(stderr,s1,s2,s3) /* * Standard exit routine for program. Deallocates resources and exits. * If it spots CtrlC has been pressed, it prints an appropriate message. */ void abort(code) int code; /* Exit code */ { if (CtrlC) Write(stderr,"^C\n",3); /* print() won't work when CtrlC is true */ if (outfile != stdout && outfile != NULL) Close(outfile); if (stderr) Close(stderr); if (memkey) FreeRemember(&memkey,TRUE); if (IntuitionBase) CloseLibrary(IntuitionBase); exit(code); } /* * Prints help message to standard output */ void help() { fprint3(stderr,"\n\ SonixPeek Instrument Lister Copyright Eddy Carroll April 1988\n\ \n\ Usage: SonixPeek {-h} {-i} {-n} {-ofile} {-xdirectory} file ...\n\ \n\ File is the Aegis Sonix file for which the instruments are to\n","\ be listed. If it is a directory, then all the Sonix files in\n\ that directory are checked, and a sorted list of the instruments\n\ contained in all of them is prepared. The flags operate as follows:\n\ \n","\ -h Suppress output of header at start of instrument listing\n\ \n\ -i Ask user whether or not to include each score found\n\ \n\ -n Don't list instruments as they are found\n\ \n");fprint(stderr,"\ -o Redirect sorted output to named file\n\ \n\ -x Format output as an execute file which will copy all the\n\ instruments found to the named directory\n\ \n"); } /*** Start of actual program ***/ void main(argc, argv) int argc; char *argv[]; { void addinstrument(), skip(), dumpins(); char charin(); BPTR mylock, oldlock; char *fname; stdin = Input(); stderr = Open("*",MODE_NEWFILE); outfile = stdout = Output(); infile = 0; if ((IntuitionBase = OpenLibrary("intuition.library",0L)) == NULL) abort(99); /* Scan command line for possible switches */ for ( argv++; argc > 1 && argv[0][0] == '-'; argc--,argv++) { switch (tolower(argv[0][1])) { case 'h': noheader = 1; break; case 'i': interactive = 1; break; case 'n': supress = 1; break; case 'o': if ((outfile = Open(argv[0]+2,MODE_NEWFILE)) == 0) { print3("Can't open file ",argv[0]+2," for output.\n"); abort(20); } break; case 'x': dirname = argv[0] + 2; break; default: print3("Unknown option ", argv[0], " ignored.\n"); break; } } /* If missing filename, or filename == '?', print instructions */ if (argc == 1 || argv[0][0] == '?') { help(); abort(0); } if (argc > 2) /* More than one file specified */ multi = 1; /* so enable printing of instrument list for each file */ /* * Now scan each of the files or directories specified, reading * the instruments from each one, and adding them to the list. */ for ( ; argc > 1; argc--, argv++) { fname = argv[0]; if ((mylock = Lock(fname, ACCESS_READ)) == 0L) { print3("Can't open file ",fname," for input.\n"); abort(20); } Examine(mylock,&myfib); if (myfib.fib_DirEntryType > 0L) { multi = 1; oldlock = CurrentDir(mylock); while (chkabort(), ExNext(mylock, &myfib) && !CtrlC) if (myfib.fib_DirEntryType < 0L) scan(&(myfib.fib_FileName), fname); oldlock = CurrentDir(oldlock); } else scan(fname,NULL); } dumpins(); UnLock(mylock); abort(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. The path parameter, which may be NULL, is the * path to be prefixed to the filename before it is stored in memory. * Note that this may entail adding a trailing '/' to the path. */ int scan(filename,path) char *filename; char *path; { LONG header[3]; char instr[50], response[50], *p; int plen, foundinstr = 0; if ((infile = Open(filename,MODE_OLDFILE)) == 0L) { print3("Can't open file ", filename, " for input\n"); return(-1); } if (Read(infile,(char *)header,8L) != 8L || header[0] != FORM) { Close(infile); return(-1); } if (Read(infile,(char *)header,4L) != 4L || header[0] != SMUS) { Close(infile); return(-1); } if (interactive) { print3("Include file ", filename, " (CR = no)? "); Read(stdin, response, 50L); if (tolower(*response) != 'y') return(-1); } else if (multi && !supress) fprint3(stdout, filename, ":\n", ""); while (chkabort(), Read(infile,(char *)header,8L) == 8L && !CtrlC) { if (header[0] != INS1) { skip(infile,header[1]); } else { skip(infile,4L); /* skip position of instrument parameter */ Read(infile, instr, roundup(header[1]) - 4L); instr[header[1] - 4L] = '\0'; /* Null-terminate string */ addinstrument(instr); foundinstr = 1; } } if (multi && !supress) fprint(stdout,"\n"); if (foundinstr) { if (path) { plen = strlen(path); p = malloc(strlen(filename)+plen+2); /* Allocate extra byte for zero terminator and for possible '/' */ strcpy(p, path); if (plen && p[plen-1] != ':' && p[plen-1] != '/') strcat(p,"/"); strcat(p,filename); } else { p = malloc(strlen(filename)+1); strcpy(p,filename); } files[numfiles++] = p; } Close(infile); return(0); } void skip(file,size) BPTR file; LONG size; { char s[256]; LONG len = 1L; size = roundup(size); while (chkabort(), size > 0 && len && !CtrlC) { len = Read(file, s, (size > 256 ? 256 : size)); size -= 256; } } /* * 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)) != -1) { for (i = numins++; i > pos; i--) ins[i] = ins[i-1]; ins[pos] = malloc(strlen(instr)+1); strcpy(ins[pos], instr); } if (multi && !supress) { fprint3(stdout, INDENT, instr, "\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 -1 if found, else position in array to insert new element. */ int matchins(instr) char *instr; { int i, z; for (i = 0; i < numins; i++) { if ((z = mystrcmp(instr, ins[i])) <= 0) { if (z) return(i); /* If less, insert here */ else return(-1); /* 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 i, j; if (numins == 0) { print("No instruments found.\n"); return; } if (!noheader) { fprint(outfile,";\n; Sorted list of instruments\n"); fprint(outfile, "; --------------------------\n;\n"); if (numfiles == 1) fprint3(outfile, "; Taken from file ", files[0], "\n"); else { fprint(outfile,"; Taken from these files:\n;\n"); for (i = 0; i < numfiles && !CtrlC; i++) { fprint3(outfile, "; ", files[i], "\n"); } } fprint(outfile,";\n"); } if (dirname) fprint(outfile,"failat 30\n"); for (i = 0; i < numins && !CtrlC; i++) { for (j = (dirname != 0); j >= 0 && !CtrlC; j--) { if (dirname) { fprint3(outfile, "copy ", ins[i], " to "); fprint3(outfile, dirname, "\n", ""); } else fprint3(outfile, INDENT, ins[i], "\n"); } } }