chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / main.c
1 static 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 */
62 struct 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 */
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();
92 #endif
93 extern FILE *yyout;     /* lex could try to write error output here */
94
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? */
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  */
104 static 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
111 static char * Read_mode = "rb";
112 #else
113 static 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  */
120 static struct RANGELIST *Page_range;
121
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));
133
134
135 \f
136
137 int
138 main(argc, argv)
139
140 int argc;
141 char **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
509 static void
510 notice()
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
522 static void
523 registration()
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
548 static void
549 usage(argv)
550
551 char **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
613 static int
614 ignore_option(opt)
615
616 int 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
645 int check = 100, *Check_p = &check;
646
647 static void
648 first_msg(pname)
649
650 char *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
780 static void
781 setvflag(fname)
782 char *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
809 int
810 yywrap()
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
871 static char *
872 derive_file_name(suffix)
873
874 char *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
923 static int
924 get_first_page(pagenum)
925
926 int 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
958 static void
959 set_pagelist(pagelist, startpage)
960
961 char *pagelist;
962 int 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
1080 static void
1081 prune_page_range(start_page)
1082
1083 int 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
1154 int
1155 onpagelist(pagenum)
1156
1157 int 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
1200 int
1201 last_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
1215 static void
1216 vis_staffs(stafflist)
1217
1218 char *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
1335 int optind = 1;
1336 char *optarg;
1337 static int argoffset;
1338 int opttype P((int option, char *optstring));
1339
1340 #define NOARG 1
1341 #define WITHARG 2
1342 #define BADOPT  3
1343
1344 int
1345 getopt(argc, argv, optstring)
1346
1347 #ifdef __STDC__
1348 int argc;
1349 char * const *argv;
1350 const char *optstring;
1351 #else
1352 int argc;
1353 char **argv;
1354 char *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
1421 int
1422 opttype(option, optstring)
1423
1424 int option;
1425 char *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