amiga-request@ab20.larc.nasa.gov (Amiga Sources/Binaries Moderator) (03/16/91)
Submitted-by: barrett@server.cs.jhu.edu Posting-number: Volume 91, Issue 064 Archive-name: midi/mp-1.0/part01 [ includes uuencoded executable ...tad ] This is a submission for comp.sources.amiga, entitled "MIDI Playground", written by me. It's a versatile utility for making your Amiga communicate with a MIDI instrument. It's helpful for learning about MIDI, designing MIDI software, and other related tasks. Uuencoded binary is included. Dan #!/bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 1 (of 2)." # Contents: CONTENTS Examples Examples.DOC Examples/CHORD-OFF # Examples/CHORD-ON Examples/DISPLAY-FUN README Scripts # Scripts/Converse Scripts/PatchChange Scripts/Text2Midi # Scripts/TwoWindows Source Source/Makefile Source/amigados.c # Source/cli.c Source/files.c Source/getopt.c Source/help.c # Source/iofunctions.c Source/main.c Source/midi.h Source/mp.h # Source/serial.c Source/text.c Source/version.h Source/wb.c # Wrapped by tadguy@ab20 on Fri Mar 15 14:25:13 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'CONTENTS' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'CONTENTS'\" else echo shar: Extracting \"'CONTENTS'\" \(353 characters\) sed "s/^X//" >'CONTENTS' <<'END_OF_FILE' XMIDI_Playground A small, useful utility for sending any MIDI data back and X forth between an Amiga and a MIDI instrument. Helpful for X learning about MIDI, writing/debugging MIDI software, X figuring out your instrument's system-exclusive X implementation, and more. Very versatile. Version 1.0, X includes source. X Author: Daniel J. Barrett X END_OF_FILE if test 353 -ne `wc -c <'CONTENTS'`; then echo shar: \"'CONTENTS'\" unpacked with wrong size! fi # end of 'CONTENTS' fi if test ! -d 'Examples' ; then echo shar: Creating directory \"'Examples'\" mkdir 'Examples' fi if test -f 'Examples.DOC' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Examples.DOC'\" else echo shar: Extracting \"'Examples.DOC'\" \(907 characters\) sed "s/^X//" >'Examples.DOC' <<'END_OF_FILE' XRead MP.DOC before you read this file. X XMP is very flexible because: X X o It can translate between 3 kinds of data formats: X X - Text. X - Binary data. X - MIDI data. X X o The input/output can be sent to/from: X X - Your CLI window. X - Files, using -g and -p. X - Files, using the CLI redirection symbols "<" and X ">". X - Newly-created windows, by specifying a file X name like "CON:0/0/640/100/MP Input". X - The MIDI port. X XMP does not have a window/mouse/icon graphic interface; instead, YOU Xdesign the interface using CLI scripts. See the "Scripts" directory Xfor some small examples. You can use MP in AREXX scripts too. X XFor a Workbench interface, use the IconX program (supplied with your XAmiga) to attach an icon to a CLI script. Then fill the CLI script Xwith MP commands. X XThe directory "Examples" has some sample files that use the input Xtext language. You can send them to your synthesizer. END_OF_FILE if test 907 -ne `wc -c <'Examples.DOC'`; then echo shar: \"'Examples.DOC'\" unpacked with wrong size! fi # end of 'Examples.DOC' fi if test -f 'Examples/CHORD-OFF' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Examples/CHORD-OFF'\" else echo shar: Extracting \"'Examples/CHORD-OFF'\" \(72 characters\) sed "s/^X//" >'Examples/CHORD-OFF' <<'END_OF_FILE' X; Cease playing a C major triad on MIDI channel 0. X X0x90 60 0 64 0 67 0 END_OF_FILE if test 72 -ne `wc -c <'Examples/CHORD-OFF'`; then echo shar: \"'Examples/CHORD-OFF'\" unpacked with wrong size! fi # end of 'Examples/CHORD-OFF' fi if test -f 'Examples/CHORD-ON' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Examples/CHORD-ON'\" else echo shar: Extracting \"'Examples/CHORD-ON'\" \(69 characters\) sed "s/^X//" >'Examples/CHORD-ON' <<'END_OF_FILE' X; Play a C major triad on MIDI channel 0. X X0x90 60 127 64 127 67 127 END_OF_FILE if test 69 -ne `wc -c <'Examples/CHORD-ON'`; then echo shar: \"'Examples/CHORD-ON'\" unpacked with wrong size! fi # end of 'Examples/CHORD-ON' fi if test -f 'Examples/DISPLAY-FUN' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Examples/DISPLAY-FUN'\" else echo shar: Extracting \"'Examples/DISPLAY-FUN'\" \(340 characters\) sed "s/^X//" >'Examples/DISPLAY-FUN' <<'END_OF_FILE' X; An example of sending text to the Matrix-12 display. X; Xpander users must change the 4th byte to 0x05. X; You MUST send exactly 80 characters or the Oberheim ignores you. X; Send this with: X; mp -g DISPLAY-FUN -it -om X X0xf0 0x10 0x02 0x06 0x01 X"I THINK THAT I SHALL NEVER SEE " X"A POEM AS LOVELY AS LITTLE OL' ME " X0xf7 END_OF_FILE if test 340 -ne `wc -c <'Examples/DISPLAY-FUN'`; then echo shar: \"'Examples/DISPLAY-FUN'\" unpacked with wrong size! fi # end of 'Examples/DISPLAY-FUN' fi if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(461 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' XMP: A MIDI Playground XAuthor: Daniel J. Barrett, barrett@cs.jhu.edu. XNo Copyright: 100% Public Domain. X Please share this program with others. X X MP is a small program that lets your Amiga communicate with a XMIDI instrument. It runs from the CLI only (but can be made to run Xfrom the Workbench by using "IconX"). X XSee the file MP.DOC for full instructions on how to use this program. XSee EXAMPLES.DOC for information about the supplied examples and scripts. END_OF_FILE if test 461 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test ! -d 'Scripts' ; then echo shar: Creating directory \"'Scripts'\" mkdir 'Scripts' fi if test -f 'Scripts/Converse' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Scripts/Converse'\" else echo shar: Extracting \"'Scripts/Converse'\" \(151 characters\) sed "s/^X//" >'Scripts/Converse' <<'END_OF_FILE' Xrun <NIL: >NIL: mp -it -om -g "CON:0/1/640/75/Send MIDI; end with ^\" Xwait 1 Xrun <NIL: >NIL: mp -im -ot -p "CON:0/76/640/75/Receive MIDI; end with ^C" END_OF_FILE if test 151 -ne `wc -c <'Scripts/Converse'`; then echo shar: \"'Scripts/Converse'\" unpacked with wrong size! fi # end of 'Scripts/Converse' fi if test -f 'Scripts/PatchChange' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Scripts/PatchChange'\" else echo shar: Extracting \"'Scripts/PatchChange'\" \(172 characters\) sed "s/^X//" >'Scripts/PatchChange' <<'END_OF_FILE' X.KEY patchnum/a X.BRA { X.KET } X Xecho "Changing to patch {patchnum} on MIDI channel 0..." Xecho > pipe:patchchange "0xC0 {patchnum}" Xmp -it -om -g pipe:patchchange Xecho Done! END_OF_FILE if test 172 -ne `wc -c <'Scripts/PatchChange'`; then echo shar: \"'Scripts/PatchChange'\" unpacked with wrong size! fi # end of 'Scripts/PatchChange' fi if test -f 'Scripts/Text2Midi' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Scripts/Text2Midi'\" else echo shar: Extracting \"'Scripts/Text2Midi'\" \(137 characters\) sed "s/^X//" >'Scripts/Text2Midi' <<'END_OF_FILE' X.KEY TextFile/a X.BRA { X.KET } X Xecho "Converting {TextFile} to {TextFile}.midi..." Xmp -it -ob -g {TextFile} -p {TextFile}.midi Xecho Done! END_OF_FILE if test 137 -ne `wc -c <'Scripts/Text2Midi'`; then echo shar: \"'Scripts/Text2Midi'\" unpacked with wrong size! fi # end of 'Scripts/Text2Midi' fi if test -f 'Scripts/TwoWindows' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Scripts/TwoWindows'\" else echo shar: Extracting \"'Scripts/TwoWindows'\" \(107 characters\) sed "s/^X//" >'Scripts/TwoWindows' <<'END_OF_FILE' Xmp -it -ot -g "CON:0/1/640/75/Type commands Here..." -p "CON:0/76/640/75/...and watch them come out here!" END_OF_FILE if test 107 -ne `wc -c <'Scripts/TwoWindows'`; then echo shar: \"'Scripts/TwoWindows'\" unpacked with wrong size! fi # end of 'Scripts/TwoWindows' fi if test ! -d 'Source' ; then echo shar: Creating directory \"'Source'\" mkdir 'Source' fi if test -f 'Source/Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Source/Makefile'\" else echo shar: Extracting \"'Source/Makefile'\" \(2707 characters\) sed "s/^X//" >'Source/Makefile' <<'END_OF_FILE' X########################################################################### X# Makefile: Makefile for Manx Aztec C, version 5.0. X# Part of MP, the MIDI Playground. X# X# Author: Daniel Barrett X# Version: See the file "version.h". X# Copyright: None! This program is in the Public Domain. X# Please share it with others. X########################################################################### X X XOPTIMIZE = -so XDEBUGGING = #-bs XLDEBUGGING = #-g X XLFLAGS = $(LDEBUGGING) +Q XCFLAGS = $(OPTIMIZE) -hi $(COMP_INC) $(DEBUGGING) XLIBS = -lc XCOMP_INC = headers.comp XLIBS = -lc X XBACKUPDIR = df1:sysex X X############################################################################## X# Files used for all programs. X############################################################################## X XPROG = mp XMAINHEADER = mp.h XHEADERS = $(MAINHEADER) version.h midi.h XSRC = text.c main.c serial.c getopt.c iofunctions.c help.c \ X files.c wb.c cli.c amigados.c XOBJ = text.o main.o serial.o getopt.o iofunctions.o help.o \ X files.o wb.o cli.o amigados.o X X############################################################################## X# The program. X############################################################################## X X$(PROG): $(COMP_INC) $(OBJ) X @echo "" X @echo "Ignore messages about _abort overriding library." X @echo "" X ln $(LFLAGS) $(OBJ) -o $(PROG) $(LIBS) X X$(OBJ): $(MAINHEADER) X X############################################################################## X# Individual dependencies. X############################################################################## X Xhelp.o: help.c $(MAINHEADER) version.h Xiofunctions.o: iofunctions.c $(MAINHEADER) midi.h Xserial.o: serial.c $(MAINHEADER) midi.h X X############################################################################## X# Compiled headers. X############################################################################## X X$(COMP_INC): $(MAINHEADER) X @echo "Compiling headers because of $?" X cc $(OPTIMIZE) $(DEBUGGING) -ho $(COMP_INC) $(MAINHEADER) X X#$(MAINHEADER): $(HEADERS) X X############################################################################## X# Clean up X############################################################################## X Xclean: X delete \#?.o \#?.pro X Xveryclean: clean X delete $(COMP_INC) $(PROG) \#?.dbg X Xcbackup: X copy \#?.c $(BACKUPDIR) X copy \#?.h $(BACKUPDIR) X copy Makefile $(BACKUPDIR) X Xbackup: cbackup X copy README $(BACKUPDIR) X copy \#?.DOC $(BACKUPDIR) X copy Scripts $(BACKUPDIR)/Scripts all X copy Examples $(BACKUPDIR)/Examples all X Xzoo: $(PROG) X delete ram:mp.zoo quiet X zoo a ram:mp.zoo Source/* Scripts/* Examples/* \ X $(PROG) *.DOC README Makefile END_OF_FILE if test 2707 -ne `wc -c <'Source/Makefile'`; then echo shar: \"'Source/Makefile'\" unpacked with wrong size! fi # end of 'Source/Makefile' fi if test -f 'Source/amigados.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Source/amigados.c'\" else echo shar: Extracting \"'Source/amigados.c'\" \(1684 characters\) sed "s/^X//" >'Source/amigados.c' <<'END_OF_FILE' X/************************************************************************** X* amigados.c: Requestor and Environment Variable routines. X* Part of MP, the MIDI Playground. X* X* Author: Daniel Barrett X* Version: See the file "version.h". X* Copyright: None! This program is in the Public Domain. X* Please share it with others. X***************************************************************************/ X X X#include <exec/types.h> X#include <stdlib.h> X#include <functions.h> X#include <libraries/dos.h> X#include <libraries/dosextens.h> X X Xvoid DisableRequestors(void); Xvoid EnableRequestors(void); X X X#define ENV_NAME_LENGTH BUFSIZ X X X/* Return the value of ENV: environment variable "variableName", if it X * exists. We use this instead of the built-in getenv() because we X * want to turn off requestors during the search for ENV:, in case it X * is not mounted. */ X Xchar *GetEnv(char *variableName) X{ X char *result; X X DisableRequestors(); /* In case ENV: is non-existent. */ X result = getenv(variableName); X EnableRequestors(); X return(result); X} X X X/*************************************************************************** X* Deal with requestors. X***************************************************************************/ X Xstatic APTR oldWindowPtr; Xstatic struct Process *theProc; X X/* Turn off system requestors for this process. */ X Xvoid DisableRequestors(void) X{ X theProc = (struct Process *)FindTask(NULL); X oldWindowPtr = theProc->pr_WindowPtr; X theProc->pr_WindowPtr = (APTR)(-1L); X} X X X/* Turn on system requestors for this process, after they have been X * turned off by DisableRequestors(), above. */ X Xvoid EnableRequestors(void) X{ X theProc->pr_WindowPtr = oldWindowPtr; X} END_OF_FILE if test 1684 -ne `wc -c <'Source/amigados.c'`; then echo shar: \"'Source/amigados.c'\" unpacked with wrong size! fi # end of 'Source/amigados.c' fi if test -f 'Source/cli.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Source/cli.c'\" else echo shar: Extracting \"'Source/cli.c'\" \(2379 characters\) sed "s/^X//" >'Source/cli.c' <<'END_OF_FILE' X/************************************************************************** X* cli.c: The command-line interface for MP. X* Part of MP, the MIDI Playground. X* X* Author: Daniel Barrett X* Version: See the file "version.h". X* Copyright: None! This program is in the Public Domain. X* Please share it with others. X***************************************************************************/ X X X#include "mp.h" X X X/* The main program from the CLI. Check the command-line arguments, set X * up the input and output files, and call MIDIPlayground(). */ X XBOOL CommandLineProgram(int argc, char *argv[]) X{ X FILE *in, *out; X char *infile, *outfile; X FLAGS theFlags[NUM_FLAGS]; X X InitStuff(theFlags, &in, &out, &infile, &outfile); X X if ((argc == 1) || (argc == 2 && !strcmp(argv[1], "?"))) X Help(argv[0]); X else if (!HandleOptions(argc, argv, theFlags, &infile, &outfile)) X { X fprintf(stderr, "There is an error in your options/flags.\n"); X BegForUsage(argv[0]); X } X else if (!SetupFiles(infile, outfile, &in, &out)) X ; X else if (optind < argc) X { X fprintf(stderr, "You gave me too many arguments.\n"); X BegForUsage(argv[0]); X } X else X (void)MIDIPlayground(theFlags, in, out); X X CloseFiles(in, out, infile, outfile); X} X X X/************************************************************************ X* Handling command-line options. X************************************************************************/ X XBOOL HandleOptions(int argc, char *argv[], FLAGS theFlags[], char **infile, X char **outfile) X{ X int c; X BOOL success = TRUE; X static char options[] = GETOPT_OPTIONS; X X while ((c = getopt(argc, argv, options)) != EOF) X { X switch (c) X { X case OPT_INPUT: X success &= !theFlags[FLAG_ITYPE]; X success &= CheckIOType(*optarg, FLAG_ITYPE, theFlags); X break; X case OPT_OUTPUT: X success &= !theFlags[FLAG_OTYPE]; X success &= CheckIOType(*optarg, FLAG_OTYPE, theFlags); X break; X case OPT_INFILE: X success &= (optarg[0] != '\0'); X success &= MakeFilename(infile, optarg); X break; X case OPT_OUTFILE: X success &= (optarg[0] != '\0'); X success &= MakeFilename(outfile, optarg); X break; X case OPT_SYSEX: X success &= !theFlags[FLAG_SYSEX]; X theFlags[FLAG_SYSEX]++; X break; X default: X success = FALSE; X break; X } X } X return(success && CheckFlags(theFlags)); X} X X END_OF_FILE if test 2379 -ne `wc -c <'Source/cli.c'`; then echo shar: \"'Source/cli.c'\" unpacked with wrong size! fi # end of 'Source/cli.c' fi if test -f 'Source/files.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Source/files.c'\" else echo shar: Extracting \"'Source/files.c'\" \(3363 characters\) sed "s/^X//" >'Source/files.c' <<'END_OF_FILE' X/************************************************************************** X* files.c: File open/close, input/output, etc. X* Part of MP, the MIDI Playground. X* X* Author: Daniel Barrett X* Version: See the file "version.h". X* Copyright: None! This program is in the Public Domain. X* Please share it with others. X***************************************************************************/ X X X#include <stdio.h> X#include <exec/types.h> X#include <libraries/dos.h> X#include <libraries/dosextens.h> X X X/* Open a file and return a pointer to it. */ X XFILE *OpenAFile(char *filename, char *mode, char *readOrWrite) X{ X FILE *fp; X X if ((fp = fopen(filename, mode)) == NULL) X fprintf(stderr, "Cannot %s the file \"%s\",\n", X readOrWrite, filename); X return(fp); X} X X X/* Open a file for reading. We might also have a macro with the same name, X * so we used ifdef's. */ X X#ifndef OpenReadFile XFILE *OpenReadFile(char *filename) X{ X return(OpenAFile(filename, "r", "read")); X} X#endif X X X/* Open a file for writing. We might also have a macro with the same name, X * so we used ifdef's. */ X X#ifndef OpenWriteFile XFILE *OpenWriteFile(char *filename) X{ X return(OpenAFile(filename, "w", "write")); X} X#endif X X X/* Tell me whether or not a file exists. */ X XBOOL FileExists(char *filename) X{ X BPTR lock; X X if ((lock = Lock(filename, ACCESS_READ)) != NULL) X { X UnLock(lock); X return(TRUE); X } X return(FALSE); X} X X X/* See if we want to overwrite an existing file. */ X X/* BUFSIZ is too large, perhaps -- change later -- but don't compromise X * on correct reading of the user's input. Too small, and extra unread X * chars are read on the NEXT call (yuck). If you use gets(), too much X * input crashes the program. */ X XBOOL DontOverwriteExistingFile(char *filename) X{ X char buf[BUFSIZ]; X BOOL safety = TRUE; X X if (FileExists(filename)) X { X fprintf(stderr, X "File \"%s\" already exists.\nOVERWRITE it? [%s]: ", X filename, "Yes/No, RETURN=No"); X fflush(stderr); X X if (fgets(buf, BUFSIZ-1, stdin)) X safety = (toupper(buf[0]) != 'Y'); X else X safety = TRUE; X } X else X safety = FALSE; /* File doesn't exist. */ X X if (safety) X fprintf(stderr, "%s not overwritten.\n\n", filename); X X return(safety); X} X X X/* Open the input and output files. X * We open input AFTER output in case the user has specified CON: windows, X * so the input window is the active one. */ X XBOOL SetupFiles(char *infile, char *outfile, FILE **in, FILE **out) X{ X if (!outfile || (outfile[0] == '\0')) X *out = stdout; X else if (DontOverwriteExistingFile(outfile)) X return(FALSE); X else if ((*out = OpenWriteFile(outfile)) == NULL) X return(FALSE); X X if (!infile || (infile[0] == '\0')) X *in = stdin; X else if ((*in = OpenReadFile(infile)) == NULL) X return(FALSE); X X return(TRUE); X} X X X/* Close the files used by the program, and deallocate the arrays used X * for storing the filenames. */ X Xvoid CloseFiles(FILE *in, FILE *out, char *filein, char *fileout) X{ X if (in && (in != stdin)) fclose(in); X if (out && (out != stdout)) fclose(out); X if (filein) free(filein); X if (fileout) free(fileout); X} X X X/* Allocate space to store a filename, and copy the contents of "src" into X * it. */ X XBOOL MakeFilename(char **dest, char *src) X{ X if ((!src) || (*src == '\0') X || ((*dest = (char *)malloc(strlen(src)+1)) == NULL)) X return(FALSE); X else X { X strcpy(*dest, src); X return(TRUE); X } X} END_OF_FILE if test 3363 -ne `wc -c <'Source/files.c'`; then echo shar: \"'Source/files.c'\" unpacked with wrong size! fi # end of 'Source/files.c' fi if test -f 'Source/getopt.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Source/getopt.c'\" else echo shar: Extracting \"'Source/getopt.c'\" \(7295 characters\) sed "s/^X//" >'Source/getopt.c' <<'END_OF_FILE' X/**************************************************************************** X * getopt(): Return the next user option on each iteration. X * This is a clone of the usual UNIX getopt() function. X * If you have never used a getopt() before, you'll have to X * read about it on any UNIX machine or other C system that X * documents it. X * X * Author: Daniel Barrett, barrett@cs.jhu.edu. X * Date: February 20, 1991. X * Version: 1.1. X * X * License: This code is placed in the Public Domain. X * Give it away to anybody for free! X * Use it for any purpose you like! X * X * If you use this code in a program, please give me credit X * for my work. Thanks! X * X * Why I wrote it: X * X * Because every other getopt() function I have ever seen X * had source code that was difficult to understand. X * I wrote this code to be very modular and readable. X * I hope you find it instructive and/or helpful. X * X * REVISION HISTORY: X * Version: 1.1 X * Date: February 20, 1991. X * Comments: Bug fix in Pass(). Forgot to check that the X * current argument is non-empty and starts with X * a DASH. X * X * Got rid of the unnecessary "g_" at the beginning X * of each function name. Since they're static, we X * don't have to worry about duplication of names X * by the calling program. X * X * Version: 1.0 X * Date: April 12, 1990. X * Comments: First released version. X * X ****************************************************************************/ X X#ifndef __STDIO_H /* If we haven't already included stdio.h, do it. */ X#include <stdio.h> /* Maybe someday I'll eliminate this. */ X#endif X X/************************************************************************ X* Some constants. X************************************************************************/ X X#define DASH '-' /* This preceeds an option. */ X#define ARG_COMING ':' /* In the option string, this indicates that X * that the option requires an argument. */ X#define UNKNOWN_OPT '?' /* The char returned for unknown option. */ X X/************************************************************************ X* Internal error codes. X************************************************************************/ X X#define ERROR_BAD_OPTION 1 X#define ERROR_MISSING_ARGUMENT 2 X X/************************************************************************ X* Mnemonic macros. X************************************************************************/ X X X X/************************************************************************ X* ANSI function prototypes. X************************************************************************/ X Xint getopt(int argc, char *argv[], char *optString); Xstatic int NextOption(char *argv[], char *optString); Xstatic int RealOption(char *argv[], char *str, int *skip, int *ind, X int opt); Xstatic void HandleArgument(char *argv[], int *optind, int *skip); Xstatic void Error(int err, int c); Xstatic void Pass(char *argv[], int *optind, int *optsSkipped); X Xextern char *index(); X X/************************************************************************ X* Global variables. You must declare these externs in your program X* if you want to see their values! X************************************************************************/ X Xchar *optarg = NULL; /* This will point to a required argument, if any. */ Xint optind = 1; /* The index of the next argument in argv. */ Xint opterr = 1; /* 1 == print internal error messages. 0 else. */ Xint optopt; /* The actual option letter that was found. */ X X Xint getopt(int argc, char *argv[], char *optString) X{ X optarg = NULL; X if (optind < argc) /* More arguments to check. */ X return(NextOption(argv, optString)); X else /* We're done. */ X return(EOF); X} X X X/* If the current argument does not begin with DASH, it is not an option. X * Return EOF. X * If we have ONLY a DASH, and nothing after it... return EOF as well. X * If we have a DASH followed immediately by another DASH, this is the X * special "--" option that means "no more options follow." Return EOF. X * Otherwise, we have an actual option or list of options. Process it. */ X Xstatic int NextOption(char *argv[], char *optString) X{ X static int optsSkipped = 0; /* In a single argv[i]. */ X X if ((argv[optind][0] == DASH) X && ((optopt = argv[optind][1+optsSkipped]) != '\0')) X { X if (optopt == DASH) X { X optind++; X return(EOF); X } X else X return(RealOption(argv, optString, &optsSkipped, X &optind, optopt)); X } X else X return(EOF); X} X X X/* At this point, we know that argv[optind] is an option or list of X * options. If this is a list of options, *optsSkipped tells us how X * many of those options have ALREADY been parsed on previous calls X * to getopt(). X * If the option is not legal (not in optString), complain and return X * UNKNOWN_OPT. X * If the option requires no argument, just return the option letter. X * If the option requires an argument, call HandleArgument and return X * the option letter. */ X Xstatic int RealOption(char *argv[], char *optString, int *optsSkipped, X int *optind, int optopt) X{ X char *where; X X (*optsSkipped)++; X if (where = index(optString, optopt)) X { X if (*(where+1) == ARG_COMING) X HandleArgument(argv, optind, optsSkipped); X X Pass(argv, optind, optsSkipped); X return(optopt); X } X else X { X Error(ERROR_BAD_OPTION, optopt); X Pass(argv, optind, optsSkipped); X return(UNKNOWN_OPT); X } X} X X X/* We have an option in argv[optind] that requires an argument. If there X * is no whitespace after the option letter itself, take the rest of X * argv[optind] to be the argument. X * If there IS whitespace after the option letter, take argv[optind+1] to X * be the argument. X * Otherwise, if there is NO argument, complain! */ X Xstatic void HandleArgument(char *argv[], int *optind, int *optsSkipped) X{ X if (argv[*optind][1+(*optsSkipped)]) X optarg = argv[*optind] + 1 + (*optsSkipped); X else if (argv[(*optind)+1]) X { X optarg = argv[(*optind)+1]; X (*optind)++; X } X else X Error(ERROR_MISSING_ARGUMENT, optopt); X X (*optsSkipped) = 0; X (*optind)++; X} X X X/* Print an appropriate error message. */ X Xstatic void Error(int err, int c) X{ X static char *optmsg = "Illegal option.\n"; X static char *argmsg = "An argument is required, but missing.\n"; X X if (opterr) X { X if (err == ERROR_BAD_OPTION) X fprintf(stderr, "-%c: %s", c, optmsg); X else if (err == ERROR_MISSING_ARGUMENT) X fprintf(stderr, "-%c: %s", c, argmsg); X X else /* Sanity check! */ X fprintf(stderr, "-%c: an unknown error occurred\n", X c); X } X} X X X/* We have reached the end of argv[optind]... there are no more options X * in it to parse. Skip to the next item in argv. */ X Xstatic void Pass(char *argv[], int *optind, int *optsSkipped) X{ X if (argv[*optind] X && (argv[*optind][0] == DASH) X && (argv[*optind][1+(*optsSkipped)] == NULL)) X { X (*optind)++; X (*optsSkipped) = 0; X } X} X X/*************************************************************************** X* A test program. Compile this file with -DTESTME as an option. X***************************************************************************/ X X#ifdef TESTME Xmain(int argc, char *argv[]) X{ X char c; X X while ((c = getopt(argc, argv, "a:b:cde")) != EOF) X { X printf("OPTION %c", c); X if ((c == 'a') || (c == 'b')) X printf(", %s\n", optarg); X else X printf("\n"); X printf("argc=%d, optind=%d\n", argc, optind); X } X exit(0); X} X#endif END_OF_FILE if test 7295 -ne `wc -c <'Source/getopt.c'`; then echo shar: \"'Source/getopt.c'\" unpacked with wrong size! fi # end of 'Source/getopt.c' fi if test -f 'Source/help.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Source/help.c'\" else echo shar: Extracting \"'Source/help.c'\" \(2340 characters\) sed "s/^X//" >'Source/help.c' <<'END_OF_FILE' X/************************************************************************** X* help.c: Functions for giving help to the user. X* Part of MP, the MIDI Playground. X* X* Author: Daniel Barrett X* Version: See the file "version.h". X* Copyright: None! This program is in the Public Domain. X* Please share it with others. X***************************************************************************/ X X X#include "mp.h" X#include "version.h" X X X/* Print a detailed help message. */ X Xvoid Help(char *progName) X{ X FILE *fp = stderr; X X fprintf(fp, "MIDI Playground (%s), version %s, by Daniel Barrett." X " 100%% Public Domain!\n", X progName, VERSION); X fprintf(fp, X "Usage: %s -%c<FORMAT> -%c<FORMAT> [-%c infile] [-%c outfile]\n", X progName, X OPT_INPUT, OPT_OUTPUT, OPT_INFILE, OPT_OUTFILE); X X fprintf(fp, X "The program quits at end-of-file, or when ^C is pressed.\n"); X X fprintf(fp, "\nThe flags may appear in any order:\n"); X fprintf(fp, "\t-%c\tSpecify the input format. [MANDATORY]\n", X OPT_INPUT); X fprintf(fp, "\t-%c\tSpecify the output format. [MANDATORY]\n", X OPT_OUTPUT); X fprintf(fp, "\t\t\"-%c\" and \"-%c\" must EACH be followed by", X OPT_INPUT, OPT_OUTPUT); X fprintf(fp, " exactly one of:\n"); X fprintf(fp, "\t\t\t%c\tText (readable by humans).\n", OPT_TEXT); X fprintf(fp, "\t\t\t%c\tBinary data (for files).\n", OPT_BINARY); X fprintf(fp, "\t\t\t%c\tMIDI (using MIDI port).\n", OPT_MIDI); X fprintf(fp, "\t-%c\tGet data from this file. [Default=keyboard]\n", X OPT_INFILE); X fprintf(fp, "\t-%c\tPut data into this file. [Default=screen]\n", X OPT_OUTFILE); X X fprintf(fp, "\nExamples:\n"); X fprintf(fp, "\t%s -%c%c -%c%c -%c data\t%s\n", X progName, X OPT_INPUT, OPT_MIDI, OPT_OUTPUT, OPT_BINARY, X OPT_OUTFILE, X "Read MIDI & put it into binary file \"data\"."); X fprintf(fp, X "\t%s -%c%c -%c%c\t\tRead typed text, send to MIDI (fun!).\n", X progName, X OPT_INPUT, OPT_TEXT, OPT_OUTPUT, OPT_MIDI); X X fprintf(fp, "\n\"%s -%c%c\" understands decimal, octal, hex,", X progName, OPT_INPUT, OPT_TEXT); X fprintf(fp, " binary, characters, and strings.\n"); X fprintf(fp, "For details, type %c while in \"-%c%c\" mode.\n", X HELP_SYMBOL, OPT_INPUT, OPT_TEXT); X} X X X/* Tell the user how to get more help. */ X Xvoid BegForUsage(char *progName) X{ X fprintf(stderr, "Please type \"%s ?\" for instructions.\n", X progName); X} END_OF_FILE if test 2340 -ne `wc -c <'Source/help.c'`; then echo shar: \"'Source/help.c'\" unpacked with wrong size! fi # end of 'Source/help.c' fi if test -f 'Source/iofunctions.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Source/iofunctions.c'\" else echo shar: Extracting \"'Source/iofunctions.c'\" \(3430 characters\) sed "s/^X//" >'Source/iofunctions.c' <<'END_OF_FILE' X/************************************************************************** X* iofunctions.c: X* Functions for input/output in a variety of formats. X* Part of MP, the MIDI Playground. X* X* Author: Daniel Barrett X* Version: See the file "version.h". X* Copyright: None! This program is in the Public Domain. X* Please share it with others. X***************************************************************************/ X X X#include "mp.h" X#include "midi.h" X X X/* Read data from MIDI. Return the value of the next MIDI byte. Because X * reading byte-by-byte from the MIDI port is inefficient, we read as X * much as we can and keep it in a static buffer. */ X XMIDI_RESULT FromMidi(FILE *in, MIDI_VALUE *value) X{ X static long bytesWaiting = 0L; /* # bytes waiting at serial port. */ X static UBYTE buf[BUFSIZ]; /* Static buffer. */ X static long currentByte = 0L; /* Index of next byte to return. */ X X if (currentByte >= bytesWaiting) X { X bytesWaiting = FastSerialRead(buf); X currentByte = 0L; X } X X if (bytesWaiting == CTRL_C_NO_BYTES) X { X bytesWaiting = currentByte = 0L; X return(RESULT_STOP); X } X else if (bytesWaiting <= 0) X { X bytesWaiting = currentByte = 0L; X return(RESULT_ERROR); X } X else X { X *value = buf[currentByte++]; X return(RESULT_OK); X } X} X X X/* Write a byte to MIDI. It is inefficient to write 1 byte at a time, but X * we want instant transmission. I will rewrite this eventually. */ X XBOOL ToMidi(FILE *dummy, MIDI_VALUE value) X{ X PrepareToWriteMidi(&value, 1); X return(DoTheIO() == 1L); X} X X/* Read 1 byte from text. We use a finite state automaton. X * If an error occurs, assign the value of the last character read X * to *answer. */ X XMIDI_RESULT FromText(FILE *in, MIDI_VALUE *value) X{ X STATE_T state = STATE_NORMAL; X int c; X X *value = 0L; X X while ((state != STATE_SUCCESS) && (state != STATE_ERROR) X && (state != STATE_OVERFLOW) X && ((c = getc(in)) != EOF)) X X state = NewState(c, state, value); X X if (state == STATE_SUCCESS) X return(RESULT_OK); X else if (c == EOF && state == STATE_NORMAL) X return(RESULT_STOP); X else if (c == EOF) X return(RESULT_ERROR); X else if (state == STATE_OVERFLOW) X return(RESULT_OVERFLOW); X else X { X *value = c; X return(RESULT_ERROR); X } X} X X X/* Write 1 MIDI value as text. */ X XBOOL ToText(FILE *out, MIDI_VALUE value) X{ X static long byt = 0L; X X fprintf(out, "<%6ld> ", ++byt); X PrintNumber(value, out); X} X X X/* Read 1 MIDI value from a binary file. Inefficient. */ X XMIDI_RESULT FromBinary(FILE *in, MIDI_VALUE *value) X{ X int c; X if ((c = getc(in)) != EOF) X { X *value = (MIDI_VALUE)c; X return(RESULT_OK); X } X X return(RESULT_STOP); X} X X X/* Write 1 MIDI value to a binary file. Inefficient. */ X XBOOL ToBinary(FILE *out, MIDI_VALUE value) X{ X return(putc(value, out) != EOF); X} X X X/**************************************************************************** X* Skipping input. X****************************************************************************/ X X X/* Skip to the next valid text input. */ X Xvoid SkipText(FILE *in, MIDI_VALUE value) X{ X register int c; X X if (isspace(value)) X return; X X while ((c = getc(in)) != EOF && !isspace(c)) X ; X if (c != EOF) X ungetc(c, in); X} X X X/* Skip to the next valid MIDI input by clearing the serial port buffer. */ X Xvoid SkipMidi(FILE *dummy, MIDI_VALUE junk) X{ X ResetSerialPort(); X} X X X/* Skip to the next valid binary value. This is currently just a stub. */ X Xvoid SkipBinary(FILE *in, MIDI_VALUE junk) X{ X return; X} END_OF_FILE if test 3430 -ne `wc -c <'Source/iofunctions.c'`; then echo shar: \"'Source/iofunctions.c'\" unpacked with wrong size! fi # end of 'Source/iofunctions.c' fi if test -f 'Source/main.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Source/main.c'\" else echo shar: Extracting \"'Source/main.c'\" \(4433 characters\) sed "s/^X//" >'Source/main.c' <<'END_OF_FILE' X/************************************************************************** X* main.c: The main program and supporting stuff. X* Part of MP, the MIDI Playground. X* X* Author: Daniel Barrett X* Version: See the file "version.h". X* Copyright: None! This program is in the Public Domain. X* Please share it with others. X***************************************************************************/ X X X#include "mp.h" X XBOOL WorkbenchProgram(char *argv[]); XBOOL CommandLineProgram(int argc, char *argv[]); X Xmain(int argc, char *argv[]) X{ X BOOL success; X X if (argc == 0) X success = WorkbenchProgram(argv); X else X success = CommandLineProgram(argc, argv); X X exit(success ? RETURN_OK : RETURN_FAIL); X} X X X/************************************************************************ X* Initialization of important variables. X************************************************************************/ X Xvoid InitStuff(FLAGS theFlags[], FILE **in, FILE **out, char **inFile, X char **outFile) X{ X int i; X X for (i=0; i<NUM_FLAGS; i++) /* Flags. */ X theFlags[i] = (FLAGS)0; X X *in = stdin; /* Input file. */ X *out = stdout; /* Output file. */ X *inFile = NULL; /* Input file name. */ X *outFile = NULL; /* Output file name. */ X} X X X/************************************************************************ X* Open the serial port if necessary, and have fun! X************************************************************************/ X XBOOL MIDIPlayground(FLAGS theFlags[], FILE *in, FILE *out) X{ X if ((theFlags[FLAG_ITYPE] == OPT_MIDI) X || (theFlags[FLAG_OTYPE] == OPT_MIDI)) X { X if (!SerialSetup(theFlags[FLAG_SYSEX])) X return(FALSE); X ResetSerialPort(); X } X X ReadAndWriteStuff(theFlags, in, out); X X X if ((theFlags[FLAG_ITYPE] == OPT_MIDI) X || (theFlags[FLAG_OTYPE] == OPT_MIDI)) X SerialShutdown(); X X return(TRUE); /* Stub. */ X} X X X/************************************************************************ X* Use the appropriate I/O functions until we're done. X************************************************************************/ X Xvoid ReadAndWriteStuff(FLAGS theFlags[], FILE *in, FILE *out) X{ X MIDI_RESULT result; X MIDI_VALUE value; X MIDI_RESULT (*getfcn)(FILE *f, MIDI_VALUE *value); X BOOL (*putfcn)(FILE *f, MIDI_VALUE value); X void (*skipfcn)(FILE *f, MIDI_VALUE value); X X SetTheFunctions(&getfcn, &putfcn, &skipfcn, theFlags); X X while ((result = getfcn(in, &value)) != RESULT_STOP) X { X switch (result) X { X case RESULT_OK: X putfcn(out, value); X break; X case RESULT_ERROR: X fprintf(out, "ERROR\n"); X skipfcn(in, value); X break; X case RESULT_OVERFLOW: X fprintf(out, "VALUE TOO BIG (overflow)\n"); X skipfcn(in, value); X break; X default: X skipfcn(in, value); X fprintf(out, "An unknown error occurred.\n"); X break; X } X } X} X X X/************************************************************************ X* Choose the appropriate I/O functions. X************************************************************************/ X Xvoid SetTheFunctions(MIDI_RESULT (**getfcn)(), BOOL (**putfcn)(), X void (**skipfcn)(), FLAGS theFlags[]) X{ X switch (theFlags[FLAG_ITYPE]) X { X case OPT_TEXT: X *getfcn = FromText; X *skipfcn = SkipText; X break; X case OPT_BINARY: X *getfcn = FromBinary; X *skipfcn = SkipBinary; X break; X case OPT_MIDI: X *getfcn = FromMidi; X *skipfcn = SkipMidi; X break; X default: X *getfcn = FromText; /* Least dangerous fcn. */ X *skipfcn = SkipText; /* Least dangerous fcn. */ X } X X switch (theFlags[FLAG_OTYPE]) X { X case OPT_TEXT: X *putfcn = ToText; X break; X case OPT_BINARY: X *putfcn = ToBinary; X break; X case OPT_MIDI: X *putfcn = ToMidi; X break; X default: X *putfcn = ToText; /* Least dangerous fcn. */ X break; X } X} X X X/* Is "opt" a valid input/output type? Set the flag too. */ X XBOOL CheckIOType(char opt, int ioFlag, FLAGS theFlags[]) X{ X return(IsIOType(theFlags[ioFlag] = opt)); X} X X X/* Is "ioType" a valid input/output type? */ X XBOOL IsIOType(char ioType) X{ X return( (ioType == OPT_TEXT) X || (ioType == OPT_BINARY) X || (ioType == OPT_MIDI)); X} X X X/* Did user specify both input and output types? */ X XBOOL CheckFlags(FLAGS theFlags[]) X{ X return(theFlags[FLAG_ITYPE] && theFlags[FLAG_OTYPE]); X} X X X/************************************************************************ X* ^C handling -- Manx stuff. X************************************************************************/ X Xvoid _abort(void) X{ X SerialShutdown(); X exit(RETURN_FAIL); X} END_OF_FILE if test 4433 -ne `wc -c <'Source/main.c'`; then echo shar: \"'Source/main.c'\" unpacked with wrong size! fi # end of 'Source/main.c' fi if test -f 'Source/midi.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Source/midi.h'\" else echo shar: Extracting \"'Source/midi.h'\" \(1182 characters\) sed "s/^X//" >'Source/midi.h' <<'END_OF_FILE' X#ifndef _MIDI_H X#define _MIDI_H 1 X X/*************************************************************************** X * MIDI values that are independent of any particular synthesizer. X ***************************************************************************/ X X/*************************************************************************** X * Some system-exclusive message constants. X ***************************************************************************/ X X#define SYSEX_BEGIN 0xF0 X#define SYSEX_END 0xF7 X X/*************************************************************************** X * Allow use of MIDI interfaces not attached to the internal serial port, X * unit 0. Here are the defaults. X ***************************************************************************/ X X#define DEFAULT_MIDI_DEVICE "serial.device" X#define DEFAULT_MIDI_UNIT 0 X#define MIDI_ENV_VAR "MP_MIDI_DEVICE" X X#define MIDI_BAUD_RATE 31250 X X#ifndef CTRL_C_NO_BYTES X#define CTRL_C_NO_BYTES (-1) X#endif X X/*************************************************************************** X * End of this header file. X ***************************************************************************/ X X#endif /* _MIDI_H */ END_OF_FILE if test 1182 -ne `wc -c <'Source/midi.h'`; then echo shar: \"'Source/midi.h'\" unpacked with wrong size! fi # end of 'Source/midi.h' fi if test -f 'Source/mp.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Source/mp.h'\" else echo shar: Extracting \"'Source/mp.h'\" \(8727 characters\) sed "s/^X//" >'Source/mp.h' <<'END_OF_FILE' X/************************************************************************** X* mp.h: Main header file. X* Part of MP, the MIDI Playground. X* X* Author: Daniel Barrett X* Version: See the file "version.h". X* Copyright: None! This program is in the Public Domain. X* Please share it with others. X***************************************************************************/ X X X#ifndef _MP_H X X#include <devices/serial.h> X#include <exec/devices.h> X#include <exec/memory.h> X#include <exec/ports.h> X#include <exec/types.h> X#include <exec/interrupts.h> X#include <libraries/dos.h> X#include <libraries/dosextens.h> X#include <workbench/startup.h> X#include <workbench/workbench.h> X#include <functions.h> X#include <stdio.h> X X#ifndef __C_MACROS__ X#define __C_MACROS__ X#endif X#include <ctype.h> X X X/************************************************************************** X* Types. X**************************************************************************/ X Xtypedef unsigned char STATE_T; /* An automaton state. */ Xtypedef UBYTE MIDI_VALUE; /* A single MIDI data value. */ Xtypedef signed char MIDI_RESULT; /* What happened while reading? */ Xtypedef char FLAGS; /* Internal flags. */ X X/************************************************************************** X* Return values from our input routine. X**************************************************************************/ X X#define RESULT_OK (MIDI_RESULT)(1) /* Legal value read. */ X#define RESULT_ERROR (MIDI_RESULT)(2) /* Illegal value read. */ X#define RESULT_OVERFLOW (MIDI_RESULT)(3) /* Value overflowed. */ X#define RESULT_STOP (MIDI_RESULT)(4) /* End of data. */ X X/************************************************************************** X* States of our finite automaton for reading input. X**************************************************************************/ X X#define STATE_SUCCESS 1 /* Got a legal value. */ X#define STATE_ERROR 2 /* Had an input error. */ X#define STATE_OVERFLOW 3 /* Read too big a value. */ X#define STATE_NORMAL 4 /* Haven't read anything yet. */ X#define STATE_INCOMMENT 5 /* Skipping a comment. */ X#define STATE_INDECIMAL 6 /* Reading a base 10 number. */ X#define STATE_LEADZERO 7 /* Read a leading zero. */ X#define STATE_INOCTAL 8 /* Reading an octal number. */ X#define STATE_STARTHEX 9 /* Read 1st digit of hex number. */ X#define STATE_INHEX 10 /* Reading a hex number. */ X#define STATE_STARTBINARY 11 /* Read 1st symbol of binary num. */ X#define STATE_INBINARY 12 /* Reading a binary number. */ X#define STATE_NEGATIVE 13 /* UNUSED. FOR FUTURE UPDATE. */ X#define STATE_INCHAR 14 /* Reading a char constant. */ X#define STATE_EXPECTQUOTE 15 /* Finishing a char constant. */ X#define STATE_BACKSLASHINCHAR 16 /* Backslash in a char constant. */ X#define STATE_BACKSLASHINSTRING 17 /* Backslash in a string constant. */ X#define STATE_INCLUDEFILE 18 /* UNUSED. FOR FUTURE UPDATE. */ X X X/************************************************************************** X* Macros for converting characters to numbers: X* X* D_TO_INT(c) Convert a digit to its numeric value. X* L_TO_INT(c) Convert a letter (A..F) to its base 16 value. X* H_TO_INT(c) Convert a hex digit to its numeric value. X**************************************************************************/ X X#define D_TO_INT(c) ((long)((c) - '0')) X#define L_TO_INT(c) ((long)((toupper(c)) - 'A') + 10) X#define H_TO_INT(c) (isdigit(c) ? D_TO_INT(c) : L_TO_INT(c)) X X/************************************************************************** X* Macros for classifying types of input. The rest are in <ctype.h>. X**************************************************************************/ X X#define isoctal(c) (((c) >= '0') && ((c) <= '7')) X X/************************************************************************** X* Other macros. X**************************************************************************/ X X#define MAX(a,b) (((a) > (b)) ? (a) : (b)) X#define MIN(a,b) (((a) < (b)) ? (a) : (b)) X X#define OpenReadFile(filename) OpenAFile(filename, "r", "read") X#define OpenWriteFile(filename) OpenAFile(filename, "w", "write") X X X/************************************************************************** X* The largest legal value our input routine can handle. X**************************************************************************/ X X#define LARGEST_VALUE (MIDI_VALUE)(255) X#define BITS_IN_MIDI_VALUE BITSPERBYTE /* in types.h */ X X/*************************************************************************** X* Character constants for classifying the input. X***************************************************************************/ X X#define START_BINARY '#' /* Symbol for start of binary number. */ X#define HELP_SYMBOL '?' /* Symbol for help request from user. */ X#define START_COMMENT ';' /* Start-comment symbol. */ X X/*************************************************************************** X* Command-line Options. X***************************************************************************/ X X#define OPT_INPUT 'i' /* Input format. */ X#define OPT_OUTPUT 'o' /* Output format. */ X#define OPT_TEXT 't' /* Text I/O. */ X#define OPT_BINARY 'b' /* Binary I/O. */ X#define OPT_MIDI 'm' /* MIDI I/O. */ X#define OPT_SYSEX 'x' /* UNUSED. FOR FUTURE UPDATE. */ X#define OPT_INFILE 'g' /* Get from file. */ X#define OPT_OUTFILE 'p' /* Put to file. */ X X/* For getopt(). */ X#define GETOPT_OPTIONS "i:o:g:p:x"; Xextern int optind; Xextern char *optarg; X X/*************************************************************************** X* Flags, and their associated macros. X***************************************************************************/ X X#define NUM_FLAGS 3 /* How many flags? */ X X#define FLAG_ITYPE 0 /* Flag ID's. */ X#define FLAG_OTYPE 1 X#define FLAG_SYSEX 2 X X X/*************************************************************************** X* Prototypes. X***************************************************************************/ X X/* read.c */ XSTATE_T NewState(int c, STATE_T state, MIDI_VALUE *answer); XSTATE_T NonStringState(int c, STATE_T state, MIDI_VALUE *answer); XSTATE_T DoNormal(int c, MIDI_VALUE *answer); XSTATE_T DoDecimal(int c, MIDI_VALUE *answer); XSTATE_T DoOctal(int c, MIDI_VALUE *answer); XSTATE_T DoHex(int c, MIDI_VALUE *answer); XSTATE_T DoInHex(int c, MIDI_VALUE *answer); XSTATE_T DoBinary(int c, MIDI_VALUE *answer); XSTATE_T DoInBinary(int c, MIDI_VALUE *answer); XSTATE_T DoLeadZero(int c, MIDI_VALUE *answer); XSTATE_T IncreaseIfPossible(MIDI_VALUE *answer, int newNum, int base, X STATE_T newState); XSTATE_T DoInChar(int c, MIDI_VALUE *answer); XSTATE_T DoExpectQuote(int c, MIDI_VALUE *answer); XSTATE_T DoBackslash(int c, MIDI_VALUE *answer, STATE_T newState); XSTATE_T DoInString(int c, STATE_T state, MIDI_VALUE *answer, X short *inString); XSTATE_T DoComment(int c); Xvoid InputHelp(); X Xvoid PrintNumber(MIDI_VALUE number, FILE *out); Xvoid PrintBinary(MIDI_VALUE number, FILE *out); X X/* serial.c */ Xshort SerialSetup(FLAGS sysex); Xvoid SerialShutdown(void); Xvoid ResetSerialPort(void); Xlong AnyMidiData(void); Xvoid PrepareToReadMidi(UBYTE *buf, int len); Xvoid PrepareToWriteMidi(UBYTE *buf, int len); Xlong DoTheIO(void); Xlong FastSerialRead(UBYTE buf[]); X X/* iofunctions.c */ XMIDI_RESULT FromMidi(FILE *in, MIDI_VALUE *value); XBOOL ToMidi(FILE *in, MIDI_VALUE value); XMIDI_RESULT FromText(FILE *in, MIDI_VALUE *value); XBOOL ToText(FILE *in, MIDI_VALUE value); XMIDI_RESULT FromBinary(FILE *in, MIDI_VALUE *value); XBOOL ToBinary(FILE *in, MIDI_VALUE value); Xvoid SkipText(FILE *in, MIDI_VALUE value); Xvoid SkipMidi(FILE *in, MIDI_VALUE junk); Xvoid SkipBinary(FILE *in, MIDI_VALUE junk); X X/* main.c */ XBOOL IsIOType(char ioType); XBOOL CheckIOType(char opt, int ioFlag, FLAGS theFlags[]); XBOOL HandleOptions(int argc, char *argv[], FLAGS theFlags[], X char **infile, char **outfile); Xvoid ReadAndWriteStuff(FLAGS theFlags[], FILE *in, FILE *out); Xvoid SetTheFunctions(MIDI_RESULT (**getfcn)(), BOOL (**putfcn)(), X void (**skipfcn)(), FLAGS theFlags[]); XBOOL CheckFlags(FLAGS theFlags[]); Xvoid InitStuff(FLAGS theFlags[], FILE **in, FILE **out, char **inFile, X char **outFile); XBOOL MIDIPlayground(FLAGS theFlags[], FILE *in, FILE *out); X X/* wb.c */ X X/* help.c */ Xvoid BegForUsage(char *progName); Xvoid Help(char *progName); X X/* files.c */ XFILE *OpenAFile(char *filename, char *mode, char *readOrWrite); XBOOL FileExists(char *filename); XBOOL DontOverwriteExistingFile(char *filename); XBOOL SetupFiles(char *infile, char *outfile, FILE **in, FILE **out); Xvoid CloseFiles(FILE *in, FILE *out, char *filein, char *fileout); XBOOL MakeFilename(char **dest, char *src); X X#ifndef OpenReadFile XFILE *OpenReadFile(char *filename); X#endif X X#ifndef OpenWriteFile XFILE *OpenWriteFile(char *filename); X#endif X X X#endif /* _MP_H */ END_OF_FILE if test 8727 -ne `wc -c <'Source/mp.h'`; then echo shar: \"'Source/mp.h'\" unpacked with wrong size! fi # end of 'Source/mp.h' fi if test -f 'Source/serial.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Source/serial.c'\" else echo shar: Extracting \"'Source/serial.c'\" \(7123 characters\) sed "s/^X//" >'Source/serial.c' <<'END_OF_FILE' X/************************************************************************** X* serial.c: Serial port access functions. X* Part of MP, the MIDI Playground. X* X* Author: Daniel Barrett X* Version: See the file "version.h". X* Copyright: None! This program is in the Public Domain. X* Please share it with others. X***************************************************************************/ X X X#include "mp.h" X#include "midi.h" X Xstatic struct MsgPort *MidiPort = NULL; Xstatic struct IOExtSer *Midi = NULL; Xlong serialFlags = 0L; X X#define F_MIDIPORT 1 X#define F_MIDI 2 X#define F_DEVICE 4 X#define F_SERPARAMS 8 X X XBOOL AllDigits(char *str); Xchar *GetEnv(char *str); XBOOL MakeSerialDeviceName(char *str, char *device, int *unit); X X X/**************************************************************************** X * SerialSetup: Open the serial port and its message port. X * Allow for an alternate MIDI device in an environment variable. X ***************************************************************************/ X XBOOL SerialSetup(FLAGS sysex) X{ X char deviceName[BUFSIZ], *envVar; X int unit; X X /* Set the defaults. */ X X strcpy(deviceName, DEFAULT_MIDI_DEVICE); X unit = DEFAULT_MIDI_UNIT; X X /* If environment variable is set, find the real MIDI device info. */ X X if ((envVar = GetEnv(MIDI_ENV_VAR)) X && (!MakeSerialDeviceName(envVar, deviceName, &unit))) X return(FALSE); X X /* Create a port. */ X X if (! (MidiPort = CreatePort(0, 0)) ) X { X fprintf(stderr, "Cannot create port\n"); X SerialShutdown(); X return(FALSE); X } X else X serialFlags |= F_MIDIPORT; X X /* Create an extended I/O structure. */ X X if (! (Midi = (struct IOExtSer *) X CreateExtIO(MidiPort, sizeof(struct IOExtSer)))) X { X fprintf(stderr, "Cannot create extended I/O structure\n"); X SerialShutdown(); X return(FALSE); X } X else X serialFlags |= F_MIDI; X X /* Open the serial device. */ X X Midi->io_SerFlags = SERF_SHARED; X if (OpenDevice(deviceName, unit, (struct IORequest *)Midi, 0)) X { X fprintf(stderr, X "Cannot open serial device \"%s\", unit %d.\n", X deviceName, unit); X SerialShutdown(); X return(FALSE); X } X else X serialFlags |= F_DEVICE; X X /* Set the serial device parameters. */ X X Midi->io_SerFlags = SERF_RAD_BOOGIE; X if (sysex) X { X Midi->io_TermArray.TermArray0 = 0xF7F7F7F7; X Midi->io_TermArray.TermArray1 = 0xF7F7F7F7; X Midi->io_SerFlags |= SERF_EOFMODE; X } X Midi->io_Baud = MIDI_BAUD_RATE; X Midi->io_ExtFlags = 0; /* For future compatibility */ X Midi->IOSer.io_Command = SDCMD_SETPARAMS; X X if (DoIO((struct IORequest *)Midi) != 0) X { X fprintf(stderr, "Cannot set serial parameters.\n"); X SerialShutdown(); X return(FALSE); X } X else X serialFlags |= F_SERPARAMS; X X return(TRUE); X} X X X/**************************************************************************** X * SerialShutdown: Close the serial port and its message port. X ***************************************************************************/ X Xvoid SerialShutdown(void) X{ X if (serialFlags & F_DEVICE) X { X ResetSerialPort(); X AbortIO((struct IORequest *)Midi); X WaitIO((struct IORequest *)Midi); X CloseDevice((struct IORequest *)Midi); X } X X if (serialFlags & F_MIDI) X DeleteExtIO((struct IORequest *)Midi); X X if (serialFlags & F_MIDIPORT) X DeletePort(MidiPort); X} X X/**************************************************************************** X * ResetSerialPort: Clear all data from the serial port. X ***************************************************************************/ X Xvoid ResetSerialPort(void) X{ X Midi->IOSer.io_Command = CMD_CLEAR; X DoIO((struct IORequest *)Midi); X} X X X/**************************************************************************** X * Is any data there? X ***************************************************************************/ X Xlong AnyMidiData(void) X{ X Midi->IOSer.io_Command = SDCMD_QUERY; X DoIO((struct IORequest *)Midi); X return(Midi->IOSer.io_Actual); X} X X X/**************************************************************************** X * PrepareToReadMidi: Prepare a READ request for the MIDI port. X * PrepareToWriteMidi: Prepare a WRITE request for the MIDI port. X ***************************************************************************/ X Xvoid PrepareToReadMidi(UBYTE buf[], int len) X{ X Midi->IOSer.io_Command = CMD_READ; X Midi->IOSer.io_Data = (APTR)buf; X Midi->IOSer.io_Length = len; X} X X Xvoid PrepareToWriteMidi(UBYTE buf[], int len) X{ X Midi->IOSer.io_Command = CMD_WRITE; X Midi->IOSer.io_Data = (APTR)buf; X Midi->IOSer.io_Length = len; X} X X X/**************************************************************************** X* DoTheIO: General-purpose MIDI I/O routine. Quits on ^C. X****************************************************************************/ X Xlong DoTheIO(void) X{ X int mask, temp; X long bytesDone = 0L; X X mask = SIGBREAKF_CTRL_C | (1L << MidiPort->mp_SigBit); X SendIO((struct IORequest *)Midi); X X while (1) X { X temp = Wait(mask); X if (temp & SIGBREAKF_CTRL_C) X { X bytesDone = CTRL_C_NO_BYTES; X break; X } X X if (CheckIO((struct IORequest *)Midi)) X { X WaitIO((struct IORequest *)Midi); X bytesDone = Midi->IOSer.io_Actual; X break; X } X } X X AbortIO((struct IORequest *)Midi); X WaitIO((struct IORequest *)Midi); X return(bytesDone); X} X X X/**************************************************************************** X* Fast serial reading routine, from idea on 1.3 RKM's page 863. X* If any data is waiting, get all of it with DoIO(). Otherwise, post an X* asynchronous request for 1 byte. Repeat this in the calling program. X****************************************************************************/ X Xlong FastSerialRead(UBYTE buf[]) X{ X long bytesWaiting, bytesRead; X X if ((bytesWaiting = AnyMidiData()) > 0) X { X PrepareToReadMidi(buf, MIN(bytesWaiting, BUFSIZ)); X DoIO((struct IORequest *)Midi); X return(Midi->IOSer.io_Actual); X } X else X { X PrepareToReadMidi(buf, 1); X return(DoTheIO()); X } X} X X X/**************************************************************************** X* Allow the use of another MIDI device than serial.device. X* Environment variable syntax is "DEVICENAME:UNITNUMBER". X* For example: "midi.device:2". X* X* "device" MUST be preallocated. X****************************************************************************/ X XBOOL MakeSerialDeviceName(char *str, char *device, int *unit) X{ X while (str && *str && (*str != ':')) X *(device++) = *(str++); X X *device = '\0'; X X if ((*str != ':') || *(str+1) == '\0') X { X fprintf(stderr, X "Your MIDI device name (variable " X MIDI_ENV_VAR X ") is missing a colon\nand/or a unit number.\n"); X return(FALSE); X } X str++; X X if (!AllDigits(str)) X { X fprintf(stderr, X "Your MIDI device unit number (variable " X MIDI_ENV_VAR X ") must be\na positive integer.\n"); X return(FALSE); X } X else X *unit = atoi(str); X X return(TRUE); X} X X X/* AllDigits: Return TRUE iff the string "str" consists only of digits. */ X XBOOL AllDigits(char *str) X{ X if ((!str) || (*str == '\0')) /* NULL or empty string. */ X return(FALSE); X else X while (*str) /* For each character... */ X if (!isdigit(*str)) /* if not a digit... */ X return(FALSE); /* goodbye! */ X else X str++; X return(TRUE); /* All were digits. */ X} END_OF_FILE if test 7123 -ne `wc -c <'Source/serial.c'`; then echo shar: \"'Source/serial.c'\" unpacked with wrong size! fi # end of 'Source/serial.c' fi if test -f 'Source/text.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Source/text.c'\" else echo shar: Extracting \"'Source/text.c'\" \(9628 characters\) sed "s/^X//" >'Source/text.c' <<'END_OF_FILE' X/************************************************************************** X* text.c: Functions for text input/output. X* Part of MP, the MIDI Playground. X* X* Author: Daniel Barrett X* Version: See the file "version.h". X* Copyright: None! This program is in the Public Domain. X* Please share it with others. X***************************************************************************/ X X X#include "mp.h" X X X/* Given the currently read character and the current state, compute X * the new state and the MIDI value. */ X XSTATE_T NewState(int c, STATE_T state, MIDI_VALUE *answer) X{ X static BOOL inString = FALSE; X X if (inString) X return(DoInString(c, state, answer, &inString)); X else if (c == HELP_SYMBOL) X { X InputHelp(); X return(state); X } X else if (state == STATE_NORMAL && c == '\"') X { X inString = TRUE; X return(STATE_NORMAL); /* Doesn't matter. */ X } X else X return(NonStringState(c, state, answer)); X} X X/**************************************************************************** X* Our finite automaton for non-strings. Process the character and return X* the new state. X****************************************************************************/ X XSTATE_T NonStringState(int c, STATE_T state, MIDI_VALUE *answer) X{ X switch (state) X { X case STATE_NORMAL: return(DoNormal(c, answer)); X case STATE_INCOMMENT: return(DoComment(c)); X case STATE_INDECIMAL: return(DoDecimal(c, answer)); X case STATE_INOCTAL: return(DoOctal(c, answer)); X case STATE_STARTHEX: return(DoHex(c, answer)); X case STATE_INHEX: return(DoInHex(c, answer)); X case STATE_LEADZERO: return(DoLeadZero(c, answer)); X case STATE_INCHAR: return(DoInChar(c, answer)); X case STATE_STARTBINARY: return(DoBinary(c, answer)); X case STATE_INBINARY: return(DoInBinary(c, answer)); X case STATE_EXPECTQUOTE: return(DoExpectQuote(c, answer)); X case STATE_BACKSLASHINCHAR: X return(DoBackslash(c, answer, STATE_EXPECTQUOTE)); X case STATE_SUCCESS: X case STATE_OVERFLOW: X case STATE_ERROR: return(state); X } X} X X X/**************************************************************************** X* We are not currently in a number. A character is read; decide what to X* do with it. X****************************************************************************/ X XSTATE_T DoNormal(int c, MIDI_VALUE *answer) X{ X if (isspace(c)) X return(STATE_NORMAL); X else if (c == START_COMMENT) X return(STATE_INCOMMENT); X else if (c == START_BINARY) X return(STATE_STARTBINARY); X else if (c == '\'') X return(STATE_INCHAR); X else if (c == '0') X return(STATE_LEADZERO); X else if (toupper(c) == 'H') X return(STATE_STARTHEX); X else if (isdigit(c)) X { X *answer = D_TO_INT(c); X return(STATE_INDECIMAL); X } X else if (isxdigit(c)) X { X *answer = H_TO_INT(c); X return(STATE_INHEX); X } X else X return(STATE_ERROR); X} X X X/**************************************************************************** X* Base 10 (decimal) numbers. X****************************************************************************/ X XSTATE_T DoDecimal(int c, MIDI_VALUE *answer) X{ X if (isdigit(c)) X return(IncreaseIfPossible(answer, D_TO_INT(c), 10, X STATE_INDECIMAL)); X else if (isspace(c)) X return(STATE_SUCCESS); X else X return(STATE_ERROR); X} X X X/**************************************************************************** X* Octal numbers. X****************************************************************************/ X XSTATE_T DoOctal(int c, MIDI_VALUE *answer) X{ X if (isoctal(c)) X return(IncreaseIfPossible(answer, D_TO_INT(c), 8, X STATE_INOCTAL)); X else if (isspace(c)) X return(STATE_SUCCESS); X else X return(STATE_ERROR); X} X X X/**************************************************************************** X* Hexadecimal numbers. X****************************************************************************/ X XSTATE_T DoHex(int c, MIDI_VALUE *answer) X{ X if (isxdigit(c)) X { X *answer = H_TO_INT(c); X return(STATE_INHEX); X } X else X return(STATE_ERROR); X} X X XSTATE_T DoInHex(int c, MIDI_VALUE *answer) X{ X if (isxdigit(c)) X return(IncreaseIfPossible(answer, H_TO_INT(c), 16, X STATE_INHEX)); X else if (isspace(c)) X return(STATE_SUCCESS); X else X return(STATE_ERROR); X} X X X/**************************************************************************** X* Binary numbers. X****************************************************************************/ X XSTATE_T DoBinary(int c, MIDI_VALUE *answer) X{ X if ((c == '0') || (c == '1')) X { X *answer = D_TO_INT(c); X return(STATE_INBINARY); X } X else X return(STATE_ERROR); X} X X XSTATE_T DoInBinary(int c, MIDI_VALUE *answer) X{ X if ((c == '0') || (c == '1')) X return(IncreaseIfPossible(answer, D_TO_INT(c), 2, X STATE_INBINARY)); X else if (isspace(c)) X return(STATE_SUCCESS); X else X return(STATE_ERROR); X} X X X/**************************************************************************** X* Hook for negative numbers. Commented out right now. X****************************************************************************/ X X#ifdef ALLOW_NEGATIVE XSTATE_T DoNegative(int c, MIDI_VALUE *answer) X{ X if (c == '0') X return(STATE_LEADZERO); X else if (isdigit(c)) X { X return(STATE_INDECIMAL); X } X else X return(STATE_ERROR); X} X#endif /* ALLOW_NEGATIVE */ X X X/**************************************************************************** X* Leading zero was found: Do octal or hexadecimal as required. X****************************************************************************/ X XSTATE_T DoLeadZero(int c, MIDI_VALUE *answer) X{ X if (toupper(c) == 'X') X return(STATE_STARTHEX); X else if (isoctal(c)) X { X *answer = D_TO_INT(c); X return(STATE_INOCTAL); X } X else if (isspace(c)) X return(STATE_SUCCESS); X else X return(STATE_ERROR); X} X X X/**************************************************************************** X* Append the digit "newNum" onto the right of the number *answer. X* Don't allow overflow. Works for any base. Return newState on success. X****************************************************************************/ X XSTATE_T IncreaseIfPossible(MIDI_VALUE *answer, int newNum, int base, X STATE_T newState) X{ X if ((*answer) > (LARGEST_VALUE / base)) X return(STATE_OVERFLOW); X else X { X *answer *= base; X X if ((*answer) > (LARGEST_VALUE - newNum)) X return(STATE_OVERFLOW); X else X { X *answer += newNum; X return(newState); X } X } X} X X X/**************************************************************************** X* Character-oriented routines. X****************************************************************************/ X XSTATE_T DoInChar(int c, MIDI_VALUE *answer) X{ X if (c == '\\') X return(STATE_BACKSLASHINCHAR); X else X { X *answer = c; X return(STATE_EXPECTQUOTE); X } X} X X XSTATE_T DoExpectQuote(int c, MIDI_VALUE *answer) X{ X return((c == '\'') ? STATE_SUCCESS : STATE_ERROR); X} X X XSTATE_T DoBackslash(int c, MIDI_VALUE *answer, STATE_T newState) X{ X switch (c) X { X case '0': *answer = '\0'; break; X case 'a': *answer = '\a'; break; X case 'b': *answer = '\b'; break; X case 'f': *answer = '\f'; break; X case 'n': *answer = '\n'; break; X case 'r': *answer = '\r'; break; X case 't': *answer = '\t'; break; X case 'v': *answer = '\v'; break; X case '\\': X case '\'': X case '\"': *answer = c; break; X default: return(STATE_ERROR); X } X return(newState); X} X X X/**************************************************************************** X* String-oriented routines. X****************************************************************************/ X XSTATE_T DoInString(int c, STATE_T state, MIDI_VALUE *answer, BOOL *inString) X{ X switch (state) X { X case STATE_NORMAL: X if (c == '\"') X { X *inString = FALSE; X return(STATE_NORMAL); X } X else if (c == '\\') X return(STATE_BACKSLASHINSTRING); X else X { X *answer = isspace(c) ? ' ' : c; X return(STATE_SUCCESS); X } X break; X case STATE_BACKSLASHINSTRING: X return(DoBackslash(c, answer, STATE_SUCCESS)); X } X} X X X/**************************************************************************** X* Handling comments. Everything from comment symbol to the end of the X* line is a comment. X****************************************************************************/ X XSTATE_T DoComment(int c) X{ X return( (c == '\n') ? STATE_NORMAL : STATE_INCOMMENT ); X} X X/**************************************************************************** X* Getting help. X****************************************************************************/ X X Xvoid InputHelp() X{ X fprintf(stderr, "INPUT\tMEANING\n" X "1-9...\tDecimal number.\n" X "0...\tOctal number.\n" X "0x...\tHexadecimal number (case-insensitive).\n" X "H...\tHexadecimal number (case-insensitive).\n" X "A-F...\tHexadecimal number (case-insensitive).\n" X "#...\tBinary number.\n" X "'x'\tThe character 'x'." X " C character syntax like \\n and \\t is valid.\n" X "\"...\"\tA text string." X " (Newlines turn into space characters.)\n" X "Largest legal input value is %ld (base 10).\n" X "All values must be separated by whitespace.\n\n", X LARGEST_VALUE); X} X X X/**************************************************************************** X* Text output functions. X****************************************************************************/ X Xvoid PrintNumber(MIDI_VALUE number, FILE *out) X{ X fprintf(out, "Dec %3ld, Oct %3lo, Hex %3lX", X number, number, number); X X PrintBinary(number, out); X X if (isprint((char)number)) X fprintf(out, ", Char '%c'", number); X X putc('\n', out); X} X X Xvoid PrintBinary(MIDI_VALUE number, FILE *out) X{ X char buf[BITS_IN_MIDI_VALUE + 1]; X int i, on; X X for (i=0; i<BITS_IN_MIDI_VALUE; i++) X { X on = number & (1 << i); X buf[BITS_IN_MIDI_VALUE-i-1] = '0' + (char)(on && 1); X } X X buf[BITS_IN_MIDI_VALUE] = '\0'; X fprintf(out, ", Bin %s", buf); X} END_OF_FILE if test 9628 -ne `wc -c <'Source/text.c'`; then echo shar: \"'Source/text.c'\" unpacked with wrong size! fi # end of 'Source/text.c' fi if test -f 'Source/version.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Source/version.h'\" else echo shar: Extracting \"'Source/version.h'\" \(628 characters\) sed "s/^X//" >'Source/version.h' <<'END_OF_FILE' X/************************************************************************** X* version.h: The version number. X* Part of MP, the MIDI Playground. X* X* Author: Daniel Barrett X* Version: See the file "version.h". X* Copyright: None! This program is in the Public Domain. X* Please share it with others. X***************************************************************************/ X X X#ifndef _VERSION_H X X/************************************************************************** X* Program version number. X**************************************************************************/ X X#define VERSION "1.0" X X#endif /* _VERSION_H */ END_OF_FILE if test 628 -ne `wc -c <'Source/version.h'`; then echo shar: \"'Source/version.h'\" unpacked with wrong size! fi # end of 'Source/version.h' fi if test -f 'Source/wb.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Source/wb.c'\" else echo shar: Extracting \"'Source/wb.c'\" \(86 characters\) sed "s/^X//" >'Source/wb.c' <<'END_OF_FILE' XBOOL WorkbenchProgram(char *argv[]) X{ X /* For future expansion. */ X X return(FALSE); X} END_OF_FILE if test 86 -ne `wc -c <'Source/wb.c'`; then echo shar: \"'Source/wb.c'\" unpacked with wrong size! fi # end of 'Source/wb.c' fi echo shar: End of archive 1 \(of 2\). cp /dev/null ark1isdone MISSING="" for I in 1 2 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked both archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 -- Mail submissions (sources or binaries) to <amiga@uunet.uu.net>. Mail comments to the moderator at <amiga-request@uunet.uu.net>. Post requests for sources, and general discussion to comp.sys.amiga.misc.