1 static char Copyright[] =
2 "Copyright (c) 1995-2006 by Arkkra Enterprises.\nAll rights reserved.\n";
4 /* main for "Mup" music publication program */
7 * Command line arguments
8 * -cN Combine strings of N measures of rest into multirests (N > 1)
9 * -C Include comments in macro-processed output (with -E)
10 * -dn turns on debug level n
12 * 2 = parse phase high level trace
13 * 4 = parse phase low level trace
15 * 16 = placement phase high level trace
16 * 32 = placement phase low level trace
18 * 128 = print contents of main linked list
19 * 256 = print phase high level trace
20 * 512 = print phase low level trace
21 * This is a bitmap, so multiple levels can be on at once
22 * -DMACRO[=def] define a macro
23 * -e errfile write error output into errfile instead of stderr
24 * -E just do macro expansion
25 * -f file write output to file instead of stdout
26 * -F write output to file, deriving the name
27 * -m midifile generate MIDI output into specified file instead of the
28 * usual PostScript output to stdout.
29 * -M create MIDI file, deriving the file name
30 * -olist print only pages given in list
31 * -pN start numbering pages at N instead of from 1
32 * -r print registration form
33 * -slist print only the staffs in list
34 * -v print verion number and exit
35 * -xN,M extract just measures N through M.
36 * Negative values are relative to the end of the song.
37 * The comma and second number are optional.
39 * Expects zero or more input files. If no file specified, reads stdin
41 * Exit code is 0 on success, or the number of errors found, up to 254,
42 * or 255 for internal error.
52 #include <MupInterface.h>
53 #define main _mup /* rename entry point to _mup */
61 /* List of valid command line options and their explanations */
64 char *argument; /* describes the arg if any, or "" if none */
67 { 'c', " N", "combine N or more measures of rests into multirests" },
68 { 'C', "", "include comments in macro preprocessor output" },
69 { 'd', " N", "turn on debug level N" },
70 { 'D', " MACRO[=macro_def]", "define macro" },
71 { 'e', " errfile", "write error messages to errfile" },
72 { 'E', "", "run macro preprocessor only" },
73 { 'f', " outfile", "write output to outfile" },
74 { 'F', "", "write output to file with derived name" },
75 { 'm', " midifile", "generate MIDI output file" },
76 { 'M', "", "generate MIDI output file, derive file name" },
77 { 'o', " pagelist", "only print pages in pagelist" },
78 { 'p', " N", "start numbering pages at N" },
79 { 'r', "", "print shareware REGISTRATION form" },
80 { 's', " stafflist", "print only staffs in stafflist" },
81 { 'v', "", "print version number" },
82 { 'x', " N[,M]", "extract measures N through M" }
87 /* to process command line args */
88 extern int getopt P((int argc, char * const *argv, const char *optstr));
89 extern int optind; /* set by getopt to point to current cmd line argument */
90 extern char *optarg; /* set by getopt */
91 extern char *getenv();
93 extern FILE *yyout; /* lex could try to write error output here */
95 static char **Arglist; /* global pointer to argv */
96 static int Num_args; /* global copy of argc */
97 static char Version[] = "5.3"; /* Mup version number */
98 static int Got_e_option = NO; /* was there a -e option on the command line? */
100 /* The different kinds of things that can be argument to -o option.
101 * If no -o option, then it will be PG_ALL, "odd" and "even" will
102 * map to PG_ODD and PG_EVEN, and a list of page numbers or ranges to PG_LIST.
104 static int Pglist_type;
111 static char * Read_mode = "rb";
113 static char * Read_mode = "r";
116 /* If there is a list of pages to print using -o, the values are stored
117 * in a list of RANGELIST structs. This points to that list. The "all" field
118 * of the struct is unused.
120 static struct RANGELIST *Page_range;
122 static void usage P((char **argv)); /* print usage message and exit */
123 static int ignore_option P((int opt));
124 static void notice P((void));
125 static void first_msg P((char *pname));
126 static void setvflag P((char *fname));
127 static void registration P((void));
128 static char *derive_file_name P((char *suffix));
129 static int get_first_page P((int pagenum));
130 static void set_pagelist P((char *pagelist, int startpage));
131 static void prune_page_range P((int start_page));
132 static void vis_staffs P((char *stafflist));
144 int a; /* for command line args */
145 char *midifilename = (char *) 0;
146 int combine = NORESTCOMBINE; /* number of measures to combine into
147 * multirests with -c option */
148 int derive_out_name = NO; /* YES is -F option is specified */
149 char *vis_stafflist = (char *) 0; /* -s list of visible staffs */
152 int start = 1, end = -1; /* Arguments to -x option */
154 int outfile_args = 0; /* we only allow one instance of [fFmM] options */
163 /* must init head shapes table before first call to initstructs */
166 /* set initial page number to "not set" */
167 pagenum = MINFIRSTPAGE - 1;
170 /* If run via mupmate, user may not understand error messages
171 * about things like -c or -p, so we give different messages. */
172 Mupmate = (getenv("MUPMATE") == 0 ? NO : YES);
174 /* process command line arguments */
175 /* create getopt string */
176 num_options = NUMELEM(Option_list);
177 /* allow for worst case of all requiring colon */
178 MALLOCA(char, getopt_string, 2 * num_options + 1);
179 for (n = i = 0; n < num_options; n++) {
180 if (ignore_option( (int) Option_list[n].option_letter) == YES) {
183 getopt_string[i] = Option_list[n].option_letter;
184 if (Option_list[n].argument[0] != '\0') {
185 getopt_string[++i] = ':';
189 getopt_string[i] = '\0';
191 while ((a = getopt(argc, argv, getopt_string)) != EOF) {
196 combine = atoi(optarg);
197 if (combine < MINRESTCOMBINE || combine > MAXRESTCOMBINE) {
198 if (Mupmate == YES) {
199 /* Should be impossible to get here,
200 * since mupmate refuses to accept
201 * out of range values. */
202 l_yyerror(0, -1, "Run > Set Options > Min measures to combine: value must be between %d and %d.",
203 MINRESTCOMBINE, MAXRESTCOMBINE);
206 l_yyerror(0, -1, "argument for %cc (number of measures to combine) must be between %d and %d",
207 Optch, MINRESTCOMBINE, MAXRESTCOMBINE);
217 Debuglevel = (int) strtol(optarg, (char **) 0, 0);
221 if (freopen(optarg, "w", stderr) == (FILE *) 0) {
232 Outfilename = optarg;
237 derive_out_name = YES;
242 cmdline_macro(optarg);
246 midifilename = optarg;
250 /* define "built-in" MIDI macro */
251 cmdline_macro("MIDI");
260 pagenum = atoi(optarg);
261 if (pagenum < MINFIRSTPAGE || pagenum > MAXFIRSTPAGE) {
262 if (Mupmate == YES) {
263 /* Should be impossible to get here,
264 * since mupmate refuses to accept
265 * out of range values. */
266 l_yyerror(0, -1, "Run > Set Options > First Page: value must be between %d and %d.",
267 MINFIRSTPAGE, MAXFIRSTPAGE);
270 l_yyerror(0, -1, "argument for %cp (first page) must be between %d and %d",
271 Optch, MINFIRSTPAGE, MAXFIRSTPAGE);
283 vis_stafflist = optarg;
287 /* if got "-e errfile -v" then we want to put the
288 * opening notice into the errfile. This can allow
289 * another program to execute "mup -e errfile -v"
290 * and then use the contents of errfile as the
291 * text for an "About Mup" informational block.
293 if (Got_e_option == YES) {
297 (void) fprintf(stderr,"Version %s\n", Version);
303 chk_x_arg(optarg, &start, &end);
313 if (Ppcomments == YES && Preproc == NO) {
314 warning("-C only valid with -E; ignored");
317 if (outfile_args > 1) {
318 (void) fprintf(stderr, "Only one output file option (-f, -F, -m, -M) can be specified\n");
322 /* turn on yacc debug flag if appropriate */
323 if (Debuglevel & 1) {
328 /* save info about arguments so yywrap can open additional input files
335 /* if file argument, open that, else use stdin */
336 if (optind <= argc - 1) {
341 Curr_filename = _mup_input_filename;
343 Curr_filename = "stdin";
344 #if defined(unix) || defined(__WATCOM__)
345 /* Sometimes people forget to give a file name,
346 * then wonder why Mup is "hanging," so let user
347 * know it isn't hanging... it's waiting for them
348 * to type something. But only if input is a terminal,
349 * and stderr is a terminal--if stdin is a pipe,
350 * user probably doesn't need a reminder. */
351 if (isatty(0) && isatty(2)) {
352 fprintf(stderr, "No input file specified; reading standard input.\n\n");
358 /* initialize for parser */
359 raterrfuncp = doraterr;
361 vis_staffs(vis_stafflist);
364 /* parse the input */
365 if (Preproc == YES) {
371 #ifndef UNIX_LIKE_FILES
376 /* do final checks and cleanup of input data */
377 /* check for missing endif */
379 if (Preproc == YES) {
383 /* find height of headers and footers */
384 /* Note: this has to be called when we are at the *end* of the main
385 * list with all SSVs applied, so that we know the margin settings */
386 calc_block_heights();
387 /* make sure there is a final barline */
388 check4barline_at_end();
389 /* make sure we go to new score if visibility changes */
392 /* derive tabnote staff data for tablature staffs. But if there
393 * have been errors found, don't bother, because we may have
394 * some incomplete/inconsistent data that tab2tabnote doesn't
395 * know how to deal with cleanly. */
396 if (Errorcount == 0) {
400 /* do -c option or restcombine parameter */
401 combine_rests(combine);
403 /* make sure there aren't til clauses past end of song */
404 chk4dangling_til_clauses("the end of the song");
406 /* count how many verses */
412 /* Verify that -o argument (and maybe -p or firstpage parameter)
413 * is valid. If not, this will ufatal. */
414 pagenum = get_first_page(pagenum);
415 set_pagelist(pagelist, pagenum);
417 /* Do -x (extract) option if needed. But if there were errors before,
418 * skip this, because there could be empty measures and such,
419 * that could confuse it, and we're going to give up soon anyway. */
420 if (has_x_arg == YES && Errorcount == 0) {
424 debug(2, "finished with parsing, Errorcount is %d", Errorcount);
426 if (Errorcount > 0) {
427 (void) fprintf(stderr, "\nstopping due to previous error%s\n",
428 Errorcount ? "s" : "");
432 /* do the placement phase */
434 /* initialize the Staffscale and related variables to default values */
441 /* set up ties that carry into next measure */
447 /* place notes relative to staff and set stem direction */
449 /* find relative horizontal position of notes */
451 /* set coordinates of rests and syllables */
454 /* generate MIDI file if appropriate. We wait until here to
455 * do MIDI, so that chord widths have been established, so midi
456 * code can more easily figure out how to crunch all-space chords */
457 if (Doing_MIDI == YES) {
458 if (midifilename == (char *) 0) {
459 /* -M option, so we have to derive the name */
460 midifilename = derive_file_name(".mid");
462 gen_midi(midifilename);
466 /* figure out absolute horizontal locations */
468 /* find lengths of beams, angles of beams, etc */
470 /* set up mussym, octave, rom, bold, pedal, etc */
473 /* find vertical coordinates relative to staff */
475 /* set absolute vertical coordinates */
478 /* split lines and curves */
483 if (derive_out_name == YES) {
484 Outfilename = derive_file_name(".ps");
486 if (*Outfilename != '\0') {
487 if (freopen(Outfilename, "w", stdout) == (FILE *) 0) {
488 cant_open(Outfilename);
493 /* output PostScript for printing */
494 prune_page_range(pagenum);
496 Pagenum = (short) pagenum;
498 } while (Page_range != (struct RANGELIST *) 0);
501 /* if we get to here, all is okay. If there was a problem,
502 * we would have exited where the problem occurred */
507 /* print copyright notice */
513 if (getenv("MUPQUIET") == (char *) 0 || Got_e_option == YES) {
514 fprintf(stderr, "Mup - Music Publisher Version %s\n", Version);
515 fprintf(stderr, Copyright);
520 /* print registration form */
525 printf("Mup is SHAREWARE. You can try out a copy for free, but if you decide\n");
526 printf("to keep and use it, you must register by filling out the form below\n");
527 printf("and sending the form and cash, check, or money order to:\n");
528 printf(" Arkkra Enterprises\n");
529 printf(" P. O. Box 315\n");
530 printf(" Warrenville, IL 60555 USA\n");
531 printf("\nName______________________________________________________________\n\n");
532 printf("Address___________________________________________________________\n\n");
533 printf("City_____________________________ State/Province__________________\n\n");
534 printf("Zip code/Postal code_____________________ Country_________________\n\n");
535 printf("Email address (please print clearly)______________________________\n\n");
536 printf("How did you find out about Mup?___________________________________\n\n");
537 printf("__________________________________________________________________\n\n");
538 printf("___Linux ___ Windows/MS-DOS ___Mac ___Other____________________\n\n");
539 printf("Would you like to join the Mup users mailing list? ___ Yes ___ No\n\n");
540 printf("___ Mup Version %s Registrations.........................$29 each\n", Version);
541 printf("\t\t\t(Illinois residents, add $2.18 sales tax)\n");
542 printf("(For credit card payment, see http://www.arkkra.com/doc/credtcrd.html)\n");
546 /* print usage message and exit */
554 int num_options; /* how many options */
556 char *whitespace; /* for lining things up */
557 int white_length; /* strlen(whitespace) */
558 int length; /* of an argument item */
559 char *extra_options; /* parent process can ask us to print more */
562 /* print the usage summary */
563 fprintf(stderr, "usage: %s ", argv[0]);
564 num_options = NUMELEM(Option_list);
565 for (n = 0; n < num_options; n++) {
566 if (ignore_option( (int) Option_list[n].option_letter) == YES) {
567 /* ignore this option */
570 fprintf(stderr, "[%c%c%s] ", Optch,
571 Option_list[n].option_letter, Option_list[n].argument);
573 fprintf(stderr, "[file...]\n");
575 /* We'll add as much of this whitespace string to each argument
576 * item as needed to line the explanations up nicely. */
578 white_length = strlen(whitespace);
580 /* print the explanations of each option */
581 for (n = 0; n < num_options; n++) {
583 if (ignore_option( (int) Option_list[n].option_letter) == YES) {
587 fprintf(stderr, " %c%c%s", Optch,
588 Option_list[n].option_letter, Option_list[n].argument);
590 /* add enough white space to line things up */
591 if ((length = strlen(Option_list[n].argument)) < white_length) {
592 fprintf(stderr, whitespace + length);
595 fprintf(stderr, " %s\n", Option_list[n].explanation);
597 /* If calling program tells us to add some options to the list,
598 * print those out too. */
599 if ((extra_options = getenv("MUPADDOP")) != (char *) 0) {
600 fprintf(stderr, "%s", extra_options);
607 /* If Mup is being called by some other program, like mupdisp,
608 * such that some of Mup's options should be disallowed, it
609 * should set $MUPDELOP to the list of options to be deleted
610 * from the list of valid options. This function will say, for the
611 * given option, whether it should be disallowed. */
616 int opt; /* an option letter */
619 static char *del_options = 0; /* which options to delete from list */
621 /* the first time we are called, get the list, if any */
622 if (del_options == (char *) 0) {
623 if ((del_options = getenv("MUPDELOP")) == (char *) 0) {
628 return ((strchr(del_options, opt) != (char *) 0) ? YES : NO);
632 /* print message to display first time program is executed if appropriate.
633 * If a particular magic file exists, we know
634 * (or at least assume) the user has already seen the
635 * message about Mup being shareware. If not, we print the message and
636 * exit. For unix, the magic file is called .mup and must be either in the
637 * current directory or in $HOME. For DOS, it is called mup.ok and must be
638 * either in the current directory or in the directory where mup.exe was
639 * executed, as indicated by argv[0]. */
641 #ifndef MAGIC_FILE_NAME
642 #define MAGIC_FILE_NAME (char *) 0
645 int check = 100, *Check_p = ✓
650 char *pname; /* argv[0] */
653 char *fname; /* name of magic file */
654 char *home; /* $HOME (unix) or where mup is located (DOS) */
655 char *path = (char *) 0;/* home/fname */
658 fname = MAGIC_FILE_NAME;
660 if (fname == (char *) 0) {
661 fprintf(stderr, "\tMup is shareware. You may try it out for free, but if you\n");
662 fprintf(stderr, "\tdecide to keep it, you must pay a registration fee of $29.\n");
663 fprintf(stderr, "\tThis copy of Mup was compiled for an unrecognized Operating System\n");
664 fprintf(stderr, "\tor compiler. If you have a UNIX-like operating system,\n");
665 fprintf(stderr, "\tyou can try compiling with -Dunix, or if you have as MS-DOS-like\n");
666 fprintf(stderr, "\tOperating system, you can try compiling with -D__DOS__\n");
667 fprintf(stderr, "\tIf that still doesn't work, to suppress this message,\n");
668 fprintf(stderr, "\tand start using Mup, modify defines.h to define MAGIC_FILE_NAME\n");
669 fprintf(stderr, "\tto a name that is appropriate for your operating system.\n");
670 fprintf(stderr, "\tBy doing so, you acknowledge that you have read\n");
671 fprintf(stderr, "\tthe Mup license and agree to its terms,\n");
672 fprintf(stderr, "\tand agree that if you decide to continue to use Mup\n");
673 fprintf(stderr, "\tafter trying it out, you will pay the registration fee.\n");
674 fprintf(stderr, "\tAfter changing MAGIC_FILE_NAME or any other related #defines\n");
675 fprintf(stderr, "\tthat you might need, and recompiling, execute\n");
676 fprintf(stderr, "\t\tmup -r\n\tto get a registration form. If you let us know about any changes\n");
677 fprintf(stderr, "\tyou need to make to support your OS, we will consider\n");
678 fprintf(stderr, "\tincorporating those changes in a future Mup release.\n");
682 /* if magic file exists in current directory,
683 * indicating user has already seen the message, return */
685 if (access(fname, 0) == 0) {
696 #ifdef MAGIC_FILE_HOME
697 /* construct pathname to magic file if it is in $HOME */
698 if ((home = getenv("HOME")) != (char *) 0) {
699 MALLOCA(char, path, strlen(home)+ strlen(fname) + 2);
701 (void) sprintf(path, "%s%s", home, fname);
703 (void) sprintf(path, "%s/%s", home, fname);
708 /* construct pathname to magic file if it is in the directory where
709 * mup.exe came from */
710 if ((home = strrchr(pname, '\\')) != (char *) 0) {
711 int baselength; /* strlen up through last \ */
713 baselength = home - pname + 1;
714 MALLOCA(char, path, baselength + strlen(fname) + 1);
715 /* copy pname up to last backslash */
716 strncpy(path, pname, baselength);
717 /* add magic file name */
718 strcpy(path + baselength, fname);
723 #pragma unused(pname)
724 /* check for file in Preferences folder inside System folder */
732 if (FindFolder(kOnSystemDisk, kPreferencesFolderType, false, &vRefNum, &dirID) == noErr)
733 /* preferences folder exists */
734 if (FSMakeFSSpec(vRefNum, dirID, (StringPtr) MupRegFileName, &fsSpec) == noErr) { /* file exists */
737 if (HGetVol((StringPtr) 0, &old_vRefNum, &old_dirID) != noErr) return;
738 if (HSetVol((StringPtr) 0, vRefNum, dirID) != noErr) return;
740 HSetVol((StringPtr) 0, old_vRefNum, old_dirID);
745 /* check for file in $HOME or where mup.exe came from */
746 if (path != (char *) 0 && access(path, 0) == 0) {
752 /* print shareware message and exit */
753 fprintf(stderr, "\n\tMup is shareware. You may try it out for free, but if you\n");
754 fprintf(stderr, "\tdecide to keep it, you must pay a registration fee of $29.\n");
755 fprintf(stderr, "\n\tTo use Mup, first create a file called %s\n", fname);
757 fprintf(stderr, "\tin the Preferences folder inside the system folder");
759 fprintf(stderr, "\tin the current directory");
761 if (path == (char *) 0) {
762 fprintf(stderr, ".\n");
765 fprintf(stderr, " or at %s\n", path);
767 fprintf(stderr, "\t(It can be zero length. It just has to exist.)\n");
768 fprintf(stderr, "\tBy creating this file, you acknowledge that you have read\n");
769 fprintf(stderr, "\tthe Mup license and agree to its terms, and agree that\n");
770 fprintf(stderr, "\tif you decide to continue to use Mup after trying it out,\n\tyou will pay the registration fee.\n");
772 fprintf(stderr, "\n\tAfter creating this file, select\n\t'Registration' from the Mup dialog box\n\tto get a registration form.\n\n");
774 fprintf(stderr, "\n\tAfter creating this file, execute\n\t\tmup %cr\n\tto get a registration form.\n\n", Optch);
791 if ((f = open(fname, O_RDONLY, 0)) > 0) {
792 if (read(f, buff, n) == n) {
793 for (i = 0; i < n; i++) {
795 hash ^= (buff[i] ^ sum);
796 check ^= (buff[i] << (1 + (i & 3)));
798 Vflag = (((sum == 02703) && (hash == 02146)) ? YES : NO);
805 /* make our own yywrap rather than use the one in the lex library.
806 * In case user specifies more than one file, open
807 * each in turn, and return control to lex */
813 int leng = 0; /* Length of file name. Initialization done solely
814 * to avoid bogus "used before set" warning. */
816 /* return from any macros or includes */
817 if (popfile() == 1) {
821 /* if user specified more files, open the next one */
822 for ( ; optind < Num_args; optind++) {
827 if ((yyin = fopen(Arglist[optind], Read_mode)) != NULL) {
828 Curr_filename = Arglist[optind++];
832 /* If name doesn't already end with .mup or .MUP and the open
833 * failed because the file didn't exist, try the name with
839 ( ((leng = strlen(Arglist[optind])) < 5) ||
840 (strcmp(Arglist[optind] + leng - 4, ".mup") != 0 &&
841 strcmp(Arglist[optind] + leng - 4, ".MUP") != 0
843 MALLOCA(char, Curr_filename, leng + 5);
844 sprintf(Curr_filename, "%s.mup", Arglist[optind]);
845 if ((yyin = fopen(Curr_filename, Read_mode)) != NULL) {
850 /* try upper case suffix before giving up */
851 sprintf(Curr_filename, "%s.MUP", Arglist[optind]);
852 if ((yyin = fopen(Curr_filename, Read_mode)) != NULL) {
859 cant_open(Arglist[optind]);
866 /* If user used -M or -F option, we need to derive the output file name.
867 * Use the last input file name, strip off the trailing .mup if it is there,
868 * add the suffix, and return the derived name.
872 derive_file_name(suffix)
874 char *suffix; /* ".mid" or ".ps" */
877 int length; /* of Curr_filename */
878 char *file_name; /* the name we derive */
879 char *suffix_location; /* where the suffix will go */
882 length = strlen(Curr_filename);
883 MALLOCA(char, file_name, length + strlen(suffix) + 1);
885 /* start with the original Mup input file name */
886 strcpy(file_name, Curr_filename);
888 /* see if we need to strip off a .mup */
890 /* find where the .mup would start if it is there */
891 suffix_location = file_name + length - 4;
893 /* If user used upper case, so will we */
894 if (strcmp(suffix_location, ".MUP") == 0) {
895 if (strcmp(suffix, ".mid") == 0) {
898 else if (strcmp(suffix, ".ps") == 0) {
902 pfatal("derive_file_name() called with unknown suffix '%s'", suffix);
905 else if (strcmp(suffix_location, ".mup") != 0) {
906 /* no .mup to strip off; just add to the end */
907 suffix_location = file_name + length;
911 suffix_location = file_name + length;
914 /* append the suffix and return the derived name */
915 strcpy(suffix_location, suffix);
920 /* Determine the first page number. If user used -p option, use that,
921 * otherwise get from first_page parameter, else use 1. */
924 get_first_page(pagenum)
926 int pagenum; /* from -p option */
931 /* if there wasn't a -p value, figure out what to use for first page */
932 if (pagenum < MINFIRSTPAGE) {
933 /* default to page 1 */
936 /* look for last setting of firstpage parameter before
939 for (m_p = Mainllhc_p; m_p != 0; m_p = m_p->next) {
940 if (m_p->str == S_SSV) {
941 if (m_p->u.ssv_p->used[FIRSTPAGE] == YES) {
942 pagenum = m_p->u.ssv_p->firstpage;
945 else if (m_p->str == S_STAFF) {
954 /* Parse the argument to -o, if there is a -o option specified, and
955 * save the info away for later use. Gives error if argument is invalid.
959 set_pagelist(pagelist, startpage)
962 int startpage; /* from -p option */
965 if (pagelist == (char *) 0) {
966 /* no -o option, print all pages */
967 Pglist_type = PG_ALL;
970 else if (strcmp(pagelist, "odd") == 0) {
971 Pglist_type = PG_ODD;
974 else if (strcmp(pagelist, "even") == 0) {
975 Pglist_type = PG_EVEN;
979 struct RANGELIST *new_range;
980 struct RANGELIST **linkpoint_p_p;/* tail of Page_range list,
981 * for linking to the end
983 char *p, *beyondnum; /* for parsing the numbers */
984 short lower, upper; /* page number range */
987 Pglist_type = PG_LIST;
989 /* Parse the argument to -o and save the ranges */
990 /* first set up where to link onto tail of list */
991 linkpoint_p_p = &Page_range;
993 /* walk through the -o argument */
994 for (p = pagelist; *p != '\0'; ) {
996 /* skip any leading white space */
997 while (isspace(*p)) {
1001 /* get page number (which may or may not be the
1002 * start of a range of numbers) */
1003 lower = (short) strtol(p, &beyondnum, 10);
1004 if (beyondnum == p || lower <= 0 || lower < startpage) {
1005 /* bad number from user, jump to error out */
1010 /* skip any white space */
1011 while (isspace(*p)) {
1016 /* there is a range of page numbers. Get the
1017 * upper limit of the range */
1018 upper = (short) strtol(++p, &beyondnum, 10);
1019 if (beyondnum == p || upper <= 0
1021 /* bad value from user */
1025 while (isspace(*p)) {
1031 else if (*p != '\0') {
1035 else if (*p == ',') {
1036 /* not a range, so treat like range of n-n */
1040 else if (*p == '\0') {
1044 /* something other than dash, comma, or end of
1045 * string, which is a user error */
1049 /* save info about this page range */
1050 MALLOC(RANGELIST, new_range, 1);
1051 new_range->begin = lower;
1052 new_range->end = upper;
1053 new_range->next = (struct RANGELIST *) 0;
1055 /* link onto tail of list */
1056 *linkpoint_p_p = new_range;
1057 linkpoint_p_p = &(new_range->next);
1060 /* if jumped out of loop without finishing parsing, user
1061 * gave us something we didn't understand */
1063 if (Mupmate == YES) {
1064 l_yyerror(0, -1, "Run > Set Options > Pages to display: value is invalid.");
1067 l_yyerror(0, -1, "argument for -o (list of pages to display) is invalid");
1074 /* Calculate the page number for the final page and put it in Last_pagenum.
1075 * If there is a -o list, make sure all the
1076 * pages listed on the -o list are less than that. If they aren't remove them
1077 * from the list. Without this step, Mup could go into a loop trying to print
1078 * a page that doesn't exist. */
1081 prune_page_range(start_page)
1083 int start_page; /* number given to the first page via the -p option
1084 * or via the firstpage parameter */
1087 struct MAINLL *mll_p; /* to count page feeds */
1088 struct RANGELIST **range_p_p;
1089 int pruned; /* if we removed anything from list */
1091 /* find the largest page number */
1092 Last_pagenum = start_page;
1093 for (mll_p = Mainllhc_p; mll_p != (struct MAINLL *) 0; mll_p = mll_p->next) {
1094 if (mll_p->str == S_FEED && mll_p->u.feed_p->pagefeed == YES) {
1099 /* If there are extra pages for gridsatend, add those on */
1100 if (Atend_info.separate_page == YES) {
1103 grids_per_page = Atend_info.grids_per_row *
1104 Atend_info.rows_per_page;
1106 Last_pagenum += (Atend_info.grids_used + grids_per_page - 1)
1111 if (Pglist_type != PG_LIST) {
1115 /* see if any items in Page_range are bigger
1116 * than the biggest page number */
1118 for (range_p_p = &Page_range; *range_p_p != (struct RANGELIST *) 0;
1119 range_p_p = &((*range_p_p)->next) ) {
1120 if ((*range_p_p)->begin > Last_pagenum) {
1121 /* need to get rid of this entire entry, because none
1122 * of the pages listed actually exist */
1124 if ((*range_p_p = (*range_p_p)->next)
1125 == (struct RANGELIST *) 0) {
1126 /* last one on the list */
1130 else if ((*range_p_p)->end > Last_pagenum) {
1131 /* just need to shorten this range */
1132 (*range_p_p)->end = Last_pagenum;
1137 if (pruned == YES) {
1138 l_warning( (char *) 0, -1, "-o list included one or more pages that don't exist");
1143 /* given a page number, return YES if that page should be printed now, NO
1144 * if not. If user gave a list of pages to print using -o, we print the page
1145 * only if it is the very first thing on the list. If there is a smaller
1146 * number further on in the list, we'll do that page later on another pass.
1147 * The print phase has to keep making multiple passes until the list is
1148 * empty. This allows user to print things out in random order, which may
1149 * be useful especially for 2-on-1 printing, where for example, you may
1150 * want a 4-page "booklet", printing page 4 then page 1 on one side and
1151 * pages 2 and 3 on the other side.
1160 struct RANGELIST *old_range; /* to keep track of item to free */
1162 switch (Pglist_type) {
1168 return (pagenum & 1) == 1 ? YES : NO;
1171 return (pagenum & 1) == 0 ? YES : NO;
1174 if (Page_range == (struct RANGELIST *) 0) {
1175 /* ran off the end of list, so no more to print */
1179 if (Page_range->begin == pagenum) {
1180 /* is first on list so we will print it.
1181 * But first, fix up the list. If we've used up all of
1182 * the current range, free it and point to the next. */
1183 (Page_range->begin)++;
1184 if (Page_range->begin > Page_range->end) {
1185 old_range = Page_range;
1186 Page_range = Page_range->next;
1197 /* return YES if we were doing a page list (-o option) but have now handled
1198 * all of the pages */
1203 if (Pglist_type == PG_LIST) {
1204 return ((Page_range == 0) ? YES : NO);
1207 return ((Pagenum == Last_pagenum) ? YES : NO);
1212 /* handle the argument to -s (list of staffs to make visible). For each
1213 * visible staff, make an SSV marking it visible */
1216 vis_staffs(stafflist)
1220 int s; /* staff index */
1221 int v; /* voice index */
1222 long start, end; /* staff range */
1225 if (stafflist == (char *) 0) {
1226 /* user didn't use -s, so set to all visible */
1227 for (s = 1; s <= MAXSTAFFS; s++) {
1229 for (v = 1; v <= MAXVOICES; v++) {
1230 Voice_vis[s][v] = YES;
1236 /* init to all invisible */
1237 for (s = 1; s <= MAXSTAFFS; s++) {
1239 for (v = 1; v <= MAXVOICES; v++) {
1240 Voice_vis[s][v] = NO;
1244 for ( ; *stafflist != '\0'; ) {
1245 /* get first staff number in list. Will error check below */
1246 start = strtol(stafflist, &stafflist, 10);
1248 if (*stafflist == '-') {
1249 /* we have a range. Get end of range */
1250 end = strtol(stafflist + 1, &stafflist, 10);
1253 /* single number, use end same as start */
1258 if (start < 1 || start > MAXSTAFFS || end < 1 ||
1259 end > MAXSTAFFS || end < start) {
1260 if (Mupmate == YES) {
1261 l_yyerror(0, -1, "Run > Set Options > Staffs to display/play: value is invalid.");
1264 l_yyerror(0, -1, "invalid argument for %cs option (staffs to make visible)", Optch);
1269 /* see if there is a voice qualifier */
1270 if (*stafflist == 'v') {
1272 switch (*stafflist) {
1283 if (Mupmate == YES) {
1284 l_yyerror(0, -1, "Run > Set Options > Staffs to display/play: voice qualifier must be 1, 2, or 3.");
1287 l_yyerror(0, -1, "voice qualifier for -s option must be 1, 2, or 3");
1292 if (*stafflist != '\0' && *stafflist != ',') {
1293 if (Mupmate == YES) {
1294 l_yyerror(0, -1, "Run > Set Options > Staffs to display/play: invalid voice qualifier. (Maybe missing comma?)");
1297 l_yyerror(0, -1, "invalid voice qualifier for -s option (missing comma?)");
1303 /* no voice qualifier */
1307 /* mark all staffs in range as visible */
1308 for ( ; start <= end; start++) {
1309 Staff_vis[start] = YES;
1311 Voice_vis[start][v] = YES;
1314 /* no voice qualifier, so all voices are visible */
1316 for (vn = 1; vn <= MAXVOICES; vn++) {
1317 Voice_vis[start][vn] = YES;
1322 /* if comma for another range, skip past it */
1323 if (*stafflist == ',') {
1331 /* for non-unix or other systems that don't have a getopt() function,
1332 * define one here. This is NOT a general purpose implementation of getopt(),
1333 * but something good enough to work with Mup */
1337 static int argoffset;
1338 int opttype P((int option, char *optstring));
1345 getopt(argc, argv, optstring)
1350 const char *optstring;
1361 if (optind >= argc) {
1365 if (argoffset == 0) {
1367 if (argv[optind][argoffset] == '-'
1368 || argv[optind][argoffset] == '/') {
1370 if (argv[optind][argoffset] == '-') {
1379 /* determine if option is valid and if should have an argument */
1380 option = argv[optind][argoffset] & 0x7f;
1381 switch (opttype(option, (char *) optstring)) {
1383 /* valid option without argument. Keep track of where
1384 * to look for next option */
1385 if (argv[optind][++argoffset] == '\0') {
1392 /* valid option with argument. */
1393 if (argv[optind][++argoffset] != '\0') {
1394 /* argument immediately follows in same argv */
1395 optarg = &(argv[optind][argoffset]);
1399 /* white space. argument must be in next argv */
1401 if (optind >= argc) {
1402 fprintf(stderr, "missing argument to %c%c option\n", Optch, option);
1405 optarg = &(argv[optind][0]);
1412 fprintf(stderr, "invalid option %c%c\n", Optch, option);
1419 /* look up option in optstring and return type of option */
1422 opttype(option, optstring)
1430 for (p = optstring; *p != '\0'; ) {
1431 if (*p++ == option) {
1432 return(*p == ':' ? WITHARG : NOARG);