--- /dev/null
+static char Copyright[] =
+ "Copyright (c) 1995-2006 by Arkkra Enterprises.\nAll rights reserved.\n";
+
+/* main for "Mup" music publication program */
+
+/*
+ * Command line arguments
+ * -cN Combine strings of N measures of rest into multirests (N > 1)
+ * -C Include comments in macro-processed output (with -E)
+ * -dn turns on debug level n
+ * 1 = yydebug
+ * 2 = parse phase high level trace
+ * 4 = parse phase low level trace
+ * 8 = reserved
+ * 16 = placement phase high level trace
+ * 32 = placement phase low level trace
+ * 64 = reserved
+ * 128 = print contents of main linked list
+ * 256 = print phase high level trace
+ * 512 = print phase low level trace
+ * This is a bitmap, so multiple levels can be on at once
+ * -DMACRO[=def] define a macro
+ * -e errfile write error output into errfile instead of stderr
+ * -E just do macro expansion
+ * -f file write output to file instead of stdout
+ * -F write output to file, deriving the name
+ * -m midifile generate MIDI output into specified file instead of the
+ * usual PostScript output to stdout.
+ * -M create MIDI file, deriving the file name
+ * -olist print only pages given in list
+ * -pN start numbering pages at N instead of from 1
+ * -r print registration form
+ * -slist print only the staffs in list
+ * -v print verion number and exit
+ * -xN,M extract just measures N through M.
+ * Negative values are relative to the end of the song.
+ * The comma and second number are optional.
+ *
+ * Expects zero or more input files. If no file specified, reads stdin
+ *
+ * Exit code is 0 on success, or the number of errors found, up to 254,
+ * or 255 for internal error.
+ */
+
+
+#ifdef __WATCOMC__
+#include <io.h>
+#endif
+#ifdef Mac_BBEdit
+#include <Files.h>
+#include <Folders.h>
+#include <MupInterface.h>
+#define main _mup /* rename entry point to _mup */
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include "defines.h"
+#include "globals.h"
+
+
+/* List of valid command line options and their explanations */
+struct Options {
+ char option_letter;
+ char *argument; /* describes the arg if any, or "" if none */
+ char *explanation;
+} Option_list[] = {
+ { 'c', " N", "combine N or more measures of rests into multirests" },
+ { 'C', "", "include comments in macro preprocessor output" },
+ { 'd', " N", "turn on debug level N" },
+ { 'D', " MACRO[=macro_def]", "define macro" },
+ { 'e', " errfile", "write error messages to errfile" },
+ { 'E', "", "run macro preprocessor only" },
+ { 'f', " outfile", "write output to outfile" },
+ { 'F', "", "write output to file with derived name" },
+ { 'm', " midifile", "generate MIDI output file" },
+ { 'M', "", "generate MIDI output file, derive file name" },
+ { 'o', " pagelist", "only print pages in pagelist" },
+ { 'p', " N", "start numbering pages at N" },
+ { 'r', "", "print shareware REGISTRATION form" },
+ { 's', " stafflist", "print only staffs in stafflist" },
+ { 'v', "", "print version number" },
+ { 'x', " N[,M]", "extract measures N through M" }
+};
+
+
+#ifndef _UNISTD_H
+/* to process command line args */
+extern int getopt P((int argc, char * const *argv, const char *optstr));
+extern int optind; /* set by getopt to point to current cmd line argument */
+extern char *optarg; /* set by getopt */
+extern char *getenv();
+#endif
+extern FILE *yyout; /* lex could try to write error output here */
+
+static char **Arglist; /* global pointer to argv */
+static int Num_args; /* global copy of argc */
+static char Version[] = "5.3"; /* Mup version number */
+static int Got_e_option = NO; /* was there a -e option on the command line? */
+
+/* The different kinds of things that can be argument to -o option.
+ * If no -o option, then it will be PG_ALL, "odd" and "even" will
+ * map to PG_ODD and PG_EVEN, and a list of page numbers or ranges to PG_LIST.
+ */
+static int Pglist_type;
+#define PG_LIST 0
+#define PG_ODD 1
+#define PG_EVEN 2
+#define PG_ALL 3
+
+#ifdef O_BINARY
+static char * Read_mode = "rb";
+#else
+static char * Read_mode = "r";
+#endif
+
+/* If there is a list of pages to print using -o, the values are stored
+ * in a list of RANGELIST structs. This points to that list. The "all" field
+ * of the struct is unused.
+ */
+static struct RANGELIST *Page_range;
+
+static void usage P((char **argv)); /* print usage message and exit */
+static int ignore_option P((int opt));
+static void notice P((void));
+static void first_msg P((char *pname));
+static void setvflag P((char *fname));
+static void registration P((void));
+static char *derive_file_name P((char *suffix));
+static int get_first_page P((int pagenum));
+static void set_pagelist P((char *pagelist, int startpage));
+static void prune_page_range P((int start_page));
+static void vis_staffs P((char *stafflist));
+
+
+\f
+
+int
+main(argc, argv)
+
+int argc;
+char **argv;
+
+{
+ int a; /* for command line args */
+ char *midifilename = (char *) 0;
+ int combine = NORESTCOMBINE; /* number of measures to combine into
+ * multirests with -c option */
+ int derive_out_name = NO; /* YES is -F option is specified */
+ char *vis_stafflist = (char *) 0; /* -s list of visible staffs */
+ int pagenum;
+ char *pagelist = 0;
+ int start = 1, end = -1; /* Arguments to -x option */
+ int has_x_arg = NO;
+ int outfile_args = 0; /* we only allow one instance of [fFmM] options */
+ int n, i;
+ int num_options;
+ char *getopt_string;
+
+
+
+ notice();
+ first_msg(argv[0]);
+ /* must init head shapes table before first call to initstructs */
+ init_symtbl();
+
+ /* set initial page number to "not set" */
+ pagenum = MINFIRSTPAGE - 1;
+ initstructs();
+
+ /* If run via mupmate, user may not understand error messages
+ * about things like -c or -p, so we give different messages. */
+ Mupmate = (getenv("MUPMATE") == 0 ? NO : YES);
+
+ /* process command line arguments */
+ /* create getopt string */
+ num_options = NUMELEM(Option_list);
+ /* allow for worst case of all requiring colon */
+ MALLOCA(char, getopt_string, 2 * num_options + 1);
+ for (n = i = 0; n < num_options; n++) {
+ if (ignore_option( (int) Option_list[n].option_letter) == YES) {
+ continue;
+ }
+ getopt_string[i] = Option_list[n].option_letter;
+ if (Option_list[n].argument[0] != '\0') {
+ getopt_string[++i] = ':';
+ }
+ i++;
+ }
+ getopt_string[i] = '\0';
+
+ while ((a = getopt(argc, argv, getopt_string)) != EOF) {
+
+ switch (a) {
+
+ case 'c':
+ combine = atoi(optarg);
+ if (combine < MINRESTCOMBINE || combine > MAXRESTCOMBINE) {
+ if (Mupmate == YES) {
+ /* Should be impossible to get here,
+ * since mupmate refuses to accept
+ * out of range values. */
+ l_yyerror(0, -1, "Run > Set Options > Min measures to combine: value must be between %d and %d.",
+ MINRESTCOMBINE, MAXRESTCOMBINE);
+ }
+ else {
+ l_yyerror(0, -1, "argument for %cc (number of measures to combine) must be between %d and %d",
+ Optch, MINRESTCOMBINE, MAXRESTCOMBINE);
+ }
+ }
+ break;
+
+ case 'C':
+ Ppcomments = YES;
+ break;
+
+ case 'd':
+ Debuglevel = (int) strtol(optarg, (char **) 0, 0);
+ break;
+
+ case 'e':
+ if (freopen(optarg, "w", stderr) == (FILE *) 0) {
+ cant_open(optarg);
+ }
+ Got_e_option = YES;
+ break;
+
+ case 'E':
+ Preproc = YES;
+ break;
+
+ case 'f':
+ Outfilename = optarg;
+ outfile_args++;
+ break;
+
+ case 'F':
+ derive_out_name = YES;
+ outfile_args++;
+ break;
+
+ case 'D':
+ cmdline_macro(optarg);
+ break;
+
+ case 'm':
+ midifilename = optarg;
+ /* FALLTHRU */
+ case 'M':
+ Doing_MIDI = YES;
+ /* define "built-in" MIDI macro */
+ cmdline_macro("MIDI");
+ outfile_args++;
+ break;
+
+ case 'o':
+ pagelist = optarg;
+ break;
+
+ case 'p':
+ pagenum = atoi(optarg);
+ if (pagenum < MINFIRSTPAGE || pagenum > MAXFIRSTPAGE) {
+ if (Mupmate == YES) {
+ /* Should be impossible to get here,
+ * since mupmate refuses to accept
+ * out of range values. */
+ l_yyerror(0, -1, "Run > Set Options > First Page: value must be between %d and %d.",
+ MINFIRSTPAGE, MAXFIRSTPAGE);
+ }
+ else {
+ l_yyerror(0, -1, "argument for %cp (first page) must be between %d and %d",
+ Optch, MINFIRSTPAGE, MAXFIRSTPAGE);
+ }
+ }
+ break;
+
+ case 'r':
+ registration();
+ exit(0);
+ /*NOTREACHED*/
+ break;
+
+ case 's':
+ vis_stafflist = optarg;
+ break;
+
+ case 'v':
+ /* if got "-e errfile -v" then we want to put the
+ * opening notice into the errfile. This can allow
+ * another program to execute "mup -e errfile -v"
+ * and then use the contents of errfile as the
+ * text for an "About Mup" informational block.
+ */
+ if (Got_e_option == YES) {
+ notice();
+ }
+
+ (void) fprintf(stderr,"Version %s\n", Version);
+ exit(0);
+ /*NOTREACHED*/
+ break;
+
+ case 'x':
+ chk_x_arg(optarg, &start, &end);
+ has_x_arg = YES;
+ break;
+
+ default:
+ usage(argv);
+ break;
+ }
+ }
+
+ if (Ppcomments == YES && Preproc == NO) {
+ warning("-C only valid with -E; ignored");
+ }
+
+ if (outfile_args > 1) {
+ (void) fprintf(stderr, "Only one output file option (-f, -F, -m, -M) can be specified\n");
+ exit(1);
+ }
+
+ /* turn on yacc debug flag if appropriate */
+ if (Debuglevel & 1) {
+ yydebug = 1;
+ ifdebug = 1;
+ }
+
+ /* save info about arguments so yywrap can open additional input files
+ * if necessary */
+ Arglist = argv;
+ Num_args = argc;
+ yyin = stdin;
+ yyout = stderr;
+
+ /* if file argument, open that, else use stdin */
+ if (optind <= argc - 1) {
+ (void) yywrap();
+ }
+ else {
+#ifdef Mac_BBEdit
+ Curr_filename = _mup_input_filename;
+#else
+ Curr_filename = "stdin";
+#if defined(unix) || defined(__WATCOM__)
+ /* Sometimes people forget to give a file name,
+ * then wonder why Mup is "hanging," so let user
+ * know it isn't hanging... it's waiting for them
+ * to type something. But only if input is a terminal,
+ * and stderr is a terminal--if stdin is a pipe,
+ * user probably doesn't need a reminder. */
+ if (isatty(0) && isatty(2)) {
+ fprintf(stderr, "No input file specified; reading standard input.\n\n");
+ }
+#endif
+#endif
+ }
+
+ /* initialize for parser */
+ raterrfuncp = doraterr;
+ initstructs();
+ vis_staffs(vis_stafflist);
+ reset_ped_state();
+
+ /* parse the input */
+ if (Preproc == YES) {
+ preproc();
+ }
+ else {
+ (void) yyparse();
+ }
+#ifndef UNIX_LIKE_FILES
+ mac_cleanup();
+#endif
+
+
+ /* do final checks and cleanup of input data */
+ /* check for missing endif */
+ chk_ifdefs();
+ if (Preproc == YES) {
+ error_exit();
+ }
+
+ /* find height of headers and footers */
+ /* Note: this has to be called when we are at the *end* of the main
+ * list with all SSVs applied, so that we know the margin settings */
+ calc_block_heights();
+ /* make sure there is a final barline */
+ check4barline_at_end();
+ /* make sure we go to new score if visibility changes */
+ chk_vis_feed();
+
+ /* derive tabnote staff data for tablature staffs. But if there
+ * have been errors found, don't bother, because we may have
+ * some incomplete/inconsistent data that tab2tabnote doesn't
+ * know how to deal with cleanly. */
+ if (Errorcount == 0) {
+ tab2tabnote();
+ }
+
+ /* do -c option or restcombine parameter */
+ combine_rests(combine);
+
+ /* make sure there aren't til clauses past end of song */
+ chk4dangling_til_clauses("the end of the song");
+
+ /* count how many verses */
+ set_maxverses();
+
+ /* process ties */
+ tie();
+
+ /* Verify that -o argument (and maybe -p or firstpage parameter)
+ * is valid. If not, this will ufatal. */
+ pagenum = get_first_page(pagenum);
+ set_pagelist(pagelist, pagenum);
+
+ /* Do -x (extract) option if needed. But if there were errors before,
+ * skip this, because there could be empty measures and such,
+ * that could confuse it, and we're going to give up soon anyway. */
+ if (has_x_arg == YES && Errorcount == 0) {
+ extract(start, end);
+ }
+
+ debug(2, "finished with parsing, Errorcount is %d", Errorcount);
+
+ if (Errorcount > 0) {
+ (void) fprintf(stderr, "\nstopping due to previous error%s\n",
+ Errorcount ? "s" : "");
+ error_exit();
+ }
+
+ /* do the placement phase */
+
+ /* initialize the Staffscale and related variables to default values */
+ initstructs();
+ set_staffscale(0);
+
+ /* transpose */
+ transgroups();
+
+ /* set up ties that carry into next measure */
+ tie_carry();
+
+ /* line up chords */
+ makechords();
+
+ /* place notes relative to staff and set stem direction */
+ setnotes();
+ /* find relative horizontal position of notes */
+ setgrps();
+ /* set coordinates of rests and syllables */
+ restsyl();
+
+ /* generate MIDI file if appropriate. We wait until here to
+ * do MIDI, so that chord widths have been established, so midi
+ * code can more easily figure out how to crunch all-space chords */
+ if (Doing_MIDI == YES) {
+ if (midifilename == (char *) 0) {
+ /* -M option, so we have to derive the name */
+ midifilename = derive_file_name(".mid");
+ }
+ gen_midi(midifilename);
+ exit(0);
+ }
+
+ /* figure out absolute horizontal locations */
+ abshorz();
+ /* find lengths of beams, angles of beams, etc */
+ beamstem();
+ /* set up mussym, octave, rom, bold, pedal, etc */
+ stuff();
+
+ /* find vertical coordinates relative to staff */
+ relvert();
+ /* set absolute vertical coordinates */
+ absvert();
+
+ /* split lines and curves */
+ fix_locvars();
+
+ print_mainll();
+
+ if (derive_out_name == YES) {
+ Outfilename = derive_file_name(".ps");
+ }
+ if (*Outfilename != '\0') {
+ if (freopen(Outfilename, "w", stdout) == (FILE *) 0) {
+ cant_open(Outfilename);
+ exit(1);
+ }
+ }
+
+ /* output PostScript for printing */
+ prune_page_range(pagenum);
+ do {
+ Pagenum = (short) pagenum;
+ print_music();
+ } while (Page_range != (struct RANGELIST *) 0);
+ trailer();
+
+ /* if we get to here, all is okay. If there was a problem,
+ * we would have exited where the problem occurred */
+ return(0);
+}
+\f
+
+/* print copyright notice */
+
+static void
+notice()
+
+{
+ if (getenv("MUPQUIET") == (char *) 0 || Got_e_option == YES) {
+ fprintf(stderr, "Mup - Music Publisher Version %s\n", Version);
+ fprintf(stderr, Copyright);
+ }
+}
+\f
+
+/* print registration form */
+
+static void
+registration()
+{
+ printf("Mup is SHAREWARE. You can try out a copy for free, but if you decide\n");
+ printf("to keep and use it, you must register by filling out the form below\n");
+ printf("and sending the form and cash, check, or money order to:\n");
+ printf(" Arkkra Enterprises\n");
+ printf(" P. O. Box 315\n");
+ printf(" Warrenville, IL 60555 USA\n");
+ printf("\nName______________________________________________________________\n\n");
+ printf("Address___________________________________________________________\n\n");
+ printf("City_____________________________ State/Province__________________\n\n");
+ printf("Zip code/Postal code_____________________ Country_________________\n\n");
+ printf("Email address (please print clearly)______________________________\n\n");
+ printf("How did you find out about Mup?___________________________________\n\n");
+ printf("__________________________________________________________________\n\n");
+ printf("___Linux ___ Windows/MS-DOS ___Mac ___Other____________________\n\n");
+ printf("Would you like to join the Mup users mailing list? ___ Yes ___ No\n\n");
+ printf("___ Mup Version %s Registrations.........................$29 each\n", Version);
+ printf("\t\t\t(Illinois residents, add $2.18 sales tax)\n");
+ printf("(For credit card payment, see http://www.arkkra.com/doc/credtcrd.html)\n");
+}
+\f
+
+/* print usage message and exit */
+
+static void
+usage(argv)
+
+char **argv;
+
+{
+ int num_options; /* how many options */
+ int n;
+ char *whitespace; /* for lining things up */
+ int white_length; /* strlen(whitespace) */
+ int length; /* of an argument item */
+ char *extra_options; /* parent process can ask us to print more */
+
+
+ /* print the usage summary */
+ fprintf(stderr, "usage: %s ", argv[0]);
+ num_options = NUMELEM(Option_list);
+ for (n = 0; n < num_options; n++) {
+ if (ignore_option( (int) Option_list[n].option_letter) == YES) {
+ /* ignore this option */
+ continue;
+ }
+ fprintf(stderr, "[%c%c%s] ", Optch,
+ Option_list[n].option_letter, Option_list[n].argument);
+ }
+ fprintf(stderr, "[file...]\n");
+
+ /* We'll add as much of this whitespace string to each argument
+ * item as needed to line the explanations up nicely. */
+ whitespace = " ";
+ white_length = strlen(whitespace);
+
+ /* print the explanations of each option */
+ for (n = 0; n < num_options; n++) {
+
+ if (ignore_option( (int) Option_list[n].option_letter) == YES) {
+ continue;
+ }
+
+ fprintf(stderr, " %c%c%s", Optch,
+ Option_list[n].option_letter, Option_list[n].argument);
+
+ /* add enough white space to line things up */
+ if ((length = strlen(Option_list[n].argument)) < white_length) {
+ fprintf(stderr, whitespace + length);
+ }
+
+ fprintf(stderr, " %s\n", Option_list[n].explanation);
+ }
+ /* If calling program tells us to add some options to the list,
+ * print those out too. */
+ if ((extra_options = getenv("MUPADDOP")) != (char *) 0) {
+ fprintf(stderr, "%s", extra_options);
+ }
+
+ exit(1);
+}
+\f
+
+/* If Mup is being called by some other program, like mupdisp,
+ * such that some of Mup's options should be disallowed, it
+ * should set $MUPDELOP to the list of options to be deleted
+ * from the list of valid options. This function will say, for the
+ * given option, whether it should be disallowed. */
+
+static int
+ignore_option(opt)
+
+int opt; /* an option letter */
+
+{
+ static char *del_options = 0; /* which options to delete from list */
+
+ /* the first time we are called, get the list, if any */
+ if (del_options == (char *) 0) {
+ if ((del_options = getenv("MUPDELOP")) == (char *) 0) {
+ del_options = "";
+ }
+ }
+
+ return ((strchr(del_options, opt) != (char *) 0) ? YES : NO);
+}
+\f
+
+/* print message to display first time program is executed if appropriate.
+ * If a particular magic file exists, we know
+ * (or at least assume) the user has already seen the
+ * message about Mup being shareware. If not, we print the message and
+ * exit. For unix, the magic file is called .mup and must be either in the
+ * current directory or in $HOME. For DOS, it is called mup.ok and must be
+ * either in the current directory or in the directory where mup.exe was
+ * executed, as indicated by argv[0]. */
+
+#ifndef MAGIC_FILE_NAME
+#define MAGIC_FILE_NAME (char *) 0
+#endif
+
+int check = 100, *Check_p = ✓
+
+static void
+first_msg(pname)
+
+char *pname; /* argv[0] */
+
+{
+ char *fname; /* name of magic file */
+ char *home; /* $HOME (unix) or where mup is located (DOS) */
+ char *path = (char *) 0;/* home/fname */
+
+
+ fname = MAGIC_FILE_NAME;
+
+ if (fname == (char *) 0) {
+ fprintf(stderr, "\tMup is shareware. You may try it out for free, but if you\n");
+ fprintf(stderr, "\tdecide to keep it, you must pay a registration fee of $29.\n");
+ fprintf(stderr, "\tThis copy of Mup was compiled for an unrecognized Operating System\n");
+ fprintf(stderr, "\tor compiler. If you have a UNIX-like operating system,\n");
+ fprintf(stderr, "\tyou can try compiling with -Dunix, or if you have as MS-DOS-like\n");
+ fprintf(stderr, "\tOperating system, you can try compiling with -D__DOS__\n");
+ fprintf(stderr, "\tIf that still doesn't work, to suppress this message,\n");
+ fprintf(stderr, "\tand start using Mup, modify defines.h to define MAGIC_FILE_NAME\n");
+ fprintf(stderr, "\tto a name that is appropriate for your operating system.\n");
+ fprintf(stderr, "\tBy doing so, you acknowledge that you have read\n");
+ fprintf(stderr, "\tthe Mup license and agree to its terms,\n");
+ fprintf(stderr, "\tand agree that if you decide to continue to use Mup\n");
+ fprintf(stderr, "\tafter trying it out, you will pay the registration fee.\n");
+ fprintf(stderr, "\tAfter changing MAGIC_FILE_NAME or any other related #defines\n");
+ fprintf(stderr, "\tthat you might need, and recompiling, execute\n");
+ fprintf(stderr, "\t\tmup -r\n\tto get a registration form. If you let us know about any changes\n");
+ fprintf(stderr, "\tyou need to make to support your OS, we will consider\n");
+ fprintf(stderr, "\tincorporating those changes in a future Mup release.\n");
+ exit(1);
+ }
+
+ /* if magic file exists in current directory,
+ * indicating user has already seen the message, return */
+#ifndef Mac_BBEdit
+ if (access(fname, 0) == 0) {
+ setvflag(fname);
+ if (Vflag == YES) {
+ return;
+ }
+ else {
+ check = 100;
+ }
+ }
+#endif
+
+#ifdef MAGIC_FILE_HOME
+ /* construct pathname to magic file if it is in $HOME */
+ if ((home = getenv("HOME")) != (char *) 0) {
+ MALLOCA(char, path, strlen(home)+ strlen(fname) + 2);
+#ifdef VMS
+ (void) sprintf(path, "%s%s", home, fname);
+#else
+ (void) sprintf(path, "%s/%s", home, fname);
+#endif
+ }
+#else
+#ifdef __DOS__
+ /* construct pathname to magic file if it is in the directory where
+ * mup.exe came from */
+ if ((home = strrchr(pname, '\\')) != (char *) 0) {
+ int baselength; /* strlen up through last \ */
+
+ baselength = home - pname + 1;
+ MALLOCA(char, path, baselength + strlen(fname) + 1);
+ /* copy pname up to last backslash */
+ strncpy(path, pname, baselength);
+ /* add magic file name */
+ strcpy(path + baselength, fname);
+ }
+#endif
+#endif
+#ifdef Mac_BBEdit
+#pragma unused(pname)
+ /* check for file in Preferences folder inside System folder */
+ path = 0;
+ home = 0;
+ {
+ short vRefNum;
+ long dirID;
+ FSSpec fsSpec;
+
+ if (FindFolder(kOnSystemDisk, kPreferencesFolderType, false, &vRefNum, &dirID) == noErr)
+ /* preferences folder exists */
+ if (FSMakeFSSpec(vRefNum, dirID, (StringPtr) MupRegFileName, &fsSpec) == noErr) { /* file exists */
+ short old_vRefNum;
+ long old_dirID;
+ if (HGetVol((StringPtr) 0, &old_vRefNum, &old_dirID) != noErr) return;
+ if (HSetVol((StringPtr) 0, vRefNum, dirID) != noErr) return;
+ setvflag(fname);
+ HSetVol((StringPtr) 0, old_vRefNum, old_dirID);
+ return;
+ }
+ }
+#else
+ /* check for file in $HOME or where mup.exe came from */
+ if (path != (char *) 0 && access(path, 0) == 0) {
+ setvflag(path);
+ return;
+ }
+#endif
+
+ /* print shareware message and exit */
+ fprintf(stderr, "\n\tMup is shareware. You may try it out for free, but if you\n");
+ fprintf(stderr, "\tdecide to keep it, you must pay a registration fee of $29.\n");
+ fprintf(stderr, "\n\tTo use Mup, first create a file called %s\n", fname);
+#ifdef Mac_BBEdit
+ fprintf(stderr, "\tin the Preferences folder inside the system folder");
+#else
+ fprintf(stderr, "\tin the current directory");
+#endif
+ if (path == (char *) 0) {
+ fprintf(stderr, ".\n");
+ }
+ else {
+ fprintf(stderr, " or at %s\n", path);
+ }
+ fprintf(stderr, "\t(It can be zero length. It just has to exist.)\n");
+ fprintf(stderr, "\tBy creating this file, you acknowledge that you have read\n");
+ fprintf(stderr, "\tthe Mup license and agree to its terms, and agree that\n");
+ fprintf(stderr, "\tif you decide to continue to use Mup after trying it out,\n\tyou will pay the registration fee.\n");
+#ifdef Mac_BBEdit
+ fprintf(stderr, "\n\tAfter creating this file, select\n\t'Registration' from the Mup dialog box\n\tto get a registration form.\n\n");
+#else
+ fprintf(stderr, "\n\tAfter creating this file, execute\n\t\tmup %cr\n\tto get a registration form.\n\n", Optch);
+#endif
+ exit(0);
+}
+\f
+
+static void
+setvflag(fname)
+char *fname;
+{
+ int f;
+ char buff[48];
+ int sum = 0;
+ int hash = 0x45;
+ int n = 16;
+ int i;
+
+ if ((f = open(fname, O_RDONLY, 0)) > 0) {
+ if (read(f, buff, n) == n) {
+ for (i = 0; i < n; i++) {
+ sum += buff[i];
+ hash ^= (buff[i] ^ sum);
+ check ^= (buff[i] << (1 + (i & 3)));
+ }
+ Vflag = (((sum == 02703) && (hash == 02146)) ? YES : NO);
+ }
+ }
+ (void) close(f);
+}
+\f
+
+/* make our own yywrap rather than use the one in the lex library.
+ * In case user specifies more than one file, open
+ * each in turn, and return control to lex */
+
+int
+yywrap()
+
+{
+ int leng = 0; /* Length of file name. Initialization done solely
+ * to avoid bogus "used before set" warning. */
+
+ /* return from any macros or includes */
+ if (popfile() == 1) {
+ return(0);
+ }
+
+ /* if user specified more files, open the next one */
+ for ( ; optind < Num_args; optind++) {
+ if (yyin != NULL) {
+ (void) fclose(yyin);
+ }
+ errno = 0;
+ if ((yyin = fopen(Arglist[optind], Read_mode)) != NULL) {
+ Curr_filename = Arglist[optind++];
+ yylineno = 1;
+ return(0);
+ }
+ /* If name doesn't already end with .mup or .MUP and the open
+ * failed because the file didn't exist, try the name with
+ * .mup appended. */
+ else if (
+#ifdef ENOENT
+ errno == ENOENT &&
+#endif
+ ( ((leng = strlen(Arglist[optind])) < 5) ||
+ (strcmp(Arglist[optind] + leng - 4, ".mup") != 0 &&
+ strcmp(Arglist[optind] + leng - 4, ".MUP") != 0
+ )) ) {
+ MALLOCA(char, Curr_filename, leng + 5);
+ sprintf(Curr_filename, "%s.mup", Arglist[optind]);
+ if ((yyin = fopen(Curr_filename, Read_mode)) != NULL) {
+ yylineno = 1;
+ optind++;
+ return(0);
+ }
+ /* try upper case suffix before giving up */
+ sprintf(Curr_filename, "%s.MUP", Arglist[optind]);
+ if ((yyin = fopen(Curr_filename, Read_mode)) != NULL) {
+ yylineno = 1;
+ optind++;
+ return(0);
+ }
+ FREE(Curr_filename);
+ }
+ cant_open(Arglist[optind]);
+ }
+
+ return(1);
+}
+\f
+
+/* If user used -M or -F option, we need to derive the output file name.
+ * Use the last input file name, strip off the trailing .mup if it is there,
+ * add the suffix, and return the derived name.
+ */
+
+static char *
+derive_file_name(suffix)
+
+char *suffix; /* ".mid" or ".ps" */
+
+{
+ int length; /* of Curr_filename */
+ char *file_name; /* the name we derive */
+ char *suffix_location; /* where the suffix will go */
+
+
+ length = strlen(Curr_filename);
+ MALLOCA(char, file_name, length + strlen(suffix) + 1);
+
+ /* start with the original Mup input file name */
+ strcpy(file_name, Curr_filename);
+
+ /* see if we need to strip off a .mup */
+ if (length > 3) {
+ /* find where the .mup would start if it is there */
+ suffix_location = file_name + length - 4;
+
+ /* If user used upper case, so will we */
+ if (strcmp(suffix_location, ".MUP") == 0) {
+ if (strcmp(suffix, ".mid") == 0) {
+ suffix = ".MID";
+ }
+ else if (strcmp(suffix, ".ps") == 0) {
+ suffix = ".PS";
+ }
+ else {
+ pfatal("derive_file_name() called with unknown suffix '%s'", suffix);
+ }
+ }
+ else if (strcmp(suffix_location, ".mup") != 0) {
+ /* no .mup to strip off; just add to the end */
+ suffix_location = file_name + length;
+ }
+ }
+ else {
+ suffix_location = file_name + length;
+ }
+
+ /* append the suffix and return the derived name */
+ strcpy(suffix_location, suffix);
+ return(file_name);
+}
+\f
+
+/* Determine the first page number. If user used -p option, use that,
+ * otherwise get from first_page parameter, else use 1. */
+
+static int
+get_first_page(pagenum)
+
+int pagenum; /* from -p option */
+
+{
+ struct MAINLL *m_p;
+
+ /* if there wasn't a -p value, figure out what to use for first page */
+ if (pagenum < MINFIRSTPAGE) {
+ /* default to page 1 */
+ pagenum = 1;
+
+ /* look for last setting of firstpage parameter before
+ * any STAFFs */
+ initstructs();
+ for (m_p = Mainllhc_p; m_p != 0; m_p = m_p->next) {
+ if (m_p->str == S_SSV) {
+ if (m_p->u.ssv_p->used[FIRSTPAGE] == YES) {
+ pagenum = m_p->u.ssv_p->firstpage;
+ }
+ }
+ else if (m_p->str == S_STAFF) {
+ break;
+ }
+ }
+ }
+ return(pagenum);
+}
+\f
+
+/* Parse the argument to -o, if there is a -o option specified, and
+ * save the info away for later use. Gives error if argument is invalid.
+ */
+
+static void
+set_pagelist(pagelist, startpage)
+
+char *pagelist;
+int startpage; /* from -p option */
+
+{
+ if (pagelist == (char *) 0) {
+ /* no -o option, print all pages */
+ Pglist_type = PG_ALL;
+ }
+
+ else if (strcmp(pagelist, "odd") == 0) {
+ Pglist_type = PG_ODD;
+ }
+
+ else if (strcmp(pagelist, "even") == 0) {
+ Pglist_type = PG_EVEN;
+ }
+
+ else {
+ struct RANGELIST *new_range;
+ struct RANGELIST **linkpoint_p_p;/* tail of Page_range list,
+ * for linking to the end
+ * of the list */
+ char *p, *beyondnum; /* for parsing the numbers */
+ short lower, upper; /* page number range */
+
+
+ Pglist_type = PG_LIST;
+
+ /* Parse the argument to -o and save the ranges */
+ /* first set up where to link onto tail of list */
+ linkpoint_p_p = &Page_range;
+
+ /* walk through the -o argument */
+ for (p = pagelist; *p != '\0'; ) {
+
+ /* skip any leading white space */
+ while (isspace(*p)) {
+ p++;
+ }
+
+ /* get page number (which may or may not be the
+ * start of a range of numbers) */
+ lower = (short) strtol(p, &beyondnum, 10);
+ if (beyondnum == p || lower <= 0 || lower < startpage) {
+ /* bad number from user, jump to error out */
+ break;
+ }
+
+ p = beyondnum;
+ /* skip any white space */
+ while (isspace(*p)) {
+ p++;
+ }
+
+ if (*p == '-') {
+ /* there is a range of page numbers. Get the
+ * upper limit of the range */
+ upper = (short) strtol(++p, &beyondnum, 10);
+ if (beyondnum == p || upper <= 0
+ || upper < lower) {
+ /* bad value from user */
+ break;
+ }
+ p = beyondnum;
+ while (isspace(*p)) {
+ p++;
+ }
+ if (*p == ',') {
+ p++;
+ }
+ else if (*p != '\0') {
+ break;
+ }
+ }
+ else if (*p == ',') {
+ /* not a range, so treat like range of n-n */
+ upper = lower;
+ p++;
+ }
+ else if (*p == '\0') {
+ upper = lower;
+ }
+ else {
+ /* something other than dash, comma, or end of
+ * string, which is a user error */
+ break;
+ }
+
+ /* save info about this page range */
+ MALLOC(RANGELIST, new_range, 1);
+ new_range->begin = lower;
+ new_range->end = upper;
+ new_range->next = (struct RANGELIST *) 0;
+
+ /* link onto tail of list */
+ *linkpoint_p_p = new_range;
+ linkpoint_p_p = &(new_range->next);
+ }
+
+ /* if jumped out of loop without finishing parsing, user
+ * gave us something we didn't understand */
+ if (*p != '\0') {
+ if (Mupmate == YES) {
+ l_yyerror(0, -1, "Run > Set Options > Pages to display: value is invalid.");
+ }
+ else {
+ l_yyerror(0, -1, "argument for -o (list of pages to display) is invalid");
+ }
+ }
+ }
+}
+\f
+
+/* Calculate the page number for the final page and put it in Last_pagenum.
+ * If there is a -o list, make sure all the
+ * pages listed on the -o list are less than that. If they aren't remove them
+ * from the list. Without this step, Mup could go into a loop trying to print
+ * a page that doesn't exist. */
+
+static void
+prune_page_range(start_page)
+
+int start_page; /* number given to the first page via the -p option
+ * or via the firstpage parameter */
+
+{
+ struct MAINLL *mll_p; /* to count page feeds */
+ struct RANGELIST **range_p_p;
+ int pruned; /* if we removed anything from list */
+
+ /* find the largest page number */
+ Last_pagenum = start_page;
+ for (mll_p = Mainllhc_p; mll_p != (struct MAINLL *) 0; mll_p = mll_p->next) {
+ if (mll_p->str == S_FEED && mll_p->u.feed_p->pagefeed == YES) {
+ Last_pagenum++;
+ }
+ }
+
+ /* If there are extra pages for gridsatend, add those on */
+ if (Atend_info.separate_page == YES) {
+ int grids_per_page;
+
+ grids_per_page = Atend_info.grids_per_row *
+ Atend_info.rows_per_page;
+ /* round up */
+ Last_pagenum += (Atend_info.grids_used + grids_per_page - 1)
+ / grids_per_page;
+ }
+
+
+ if (Pglist_type != PG_LIST) {
+ return;
+ }
+
+ /* see if any items in Page_range are bigger
+ * than the biggest page number */
+ pruned = NO;
+ for (range_p_p = &Page_range; *range_p_p != (struct RANGELIST *) 0;
+ range_p_p = &((*range_p_p)->next) ) {
+ if ((*range_p_p)->begin > Last_pagenum) {
+ /* need to get rid of this entire entry, because none
+ * of the pages listed actually exist */
+ pruned = YES;
+ if ((*range_p_p = (*range_p_p)->next)
+ == (struct RANGELIST *) 0) {
+ /* last one on the list */
+ break;
+ }
+ }
+ else if ((*range_p_p)->end > Last_pagenum) {
+ /* just need to shorten this range */
+ (*range_p_p)->end = Last_pagenum;
+ pruned = YES;
+ }
+ }
+
+ if (pruned == YES) {
+ l_warning( (char *) 0, -1, "-o list included one or more pages that don't exist");
+ }
+}
+\f
+
+/* given a page number, return YES if that page should be printed now, NO
+ * if not. If user gave a list of pages to print using -o, we print the page
+ * only if it is the very first thing on the list. If there is a smaller
+ * number further on in the list, we'll do that page later on another pass.
+ * The print phase has to keep making multiple passes until the list is
+ * empty. This allows user to print things out in random order, which may
+ * be useful especially for 2-on-1 printing, where for example, you may
+ * want a 4-page "booklet", printing page 4 then page 1 on one side and
+ * pages 2 and 3 on the other side.
+ */
+
+int
+onpagelist(pagenum)
+
+int pagenum;
+
+{
+ struct RANGELIST *old_range; /* to keep track of item to free */
+
+ switch (Pglist_type) {
+
+ case PG_ALL:
+ return(YES);
+
+ case PG_ODD:
+ return (pagenum & 1) == 1 ? YES : NO;
+
+ case PG_EVEN:
+ return (pagenum & 1) == 0 ? YES : NO;
+
+ default:
+ if (Page_range == (struct RANGELIST *) 0) {
+ /* ran off the end of list, so no more to print */
+ return(NO);
+ }
+
+ if (Page_range->begin == pagenum) {
+ /* is first on list so we will print it.
+ * But first, fix up the list. If we've used up all of
+ * the current range, free it and point to the next. */
+ (Page_range->begin)++;
+ if (Page_range->begin > Page_range->end) {
+ old_range = Page_range;
+ Page_range = Page_range->next;
+ FREE(old_range);
+ }
+ return(YES);
+ }
+ break;
+ }
+ return(NO);
+}
+\f
+
+/* return YES if we were doing a page list (-o option) but have now handled
+ * all of the pages */
+
+int
+last_page()
+{
+ if (Pglist_type == PG_LIST) {
+ return ((Page_range == 0) ? YES : NO);
+ }
+ else {
+ return ((Pagenum == Last_pagenum) ? YES : NO);
+ }
+}
+\f
+
+/* handle the argument to -s (list of staffs to make visible). For each
+ * visible staff, make an SSV marking it visible */
+
+static void
+vis_staffs(stafflist)
+
+char *stafflist;
+{
+ int s; /* staff index */
+ int v; /* voice index */
+ long start, end; /* staff range */
+
+
+ if (stafflist == (char *) 0) {
+ /* user didn't use -s, so set to all visible */
+ for (s = 1; s <= MAXSTAFFS; s++) {
+ Staff_vis[s] = YES;
+ for (v = 1; v <= MAXVOICES; v++) {
+ Voice_vis[s][v] = YES;
+ }
+ }
+ return;
+ }
+
+ /* init to all invisible */
+ for (s = 1; s <= MAXSTAFFS; s++) {
+ Staff_vis[s] = NO;
+ for (v = 1; v <= MAXVOICES; v++) {
+ Voice_vis[s][v] = NO;
+ }
+ }
+
+ for ( ; *stafflist != '\0'; ) {
+ /* get first staff number in list. Will error check below */
+ start = strtol(stafflist, &stafflist, 10);
+
+ if (*stafflist == '-') {
+ /* we have a range. Get end of range */
+ end = strtol(stafflist + 1, &stafflist, 10);
+ }
+ else {
+ /* single number, use end same as start */
+ end = start;
+ }
+
+ /* error check */
+ if (start < 1 || start > MAXSTAFFS || end < 1 ||
+ end > MAXSTAFFS || end < start) {
+ if (Mupmate == YES) {
+ l_yyerror(0, -1, "Run > Set Options > Staffs to display/play: value is invalid.");
+ }
+ else {
+ l_yyerror(0, -1, "invalid argument for %cs option (staffs to make visible)", Optch);
+ }
+ return;
+ }
+
+ /* see if there is a voice qualifier */
+ if (*stafflist == 'v') {
+ stafflist++;
+ switch (*stafflist) {
+ case '1':
+ v = 1;
+ break;
+ case '2':
+ v = 2;
+ break;
+ case '3':
+ v = 3;
+ break;
+ default:
+ if (Mupmate == YES) {
+ l_yyerror(0, -1, "Run > Set Options > Staffs to display/play: voice qualifier must be 1, 2, or 3.");
+ }
+ else {
+ l_yyerror(0, -1, "voice qualifier for -s option must be 1, 2, or 3");
+ }
+ return;
+ }
+ stafflist++;
+ if (*stafflist != '\0' && *stafflist != ',') {
+ if (Mupmate == YES) {
+ l_yyerror(0, -1, "Run > Set Options > Staffs to display/play: invalid voice qualifier. (Maybe missing comma?)");
+ }
+ else {
+ l_yyerror(0, -1, "invalid voice qualifier for -s option (missing comma?)");
+ }
+ return;
+ }
+ }
+ else {
+ /* no voice qualifier */
+ v = 0;
+ }
+
+ /* mark all staffs in range as visible */
+ for ( ; start <= end; start++) {
+ Staff_vis[start] = YES;
+ if (v != 0) {
+ Voice_vis[start][v] = YES;
+ }
+ else {
+ /* no voice qualifier, so all voices are visible */
+ int vn;
+ for (vn = 1; vn <= MAXVOICES; vn++) {
+ Voice_vis[start][vn] = YES;
+ }
+ }
+ }
+
+ /* if comma for another range, skip past it */
+ if (*stafflist == ',') {
+ stafflist++;
+ }
+ }
+}
+\f
+
+#ifdef NEED_GETOPT
+/* for non-unix or other systems that don't have a getopt() function,
+ * define one here. This is NOT a general purpose implementation of getopt(),
+ * but something good enough to work with Mup */
+
+int optind = 1;
+char *optarg;
+static int argoffset;
+int opttype P((int option, char *optstring));
+
+#define NOARG 1
+#define WITHARG 2
+#define BADOPT 3
+
+int
+getopt(argc, argv, optstring)
+
+#ifdef __STDC__
+int argc;
+char * const *argv;
+const char *optstring;
+#else
+int argc;
+char **argv;
+char *optstring;
+#endif
+
+{
+ int option;
+
+
+ if (optind >= argc) {
+ return(EOF);
+ }
+
+ if (argoffset == 0) {
+#ifdef __DOS__
+ if (argv[optind][argoffset] == '-'
+ || argv[optind][argoffset] == '/') {
+#else
+ if (argv[optind][argoffset] == '-') {
+#endif
+ argoffset = 1;
+ }
+ else {
+ return(EOF);
+ }
+ }
+
+ /* determine if option is valid and if should have an argument */
+ option = argv[optind][argoffset] & 0x7f;
+ switch (opttype(option, (char *) optstring)) {
+ case NOARG:
+ /* valid option without argument. Keep track of where
+ * to look for next option */
+ if (argv[optind][++argoffset] == '\0') {
+ optind++;
+ argoffset = 0;
+ }
+ break;
+
+ case WITHARG:
+ /* valid option with argument. */
+ if (argv[optind][++argoffset] != '\0') {
+ /* argument immediately follows in same argv */
+ optarg = &(argv[optind][argoffset]);
+ optind++;
+ }
+ else {
+ /* white space. argument must be in next argv */
+ optind++;
+ if (optind >= argc) {
+ fprintf(stderr, "missing argument to %c%c option\n", Optch, option);
+ return('?');
+ }
+ optarg = &(argv[optind][0]);
+ optind++;
+ }
+ argoffset = 0;
+ break;
+
+ default:
+ fprintf(stderr, "invalid option %c%c\n", Optch, option);
+ option = '?';
+ }
+ return(option);
+}
+
+
+/* look up option in optstring and return type of option */
+
+int
+opttype(option, optstring)
+
+int option;
+char *optstring;
+
+{
+ char *p;
+
+ for (p = optstring; *p != '\0'; ) {
+ if (*p++ == option) {
+ return(*p == ':' ? WITHARG : NOARG);
+ }
+ if (*p == ':') {
+ p++;
+ }
+ }
+ return(BADOPT);
+}
+
+#endif