chiark / gitweb /
Merge branch 'arkkra' into shiny
[mup] / mup / mup / main.c
CommitLineData
69695f33
MW
1static char Copyright[] =
2 "Copyright (c) 1995-2006 by Arkkra Enterprises.\nAll rights reserved.\n";
3
4/* main for "Mup" music publication program */
5
6/*
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
11 * 1 = yydebug
12 * 2 = parse phase high level trace
13 * 4 = parse phase low level trace
14 * 8 = reserved
15 * 16 = placement phase high level trace
16 * 32 = placement phase low level trace
17 * 64 = reserved
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.
38 *
39 * Expects zero or more input files. If no file specified, reads stdin
40 *
41 * Exit code is 0 on success, or the number of errors found, up to 254,
42 * or 255 for internal error.
43 */
44
45
46#ifdef __WATCOMC__
47#include <io.h>
48#endif
49#ifdef Mac_BBEdit
50#include <Files.h>
51#include <Folders.h>
52#include <MupInterface.h>
53#define main _mup /* rename entry point to _mup */
54#endif
55#include <errno.h>
56#include <fcntl.h>
57#include "defines.h"
58#include "globals.h"
59
60
61/* List of valid command line options and their explanations */
62struct Options {
63 char option_letter;
64 char *argument; /* describes the arg if any, or "" if none */
65 char *explanation;
66} Option_list[] = {
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" }
83};
84
85
86#ifndef _UNISTD_H
87/* to process command line args */
88extern int getopt P((int argc, char * const *argv, const char *optstr));
89extern int optind; /* set by getopt to point to current cmd line argument */
90extern char *optarg; /* set by getopt */
91extern char *getenv();
92#endif
93extern FILE *yyout; /* lex could try to write error output here */
94
95static char **Arglist; /* global pointer to argv */
96static int Num_args; /* global copy of argc */
97static char Version[] = "5.3"; /* Mup version number */
98static int Got_e_option = NO; /* was there a -e option on the command line? */
99
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.
103 */
104static int Pglist_type;
105#define PG_LIST 0
106#define PG_ODD 1
107#define PG_EVEN 2
108#define PG_ALL 3
109
110#ifdef O_BINARY
111static char * Read_mode = "rb";
112#else
113static char * Read_mode = "r";
114#endif
115
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.
119 */
120static struct RANGELIST *Page_range;
121
122static void usage P((char **argv)); /* print usage message and exit */
123static int ignore_option P((int opt));
124static void notice P((void));
125static void first_msg P((char *pname));
126static void setvflag P((char *fname));
127static void registration P((void));
128static char *derive_file_name P((char *suffix));
129static int get_first_page P((int pagenum));
130static void set_pagelist P((char *pagelist, int startpage));
131static void prune_page_range P((int start_page));
132static void vis_staffs P((char *stafflist));
133
134
135\f
136
137int
138main(argc, argv)
139
140int argc;
141char **argv;
142
143{
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 */
150 int pagenum;
151 char *pagelist = 0;
152 int start = 1, end = -1; /* Arguments to -x option */
153 int has_x_arg = NO;
154 int outfile_args = 0; /* we only allow one instance of [fFmM] options */
155 int n, i;
156 int num_options;
157 char *getopt_string;
158
159
160
161 notice();
162 first_msg(argv[0]);
163 /* must init head shapes table before first call to initstructs */
164 init_symtbl();
165
166 /* set initial page number to "not set" */
167 pagenum = MINFIRSTPAGE - 1;
168 initstructs();
169
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);
173
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) {
181 continue;
182 }
183 getopt_string[i] = Option_list[n].option_letter;
184 if (Option_list[n].argument[0] != '\0') {
185 getopt_string[++i] = ':';
186 }
187 i++;
188 }
189 getopt_string[i] = '\0';
190
191 while ((a = getopt(argc, argv, getopt_string)) != EOF) {
192
193 switch (a) {
194
195 case 'c':
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);
204 }
205 else {
206 l_yyerror(0, -1, "argument for %cc (number of measures to combine) must be between %d and %d",
207 Optch, MINRESTCOMBINE, MAXRESTCOMBINE);
208 }
209 }
210 break;
211
212 case 'C':
213 Ppcomments = YES;
214 break;
215
216 case 'd':
217 Debuglevel = (int) strtol(optarg, (char **) 0, 0);
218 break;
219
220 case 'e':
221 if (freopen(optarg, "w", stderr) == (FILE *) 0) {
222 cant_open(optarg);
223 }
224 Got_e_option = YES;
225 break;
226
227 case 'E':
228 Preproc = YES;
229 break;
230
231 case 'f':
232 Outfilename = optarg;
233 outfile_args++;
234 break;
235
236 case 'F':
237 derive_out_name = YES;
238 outfile_args++;
239 break;
240
241 case 'D':
242 cmdline_macro(optarg);
243 break;
244
245 case 'm':
246 midifilename = optarg;
247 /* FALLTHRU */
248 case 'M':
249 Doing_MIDI = YES;
250 /* define "built-in" MIDI macro */
251 cmdline_macro("MIDI");
252 outfile_args++;
253 break;
254
255 case 'o':
256 pagelist = optarg;
257 break;
258
259 case 'p':
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);
268 }
269 else {
270 l_yyerror(0, -1, "argument for %cp (first page) must be between %d and %d",
271 Optch, MINFIRSTPAGE, MAXFIRSTPAGE);
272 }
273 }
274 break;
275
276 case 'r':
277 registration();
278 exit(0);
279 /*NOTREACHED*/
280 break;
281
282 case 's':
283 vis_stafflist = optarg;
284 break;
285
286 case 'v':
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.
292 */
293 if (Got_e_option == YES) {
294 notice();
295 }
296
297 (void) fprintf(stderr,"Version %s\n", Version);
298 exit(0);
299 /*NOTREACHED*/
300 break;
301
302 case 'x':
303 chk_x_arg(optarg, &start, &end);
304 has_x_arg = YES;
305 break;
306
307 default:
308 usage(argv);
309 break;
310 }
311 }
312
313 if (Ppcomments == YES && Preproc == NO) {
314 warning("-C only valid with -E; ignored");
315 }
316
317 if (outfile_args > 1) {
318 (void) fprintf(stderr, "Only one output file option (-f, -F, -m, -M) can be specified\n");
319 exit(1);
320 }
321
322 /* turn on yacc debug flag if appropriate */
323 if (Debuglevel & 1) {
324 yydebug = 1;
325 ifdebug = 1;
326 }
327
328 /* save info about arguments so yywrap can open additional input files
329 * if necessary */
330 Arglist = argv;
331 Num_args = argc;
332 yyin = stdin;
333 yyout = stderr;
334
335 /* if file argument, open that, else use stdin */
336 if (optind <= argc - 1) {
337 (void) yywrap();
338 }
339 else {
340#ifdef Mac_BBEdit
341 Curr_filename = _mup_input_filename;
342#else
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");
353 }
354#endif
355#endif
356 }
357
358 /* initialize for parser */
359 raterrfuncp = doraterr;
360 initstructs();
361 vis_staffs(vis_stafflist);
362 reset_ped_state();
363
364 /* parse the input */
365 if (Preproc == YES) {
366 preproc();
367 }
368 else {
369 (void) yyparse();
370 }
371#ifndef UNIX_LIKE_FILES
372 mac_cleanup();
373#endif
374
375
376 /* do final checks and cleanup of input data */
377 /* check for missing endif */
378 chk_ifdefs();
379 if (Preproc == YES) {
380 error_exit();
381 }
382
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 */
390 chk_vis_feed();
391
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) {
397 tab2tabnote();
398 }
399
400 /* do -c option or restcombine parameter */
401 combine_rests(combine);
402
403 /* make sure there aren't til clauses past end of song */
404 chk4dangling_til_clauses("the end of the song");
405
406 /* count how many verses */
407 set_maxverses();
408
409 /* process ties */
410 tie();
411
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);
416
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) {
421 extract(start, end);
422 }
423
424 debug(2, "finished with parsing, Errorcount is %d", Errorcount);
425
426 if (Errorcount > 0) {
427 (void) fprintf(stderr, "\nstopping due to previous error%s\n",
428 Errorcount ? "s" : "");
429 error_exit();
430 }
431
432 /* do the placement phase */
433
434 /* initialize the Staffscale and related variables to default values */
435 initstructs();
436 set_staffscale(0);
437
438 /* transpose */
439 transgroups();
440
441 /* set up ties that carry into next measure */
442 tie_carry();
443
444 /* line up chords */
445 makechords();
446
447 /* place notes relative to staff and set stem direction */
448 setnotes();
449 /* find relative horizontal position of notes */
450 setgrps();
451 /* set coordinates of rests and syllables */
452 restsyl();
453
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");
461 }
462 gen_midi(midifilename);
463 exit(0);
464 }
465
466 /* figure out absolute horizontal locations */
467 abshorz();
468 /* find lengths of beams, angles of beams, etc */
469 beamstem();
470 /* set up mussym, octave, rom, bold, pedal, etc */
471 stuff();
472
473 /* find vertical coordinates relative to staff */
474 relvert();
475 /* set absolute vertical coordinates */
476 absvert();
477
478 /* split lines and curves */
479 fix_locvars();
480
481 print_mainll();
482
483 if (derive_out_name == YES) {
484 Outfilename = derive_file_name(".ps");
485 }
486 if (*Outfilename != '\0') {
487 if (freopen(Outfilename, "w", stdout) == (FILE *) 0) {
488 cant_open(Outfilename);
489 exit(1);
490 }
491 }
492
493 /* output PostScript for printing */
494 prune_page_range(pagenum);
495 do {
496 Pagenum = (short) pagenum;
497 print_music();
498 } while (Page_range != (struct RANGELIST *) 0);
499 trailer();
500
501 /* if we get to here, all is okay. If there was a problem,
502 * we would have exited where the problem occurred */
503 return(0);
504}
505\f
506
507/* print copyright notice */
508
509static void
510notice()
511
512{
513 if (getenv("MUPQUIET") == (char *) 0 || Got_e_option == YES) {
514 fprintf(stderr, "Mup - Music Publisher Version %s\n", Version);
515 fprintf(stderr, Copyright);
516 }
517}
518\f
519
520/* print registration form */
521
522static void
523registration()
524{
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");
543}
544\f
545
546/* print usage message and exit */
547
548static void
549usage(argv)
550
551char **argv;
552
553{
554 int num_options; /* how many options */
555 int n;
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 */
560
561
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 */
568 continue;
569 }
570 fprintf(stderr, "[%c%c%s] ", Optch,
571 Option_list[n].option_letter, Option_list[n].argument);
572 }
573 fprintf(stderr, "[file...]\n");
574
575 /* We'll add as much of this whitespace string to each argument
576 * item as needed to line the explanations up nicely. */
577 whitespace = " ";
578 white_length = strlen(whitespace);
579
580 /* print the explanations of each option */
581 for (n = 0; n < num_options; n++) {
582
583 if (ignore_option( (int) Option_list[n].option_letter) == YES) {
584 continue;
585 }
586
587 fprintf(stderr, " %c%c%s", Optch,
588 Option_list[n].option_letter, Option_list[n].argument);
589
590 /* add enough white space to line things up */
591 if ((length = strlen(Option_list[n].argument)) < white_length) {
592 fprintf(stderr, whitespace + length);
593 }
594
595 fprintf(stderr, " %s\n", Option_list[n].explanation);
596 }
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);
601 }
602
603 exit(1);
604}
605\f
606
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. */
612
613static int
614ignore_option(opt)
615
616int opt; /* an option letter */
617
618{
619 static char *del_options = 0; /* which options to delete from list */
620
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) {
624 del_options = "";
625 }
626 }
627
628 return ((strchr(del_options, opt) != (char *) 0) ? YES : NO);
629}
630\f
631
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]. */
640
641#ifndef MAGIC_FILE_NAME
642#define MAGIC_FILE_NAME (char *) 0
643#endif
644
645int check = 100, *Check_p = &check;
646
647static void
648first_msg(pname)
649
650char *pname; /* argv[0] */
651
652{
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 */
656
657
658 fname = MAGIC_FILE_NAME;
659
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");
679 exit(1);
680 }
681
682 /* if magic file exists in current directory,
683 * indicating user has already seen the message, return */
684#ifndef Mac_BBEdit
685 if (access(fname, 0) == 0) {
686 setvflag(fname);
687 if (Vflag == YES) {
688 return;
689 }
690 else {
691 check = 100;
692 }
693 }
694#endif
695
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);
700#ifdef VMS
701 (void) sprintf(path, "%s%s", home, fname);
702#else
703 (void) sprintf(path, "%s/%s", home, fname);
704#endif
705 }
706#else
707#ifdef __DOS__
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 \ */
712
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);
719 }
720#endif
721#endif
722#ifdef Mac_BBEdit
723#pragma unused(pname)
724 /* check for file in Preferences folder inside System folder */
725 path = 0;
726 home = 0;
727 {
728 short vRefNum;
729 long dirID;
730 FSSpec fsSpec;
731
732 if (FindFolder(kOnSystemDisk, kPreferencesFolderType, false, &vRefNum, &dirID) == noErr)
733 /* preferences folder exists */
734 if (FSMakeFSSpec(vRefNum, dirID, (StringPtr) MupRegFileName, &fsSpec) == noErr) { /* file exists */
735 short old_vRefNum;
736 long old_dirID;
737 if (HGetVol((StringPtr) 0, &old_vRefNum, &old_dirID) != noErr) return;
738 if (HSetVol((StringPtr) 0, vRefNum, dirID) != noErr) return;
739 setvflag(fname);
740 HSetVol((StringPtr) 0, old_vRefNum, old_dirID);
741 return;
742 }
743 }
744#else
745 /* check for file in $HOME or where mup.exe came from */
746 if (path != (char *) 0 && access(path, 0) == 0) {
747 setvflag(path);
748 return;
749 }
750#endif
751
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);
756#ifdef Mac_BBEdit
757 fprintf(stderr, "\tin the Preferences folder inside the system folder");
758#else
759 fprintf(stderr, "\tin the current directory");
760#endif
761 if (path == (char *) 0) {
762 fprintf(stderr, ".\n");
763 }
764 else {
765 fprintf(stderr, " or at %s\n", path);
766 }
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");
771#ifdef Mac_BBEdit
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");
773#else
774 fprintf(stderr, "\n\tAfter creating this file, execute\n\t\tmup %cr\n\tto get a registration form.\n\n", Optch);
775#endif
776 exit(0);
777}
778\f
779
780static void
781setvflag(fname)
782char *fname;
783{
784 int f;
785 char buff[48];
786 int sum = 0;
787 int hash = 0x45;
788 int n = 16;
789 int i;
790
791 if ((f = open(fname, O_RDONLY, 0)) > 0) {
792 if (read(f, buff, n) == n) {
793 for (i = 0; i < n; i++) {
794 sum += buff[i];
795 hash ^= (buff[i] ^ sum);
796 check ^= (buff[i] << (1 + (i & 3)));
797 }
798 Vflag = (((sum == 02703) && (hash == 02146)) ? YES : NO);
799 }
800 }
801 (void) close(f);
802}
803\f
804
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 */
808
809int
810yywrap()
811
812{
813 int leng = 0; /* Length of file name. Initialization done solely
814 * to avoid bogus "used before set" warning. */
815
816 /* return from any macros or includes */
817 if (popfile() == 1) {
818 return(0);
819 }
820
821 /* if user specified more files, open the next one */
822 for ( ; optind < Num_args; optind++) {
823 if (yyin != NULL) {
824 (void) fclose(yyin);
825 }
826 errno = 0;
827 if ((yyin = fopen(Arglist[optind], Read_mode)) != NULL) {
828 Curr_filename = Arglist[optind++];
829 yylineno = 1;
830 return(0);
831 }
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
834 * .mup appended. */
835 else if (
836#ifdef ENOENT
837 errno == ENOENT &&
838#endif
839 ( ((leng = strlen(Arglist[optind])) < 5) ||
840 (strcmp(Arglist[optind] + leng - 4, ".mup") != 0 &&
841 strcmp(Arglist[optind] + leng - 4, ".MUP") != 0
842 )) ) {
843 MALLOCA(char, Curr_filename, leng + 5);
844 sprintf(Curr_filename, "%s.mup", Arglist[optind]);
845 if ((yyin = fopen(Curr_filename, Read_mode)) != NULL) {
846 yylineno = 1;
847 optind++;
848 return(0);
849 }
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) {
853 yylineno = 1;
854 optind++;
855 return(0);
856 }
857 FREE(Curr_filename);
858 }
859 cant_open(Arglist[optind]);
860 }
861
862 return(1);
863}
864\f
865
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.
869 */
870
871static char *
872derive_file_name(suffix)
873
874char *suffix; /* ".mid" or ".ps" */
875
876{
877 int length; /* of Curr_filename */
878 char *file_name; /* the name we derive */
879 char *suffix_location; /* where the suffix will go */
880
881
882 length = strlen(Curr_filename);
883 MALLOCA(char, file_name, length + strlen(suffix) + 1);
884
885 /* start with the original Mup input file name */
886 strcpy(file_name, Curr_filename);
887
888 /* see if we need to strip off a .mup */
889 if (length > 3) {
890 /* find where the .mup would start if it is there */
891 suffix_location = file_name + length - 4;
892
893 /* If user used upper case, so will we */
894 if (strcmp(suffix_location, ".MUP") == 0) {
895 if (strcmp(suffix, ".mid") == 0) {
896 suffix = ".MID";
897 }
898 else if (strcmp(suffix, ".ps") == 0) {
899 suffix = ".PS";
900 }
901 else {
902 pfatal("derive_file_name() called with unknown suffix '%s'", suffix);
903 }
904 }
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;
908 }
909 }
910 else {
911 suffix_location = file_name + length;
912 }
913
914 /* append the suffix and return the derived name */
915 strcpy(suffix_location, suffix);
916 return(file_name);
917}
918\f
919
920/* Determine the first page number. If user used -p option, use that,
921 * otherwise get from first_page parameter, else use 1. */
922
923static int
924get_first_page(pagenum)
925
926int pagenum; /* from -p option */
927
928{
929 struct MAINLL *m_p;
930
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 */
934 pagenum = 1;
935
936 /* look for last setting of firstpage parameter before
937 * any STAFFs */
938 initstructs();
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;
943 }
944 }
945 else if (m_p->str == S_STAFF) {
946 break;
947 }
948 }
949 }
950 return(pagenum);
951}
952\f
953
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.
956 */
957
958static void
959set_pagelist(pagelist, startpage)
960
961char *pagelist;
962int startpage; /* from -p option */
963
964{
965 if (pagelist == (char *) 0) {
966 /* no -o option, print all pages */
967 Pglist_type = PG_ALL;
968 }
969
970 else if (strcmp(pagelist, "odd") == 0) {
971 Pglist_type = PG_ODD;
972 }
973
974 else if (strcmp(pagelist, "even") == 0) {
975 Pglist_type = PG_EVEN;
976 }
977
978 else {
979 struct RANGELIST *new_range;
980 struct RANGELIST **linkpoint_p_p;/* tail of Page_range list,
981 * for linking to the end
982 * of the list */
983 char *p, *beyondnum; /* for parsing the numbers */
984 short lower, upper; /* page number range */
985
986
987 Pglist_type = PG_LIST;
988
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;
992
993 /* walk through the -o argument */
994 for (p = pagelist; *p != '\0'; ) {
995
996 /* skip any leading white space */
997 while (isspace(*p)) {
998 p++;
999 }
1000
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 */
1006 break;
1007 }
1008
1009 p = beyondnum;
1010 /* skip any white space */
1011 while (isspace(*p)) {
1012 p++;
1013 }
1014
1015 if (*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
1020 || upper < lower) {
1021 /* bad value from user */
1022 break;
1023 }
1024 p = beyondnum;
1025 while (isspace(*p)) {
1026 p++;
1027 }
1028 if (*p == ',') {
1029 p++;
1030 }
1031 else if (*p != '\0') {
1032 break;
1033 }
1034 }
1035 else if (*p == ',') {
1036 /* not a range, so treat like range of n-n */
1037 upper = lower;
1038 p++;
1039 }
1040 else if (*p == '\0') {
1041 upper = lower;
1042 }
1043 else {
1044 /* something other than dash, comma, or end of
1045 * string, which is a user error */
1046 break;
1047 }
1048
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;
1054
1055 /* link onto tail of list */
1056 *linkpoint_p_p = new_range;
1057 linkpoint_p_p = &(new_range->next);
1058 }
1059
1060 /* if jumped out of loop without finishing parsing, user
1061 * gave us something we didn't understand */
1062 if (*p != '\0') {
1063 if (Mupmate == YES) {
1064 l_yyerror(0, -1, "Run > Set Options > Pages to display: value is invalid.");
1065 }
1066 else {
1067 l_yyerror(0, -1, "argument for -o (list of pages to display) is invalid");
1068 }
1069 }
1070 }
1071}
1072\f
1073
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. */
1079
1080static void
1081prune_page_range(start_page)
1082
1083int start_page; /* number given to the first page via the -p option
1084 * or via the firstpage parameter */
1085
1086{
1087 struct MAINLL *mll_p; /* to count page feeds */
1088 struct RANGELIST **range_p_p;
1089 int pruned; /* if we removed anything from list */
1090
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) {
1095 Last_pagenum++;
1096 }
1097 }
1098
1099 /* If there are extra pages for gridsatend, add those on */
1100 if (Atend_info.separate_page == YES) {
1101 int grids_per_page;
1102
1103 grids_per_page = Atend_info.grids_per_row *
1104 Atend_info.rows_per_page;
1105 /* round up */
1106 Last_pagenum += (Atend_info.grids_used + grids_per_page - 1)
1107 / grids_per_page;
1108 }
1109
1110
1111 if (Pglist_type != PG_LIST) {
1112 return;
1113 }
1114
1115 /* see if any items in Page_range are bigger
1116 * than the biggest page number */
1117 pruned = NO;
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 */
1123 pruned = YES;
1124 if ((*range_p_p = (*range_p_p)->next)
1125 == (struct RANGELIST *) 0) {
1126 /* last one on the list */
1127 break;
1128 }
1129 }
1130 else if ((*range_p_p)->end > Last_pagenum) {
1131 /* just need to shorten this range */
1132 (*range_p_p)->end = Last_pagenum;
1133 pruned = YES;
1134 }
1135 }
1136
1137 if (pruned == YES) {
1138 l_warning( (char *) 0, -1, "-o list included one or more pages that don't exist");
1139 }
1140}
1141\f
1142
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.
1152 */
1153
1154int
1155onpagelist(pagenum)
1156
1157int pagenum;
1158
1159{
1160 struct RANGELIST *old_range; /* to keep track of item to free */
1161
1162 switch (Pglist_type) {
1163
1164 case PG_ALL:
1165 return(YES);
1166
1167 case PG_ODD:
1168 return (pagenum & 1) == 1 ? YES : NO;
1169
1170 case PG_EVEN:
1171 return (pagenum & 1) == 0 ? YES : NO;
1172
1173 default:
1174 if (Page_range == (struct RANGELIST *) 0) {
1175 /* ran off the end of list, so no more to print */
1176 return(NO);
1177 }
1178
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;
1187 FREE(old_range);
1188 }
1189 return(YES);
1190 }
1191 break;
1192 }
1193 return(NO);
1194}
1195\f
1196
1197/* return YES if we were doing a page list (-o option) but have now handled
1198 * all of the pages */
1199
1200int
1201last_page()
1202{
1203 if (Pglist_type == PG_LIST) {
1204 return ((Page_range == 0) ? YES : NO);
1205 }
1206 else {
1207 return ((Pagenum == Last_pagenum) ? YES : NO);
1208 }
1209}
1210\f
1211
1212/* handle the argument to -s (list of staffs to make visible). For each
1213 * visible staff, make an SSV marking it visible */
1214
1215static void
1216vis_staffs(stafflist)
1217
1218char *stafflist;
1219{
1220 int s; /* staff index */
1221 int v; /* voice index */
1222 long start, end; /* staff range */
1223
1224
1225 if (stafflist == (char *) 0) {
1226 /* user didn't use -s, so set to all visible */
1227 for (s = 1; s <= MAXSTAFFS; s++) {
1228 Staff_vis[s] = YES;
1229 for (v = 1; v <= MAXVOICES; v++) {
1230 Voice_vis[s][v] = YES;
1231 }
1232 }
1233 return;
1234 }
1235
1236 /* init to all invisible */
1237 for (s = 1; s <= MAXSTAFFS; s++) {
1238 Staff_vis[s] = NO;
1239 for (v = 1; v <= MAXVOICES; v++) {
1240 Voice_vis[s][v] = NO;
1241 }
1242 }
1243
1244 for ( ; *stafflist != '\0'; ) {
1245 /* get first staff number in list. Will error check below */
1246 start = strtol(stafflist, &stafflist, 10);
1247
1248 if (*stafflist == '-') {
1249 /* we have a range. Get end of range */
1250 end = strtol(stafflist + 1, &stafflist, 10);
1251 }
1252 else {
1253 /* single number, use end same as start */
1254 end = start;
1255 }
1256
1257 /* error check */
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.");
1262 }
1263 else {
1264 l_yyerror(0, -1, "invalid argument for %cs option (staffs to make visible)", Optch);
1265 }
1266 return;
1267 }
1268
1269 /* see if there is a voice qualifier */
1270 if (*stafflist == 'v') {
1271 stafflist++;
1272 switch (*stafflist) {
1273 case '1':
1274 v = 1;
1275 break;
1276 case '2':
1277 v = 2;
1278 break;
1279 case '3':
1280 v = 3;
1281 break;
1282 default:
1283 if (Mupmate == YES) {
1284 l_yyerror(0, -1, "Run > Set Options > Staffs to display/play: voice qualifier must be 1, 2, or 3.");
1285 }
1286 else {
1287 l_yyerror(0, -1, "voice qualifier for -s option must be 1, 2, or 3");
1288 }
1289 return;
1290 }
1291 stafflist++;
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?)");
1295 }
1296 else {
1297 l_yyerror(0, -1, "invalid voice qualifier for -s option (missing comma?)");
1298 }
1299 return;
1300 }
1301 }
1302 else {
1303 /* no voice qualifier */
1304 v = 0;
1305 }
1306
1307 /* mark all staffs in range as visible */
1308 for ( ; start <= end; start++) {
1309 Staff_vis[start] = YES;
1310 if (v != 0) {
1311 Voice_vis[start][v] = YES;
1312 }
1313 else {
1314 /* no voice qualifier, so all voices are visible */
1315 int vn;
1316 for (vn = 1; vn <= MAXVOICES; vn++) {
1317 Voice_vis[start][vn] = YES;
1318 }
1319 }
1320 }
1321
1322 /* if comma for another range, skip past it */
1323 if (*stafflist == ',') {
1324 stafflist++;
1325 }
1326 }
1327}
1328\f
1329
1330#ifdef NEED_GETOPT
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 */
1334
1335int optind = 1;
1336char *optarg;
1337static int argoffset;
1338int opttype P((int option, char *optstring));
1339
1340#define NOARG 1
1341#define WITHARG 2
1342#define BADOPT 3
1343
1344int
1345getopt(argc, argv, optstring)
1346
1347#ifdef __STDC__
1348int argc;
1349char * const *argv;
1350const char *optstring;
1351#else
1352int argc;
1353char **argv;
1354char *optstring;
1355#endif
1356
1357{
1358 int option;
1359
1360
1361 if (optind >= argc) {
1362 return(EOF);
1363 }
1364
1365 if (argoffset == 0) {
1366#ifdef __DOS__
1367 if (argv[optind][argoffset] == '-'
1368 || argv[optind][argoffset] == '/') {
1369#else
1370 if (argv[optind][argoffset] == '-') {
1371#endif
1372 argoffset = 1;
1373 }
1374 else {
1375 return(EOF);
1376 }
1377 }
1378
1379 /* determine if option is valid and if should have an argument */
1380 option = argv[optind][argoffset] & 0x7f;
1381 switch (opttype(option, (char *) optstring)) {
1382 case NOARG:
1383 /* valid option without argument. Keep track of where
1384 * to look for next option */
1385 if (argv[optind][++argoffset] == '\0') {
1386 optind++;
1387 argoffset = 0;
1388 }
1389 break;
1390
1391 case WITHARG:
1392 /* valid option with argument. */
1393 if (argv[optind][++argoffset] != '\0') {
1394 /* argument immediately follows in same argv */
1395 optarg = &(argv[optind][argoffset]);
1396 optind++;
1397 }
1398 else {
1399 /* white space. argument must be in next argv */
1400 optind++;
1401 if (optind >= argc) {
1402 fprintf(stderr, "missing argument to %c%c option\n", Optch, option);
1403 return('?');
1404 }
1405 optarg = &(argv[optind][0]);
1406 optind++;
1407 }
1408 argoffset = 0;
1409 break;
1410
1411 default:
1412 fprintf(stderr, "invalid option %c%c\n", Optch, option);
1413 option = '?';
1414 }
1415 return(option);
1416}
1417
1418
1419/* look up option in optstring and return type of option */
1420
1421int
1422opttype(option, optstring)
1423
1424int option;
1425char *optstring;
1426
1427{
1428 char *p;
1429
1430 for (p = optstring; *p != '\0'; ) {
1431 if (*p++ == option) {
1432 return(*p == ':' ? WITHARG : NOARG);
1433 }
1434 if (*p == ':') {
1435 p++;
1436 }
1437 }
1438 return(BADOPT);
1439}
1440
1441#endif