Commit | Line | Data |
---|---|---|
69695f33 MW |
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 = ✓ | |
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 |