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
= &check
;
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
);