chiark / gitweb /
Merge branch 'arkkra' into shiny
[mup] / mup / mup / parstuff.c
CommitLineData
69695f33
MW
1
2/* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2002, 2003, 2004 by Arkkra Enterprises */
3/* All rights reserved */
4
5/* parser functions related to STUFF */
6
7
8#include <string.h>
9#include "defines.h"
10#include "structs.h"
11#include "globals.h"
12
13/* if user specifies a "til" clause on stuff with a number of measures > 0,
14 * we need to save away info about where the til clause will end, to make sure
15 * that it doesn't fall off the end of the measure or the piece. This is the
16 * struct we use to save this info. */
17static struct TIL_INFO {
18 char *inputfile; /* where STUFF was defined */
19 int inputlineno; /* where STUFF was defined */
20 int measnum; /* number of measure in which til clause ends */
21 float count; /* count in measure where til clause ends */
22 struct TIL_INFO *next; /* for linked list */
23} *Til_info_list_p;
24
25/* info about the STUFF currently being collected from input */
26static int Curr_stuff_type; /* ST_* */
27static int Stuff_size; /* point size of stuff text string */
28static int Modifier; /* TM_* for text, L_* for phrase */
29static int Measnum = 1; /* to check til clauses. Can't use Meas_num
30 * global because it doesn't count invisible
31 * bars but til clauses do */
32static int Multi_adjust; /* adjustment to Measnum to account
33 * for multirests */
34
35/* head and tail of list of STUFF currently being collected from input */
36static struct STUFF *Head_stufflist_p;
37static struct STUFF *Tail_stufflist_p;
38
39/* current pedal state for each staff. YES if in the middle of doing pedal,
40 * NO if not. */
41static short Pedal_state[MAXSTAFFS + 1];
42static char *Ped_begin_str; /* will point to "\(begped)" */
43static char *Ped_up_down_str; /* will point to "\(pedal)" */
44
45/* static functions */
46static struct STUFF *clone_stufflist P((struct STUFF *stufflist_p,
47 int staffno, int all));
48static void do_attach P((int staffno, int all, struct RANGELIST *vno_range_p));
49static void midi_attach P((int staffno, struct STAFF *staff_p,
50 struct RANGELIST *vno_range_p, int all));
51static void free_stufflist P((struct STUFF *stuff_P));
52static void free_tils P((struct TIL_INFO *til_p));
53static void fix_pedal P((int staffno, struct STUFF *stuff_p));
54static void ped_order_chk P((void));
55\f
56
57/* save current stuff type value. Also check that we are in data (music)
58 * context */
59
60void
61set_stuff_type(stuff_type)
62
63int stuff_type;
64
65{
66 Curr_stuff_type = stuff_type;
67
68 (void) contextcheck(C_MUSIC, "statement");
69}
70\f
71
72/* return current stuff type */
73
74int
75get_stuff_type()
76
77{
78 return(Curr_stuff_type);
79}
80\f
81
82/* check all the things in an input line of stuff, up to the colon,
83 * for consistency, and save interesting info away for later use. */
84
85void
86chk_stuff_header(size, modifier, place, dist_usage)
87
88int size; /* point size, or -1 if to use default */
89int modifier; /* TM_* for text, L_* for phrase */
90int place; /* PL_* */
91int dist_usage; /* SD_* */
92
93{
94
95 debug(4, "chk_stuff_header");
96
97 switch (Curr_stuff_type) {
98 case ST_ROM:
99 case ST_BOLD:
100 case ST_ITAL:
101 case ST_BOLDITAL:
102 case ST_MUSSYM:
103 break;
104 case ST_PEDAL:
105 if (place != PL_BELOW && place != PL_UNKNOWN) {
106 yyerror("pedal must be below");
107 }
108 /*FALLTHRU*/
109 default:
110 if (size != -1) {
111 yyerror("can't specify size except with a font or mussym");
112 }
113 if (modifier != TM_NONE && Curr_stuff_type != ST_PHRASE) {
114 l_yyerror(Curr_filename, yylineno,
115 "can't specify %s except with a font",
116 stuff_modifier(modifier));
117 }
118 if (Curr_stuff_type == ST_PHRASE && modifier != L_NORMAL &&
119 modifier != L_DOTTED && modifier != L_DASHED) {
120 l_yyerror(Curr_filename, yylineno,
121 "only dotted or dashed line type can be specified for phrase");
122 }
123 break;
124 }
125
126 if (Curr_stuff_type == ST_OCTAVE) {
127 if (is_tab_range() == YES) {
128 yyerror("octave not allowed on tablature staff");
129 }
130 else if(place == PL_BETWEEN) {
131 yyerror("octave must be above or below");
132 place = PL_ABOVE;
133 }
134 }
135
136 if (Curr_stuff_type == ST_PHRASE && place == PL_BETWEEN) {
137 yyerror("phrase must be above, below, or omitted");
138 place = PL_ABOVE;
139 }
140
141 if (dist_usage != SD_NONE) {
142 if (Curr_stuff_type == ST_PEDAL) {
143 yyerror("dist not allowed on pedal");
144 }
145 else if (Curr_stuff_type == ST_PHRASE) {
146 yyerror("dist not allowed on phrase");
147 }
148 else if (Curr_stuff_type == ST_MIDI) {
149 yyerror("dist not allowed on midi");
150 }
151
152 if (place == PL_BETWEEN) {
153 yyerror("dist not allowed with 'between'");
154 }
155 }
156
157 /* Save the modifier value.
158 * Have to set this before calling dflt_place() */
159 Modifier = modifier;
160
161 /* fill in default values if user didn't specify */
162 if (place == PL_UNKNOWN) {
163 place = dflt_place();
164 }
165
166 Stuff_size = size;
167 Place = (short) place;
168
169 /* make sure current list of stuff is empty */
170 Head_stufflist_p = Tail_stufflist_p = (struct STUFF *) 0;
171}
172\f
173
174/* return default value for place depending on value of Curr_stuff_type */
175
176int
177dflt_place()
178
179{
180 switch (Curr_stuff_type) {
181
182 case ST_PEDAL:
183 return(PL_BELOW);
184
185 case ST_OCTAVE:
186 yyerror("must specify above or below with octave");
187 /* arbitrarily return above. If we leave it as unknown,
188 * we can get double error messages in some cases */
189 return(PL_ABOVE);
190
191 case ST_PHRASE:
192 /* stays unknown at this point */
193 return(PL_UNKNOWN);
194
195 default:
196 if (Modifier == TM_ANALYSIS || Modifier == TM_FIGBASS) {
197 return(PL_BELOW);
198 }
199 /* default for everything else is above */
200 return(PL_ABOVE);
201 }
202}
203\f
204
205/* Add a space padding to a string (except if is it boxed).
206 * If padding was added, free the passed-in string and return the padded string,
207 * else return the string as is. The incoming string
208 * is expected to already be converted to font/size/string
209 * internal format by this time, although still in input ASCII form.
210 */
211
212char *
213pad_string(string, modifier)
214
215char *string;
216int modifier; /* TM_* */
217
218{
219 char *padded_string; /* string with 1-space padding at end */
220 char *str_p; /* walk through padded_string */
221 int len; /* length of string */
222 int last_was_backslash; /* YES/NO */
223 int count_backslashed; /* YES/NO if to count backslashed or
224 * unbackslashed colons */
225 int colons; /* how many colons found */
226 int extra; /* how many extra bytes to malloc */
227
228 /* Boxed and circled strings don't get any extra padding,
229 * so we can use what we have */
230 if (string[2] == '\\' && (string[3] == '[' || string[3] == '{')) {
231 return(string);
232 }
233
234 /* Make a new copy with a space at the end.
235 * But if the string ends in the middle of a pile,
236 * we need to implicitly end the pile before adding the space.
237 * Since the string is still in ASCII form,
238 * we have to count up the number of colons
239 * to see if we are mid-pile. In chord/analysis/figbass
240 * we need to count unbackslashed colon,
241 * otherwise backslashed.*/
242 count_backslashed = (IS_CHORDLIKE(modifier) ? NO : YES);
243 /* figbass implicitly begins with a pile */
244 colons = (modifier == TM_FIGBASS ? 1 : 0);
245 last_was_backslash = NO;
246 for (str_p = string + 2; *str_p != '\0'; str_p++) {
247 if (last_was_backslash == YES) {
248 if (*str_p == ':' && count_backslashed == YES) {
249 colons++;
250 }
251 last_was_backslash = NO;
252 }
253 else {
254 if (*str_p == ':' && count_backslashed == NO) {
255 colons++;
256 }
257 last_was_backslash = (*str_p == '\\' ? YES : NO);
258 }
259 }
260
261 /* If odd number of colons, we are mid-pile. Will need
262 * add extra byte to hold the colon to implicitly end the
263 * pile, and if it needs to be a backslashed colon,
264 * another extra byte for that. */
265 if (colons & 1) {
266 extra = (count_backslashed == YES ? 2 : 1);
267 }
268 else {
269 extra = 0;
270 }
271
272 len = strlen(string);
273
274 /* +2 is for space/null at end */
275 MALLOCA(char, padded_string, len + 2 + extra);
276 (void) memcpy(padded_string, string, len);
277 str_p = padded_string + len;
278
279 /* add implicit end-pile if needed */
280 if (extra == 2) {
281 *str_p++ = '\\';
282 }
283 if (extra > 0) {
284 *str_p++ = ':';
285 }
286
287 /* now add space padding */
288 *str_p++ = ' ';
289 *str_p = '\0';
290 FREE(string);
291 return(padded_string);
292}
293\f
294
295/* check a "stuff" item and add to list */
296
297void
298add_stuff_item(start_count, start_steps, gracebackup, string, bars, count,
299 dist, dist_usage)
300
301double start_count; /* where in measure to start this stuff */
302double start_steps; /* offset by this many stepsizes */
303int gracebackup; /* how many grace notes to back up from start */
304char *string; /* what to print */
305int bars; /* how many bar lines to cross with this stuff */
306double count; /* how many beats into last measure */
307int dist; /* dist for this specific STUFF, to override param */
308int dist_usage; /* meaning of dist, SD_* */
309
310{
311 struct STUFF *new_p; /* where to store STUFF */
312 struct TIL_INFO *til_info_p; /* to save info about til clause */
313 int len; /* length of stuff text string */
314 char *padded_string; /* string with 1-space padding at end */
315 char lch; /* last character of string */
316
317
318 if (bars != 0 || count != 0.0) {
319 /* has a "til" clause. Check if that is valid */
320 if (Curr_stuff_type == ST_MUSSYM) {
321 if (string == (char *) 0) {
322 yyerror("missing string");
323 return;
324 }
325
326 /* not yet changed to internal form, need to compare
327 * in ASCII form */
328 if ((strcmp(string + 2, "tr") != 0) &&
329 (strcmp(string + 2, "\\(tr)") != 0)) {
330 yyerror("til not allowed on mussym except on trills");
331 }
332 }
333
334 else if (Curr_stuff_type == ST_PEDAL) {
335 yyerror("til not allowed on pedal");
336 }
337
338 else if (Curr_stuff_type == ST_MIDI) {
339 yyerror("til not allowed on midi");
340 }
341
342 if (Curr_stuff_type != ST_PHRASE &&
343 (Modifier == TM_CHORD || Modifier == TM_ANALYSIS) ) {
344 l_yyerror(Curr_filename, yylineno,
345 "til not allowed with %s",
346 stuff_modifier(Modifier));
347 }
348
349 if (bars == 0) {
350 if (count > Score.timenum + 1) {
351 yyerror("'til' value must be <= numerator of time signature + 1");
352 }
353
354 if (count < start_count) {
355 yyerror("til value must be >= start value");
356 }
357 }
358
359 }
360 else {
361 /* doesn't have a "til" clause. Check if one is required */
362 if (Curr_stuff_type == ST_CRESC ||
363 Curr_stuff_type == ST_DECRESC) {
364 yyerror("til required on cresc/decresc");
365 }
366 }
367
368 if (start_count > Score.timenum + 1) {
369 yyerror("beat offset must be <= numerator of time signature + 1");
370 }
371
372 if (Curr_stuff_type == ST_CRESC || Curr_stuff_type == ST_DECRESC) {
373 if (string != (char *) 0) {
374 yyerror("string not allowed with cresc/decresc");
375 }
376 Modifier = TM_DYN;
377 }
378
379 else if (Curr_stuff_type == ST_PHRASE) {
380 if (string != (char *) 0) {
381 yyerror("string not allowed with phrase");
382 }
383 }
384
385 else if (Curr_stuff_type == ST_PEDAL) {
386 if ( (string != (char *) 0)
387 && (strcmp(string + 2, "\\(endped)") != 0) ) {
388 yyerror("pedal string must be either blank or *");
389 }
390 }
391
392 else {
393 if (string == (char *) 0) {
394 yyerror("string is required");
395 return;
396 }
397 }
398
399 if (gracebackup != 0 && Place == PL_BETWEEN) {
400 yyerror("grace backup not allowed with 'between'");
401 }
402
403 /* we can't deal with step offset on phrase marks very well,
404 * so warn and ignore if we get one */
405 if (start_steps != 0.0 && Curr_stuff_type == ST_PHRASE) {
406 l_warning(Curr_filename, yylineno, "step offset ignored on phrase mark");
407 start_steps = 0.0;
408 }
409
410 switch (Curr_stuff_type) {
411 case ST_ROM:
412 case ST_BOLD:
413 case ST_ITAL:
414 case ST_BOLDITAL:
415 /* the text-type stuffs are supposed to have a 1-space padding
416 * at the end of them */
417 if (bars != 0 || count != 0.0) {
418 /* don't add padding if has wavy or solid line
419 * til clause */
420 lch = last_char(string);
421 if (lch == '~' || lch == '_') {
422 break;
423 }
424 }
425 string = pad_string(string, Modifier);
426 break;
427
428 case ST_MUSSYM:
429 /* in mussym, user can specify things without the usual
430 * \(---) convention. Change to include them */
431 if (string[2] == '\\' && string[3] == '(') {
432 /* if user unnecessarily put in the \(--), leave it */
433 break;
434 }
435
436 len = strlen(string + 2);
437 MALLOCA(char, padded_string, len + 6);
438 (void) sprintf(padded_string, "%c%c\\(%s)", FONT_TR, DFLT_SIZE,
439 string + 2);
440 FREE(string);
441 string = padded_string;
442 break;
443
444 default:
445 break;
446 }
447
448 /* fill in a new STUFF struct with appropriate info */
449 new_p = newSTUFF(string, dist, dist_usage, start_count, start_steps,
450 gracebackup, bars, count,
451 Curr_stuff_type, Modifier, Place, Curr_filename, yylineno);
452
453 /* if bars > 0, need to save away til info for later error
454 * checking */
455 if (bars > 0) {
456 CALLOC(TIL_INFO, til_info_p, 1);
457 til_info_p->measnum = Measnum + bars;
458 til_info_p->count = count;
459 til_info_p->inputfile = new_p->inputfile;
460 til_info_p->inputlineno = new_p->inputlineno;
461 til_info_p->next = Til_info_list_p;
462 Til_info_list_p = til_info_p;
463 }
464
465 /* above/between go on the head of the list, below goes on the
466 * tail of the list, so that things come out in the right order.
467 * Midi always goes at the end */
468 if (Place == PL_BELOW || Curr_stuff_type == ST_MIDI) {
469 /* link onto list tail */
470 if ( Tail_stufflist_p == (struct STUFF *) 0) {
471 Head_stufflist_p = new_p;
472 }
473 else {
474 Tail_stufflist_p->next = new_p;
475 }
476 Tail_stufflist_p = new_p;
477 }
478 else {
479 /* link onto head of list */
480 new_p->next = Head_stufflist_p;
481 Head_stufflist_p = new_p;
482 if (Tail_stufflist_p == (struct STUFF *) 0) {
483 Tail_stufflist_p = new_p;
484 }
485 }
486}
487\f
488
489/* return YES if given string consists entirely of the specific music symbol */
490/* the string should be in the internal format of font/size/string */
491
492int
493string_is_sym(string, sym, symfont)
494
495char *string; /* which string to check */
496int sym; /* check for this music symbol */
497int symfont; /* FONT_MUSIC* */
498
499{
500 int font, size;
501
502
503 if (string == (char *) 0) {
504 return(NO);
505 }
506
507 font = *string++;
508 size = *string++;
509 if (next_str_char(&string, &font, &size) != sym) {
510 return(NO);
511 }
512 if (font != symfont) {
513 return(NO);
514 }
515 if (next_str_char(&string, &font, &size) == '\0') {
516 return(YES);
517 }
518 return (NO);
519}
520\f
521
522/* connect a list of STUFF to a STAFF. If there is already something on
523 * that STAFF's STUFF list, attach at the end or beginning as appropriate
524 * depending on place. */
525
526void
527attach_stuff()
528
529{
530 struct SVRANGELIST *svr_p; /* to walk through Svrangelist */
531 struct RANGELIST *r_p; /* to walk through staff range list */
532 short staffno;
533
534
535 debug(4, "attach_stuff");
536
537 /* make sure we've got STAFF structs for this measure */
538 create_staffs();
539
540 for (svr_p = Svrangelist_p; svr_p != (struct SVRANGELIST *) 0;
541 svr_p = svr_p->next) {
542 for (r_p = svr_p->stafflist_p; r_p != (struct RANGELIST *) 0;
543 r_p = r_p->next) {
544
545 for (staffno = r_p->begin; staffno <= r_p->end
546 && staffno <= MAXSTAFFS; staffno++) {
547 do_attach(staffno, r_p->all, svr_p->vnolist_p);
548
549 if (Place == PL_BETWEEN) {
550 /* between has 2 staffs in its range,
551 * but stuff is only associated
552 * with the top staff */
553 break;
554 }
555 }
556 }
557 }
558
559 free_rlists();
560
561 /* have made copies of stuff for each staff that gets one, with
562 * the proper font/size etc, so need to free master stufflist copy */
563 free_stufflist(Head_stufflist_p);
564}
565\f
566
567/* Attach STUFF for a specific staff. */
568
569static void
570do_attach(staffno, all, vno_range_p)
571
572int staffno;
573int all;
574struct RANGELIST *vno_range_p;
575
576{
577 struct STAFF *staff_p; /* where to attach STUFF */
578 struct STUFF *stufflist_p; /* current copy of STUFF list */
579
580
581 if (staffno > Score.staffs) {
582 l_yyerror(Head_stufflist_p->inputfile,
583 Head_stufflist_p->inputlineno,
584 "staff number out of range");
585 return;
586 }
587
588 staff_p = Staffmap_p[staffno]->u.staff_p;
589
590 if (Place == PL_BETWEEN) {
591 if (staffno + 1 > Score.staffs) {
592 /* will have already exclaimed about
593 * this error before, so no need to print message,
594 * but better skip next check */
595 return;
596 }
597
598 /* if either staff of a between is invisible,
599 * throw this stuff away */
600 if (svpath(staffno, VISIBLE)->visible == NO ||
601 svpath(staffno + 1,
602 VISIBLE)->visible == NO) {
603 return;
604 }
605 }
606
607 /* handle MIDI stuff specially */
608 if (Curr_stuff_type == ST_MIDI) {
609 if (all == YES) {
610 /* need to find top visible staff/voice to attach to */
611 int s; /* staff number */
612 int v; /* voice number */
613 struct RANGELIST range;
614
615 v = 1; /* avoid bogus "used before set" warning */
616 for (s = 1; s <= MAXSTAFFS; s++) {
617 if (svpath(s, VISIBLE)->visible == YES) {
618 for (v = 1; v <= MAXVOICES; v++) {
619 if (vvpath(s, v, VISIBLE)->visible == YES) {
620 break;
621 }
622 }
623 if (v <= MAXVOICES) {
624 break;
625 }
626 }
627 }
628 if (s > MAXSTAFFS || v > MAXVOICES) {
629 pfatal("failed to find top visible staff/voice");
630 }
631 /* make a special RANGELIST for this */
632 range.begin = range.end = v;
633 range.all = YES;
634 range.next = 0;
635 midi_attach(s, Staffmap_p[s]->u.staff_p, &range, all);
636 }
637 else {
638 midi_attach(staffno, staff_p, vno_range_p, all);
639 }
640 }
641
642 else {
643 /* make the copy for this staff from master copy */
644 stufflist_p = clone_stufflist(Head_stufflist_p, staffno, all);
645
646 if (Curr_stuff_type == ST_PEDAL) {
647 fix_pedal(staffno, stufflist_p);
648 }
649
650 connect_stuff(staff_p, stufflist_p);
651 }
652}
653\f
654
655/* attach MIDI stuff. This is slightly different than other stuff because
656 * it can be applied to one or both voices. */
657
658static void
659midi_attach(staffno, staff_p, vno_range_p, all)
660
661int staffno; /* attach to this staff number */
662struct STAFF *staff_p; /* attach to this staff struct */
663struct RANGELIST *vno_range_p;
664int all; /* if associated with "all" */
665
666{
667 struct RANGELIST *r_p; /* walk through vno_range_p */
668 int vno; /* voice number */
669 struct STUFF *stufflist_p; /* copy of stuff */
670 struct STUFF *st_p; /* walk through stufflist_p */
671 short place;
672
673
674 /* do for each voice that MIDI stuff applies to */
675 for (r_p = vno_range_p; r_p != (struct RANGELIST *) 0; r_p = r_p->next) {
676 for (vno = r_p->begin; vno <= r_p->end; vno++) {
677
678 /* make the copy for this staff from master copy */
679 stufflist_p = clone_stufflist(Head_stufflist_p,
680 staffno, all);
681
682 /* fix up place based on voice number */
683 switch (vno) {
684 case 1:
685 place = PL_ABOVE;
686 break;
687 case 2:
688 place = PL_BELOW;
689 break;
690 case 3:
691 place = PL_BETWEEN;
692 break;
693 default:
694 pfatal("illegal vno for midi");
695 /*NOTREACHED*/
696 place = PL_UNKNOWN; /* avoid "used before set" warning */
697 break;
698 }
699 for (st_p = stufflist_p; st_p != (struct STUFF *) 0;
700 st_p = st_p->next) {
701 st_p->place = place;
702 }
703
704 connect_stuff(staff_p, stufflist_p);
705 }
706 }
707}
708\f
709
710/* connect a new stuff list into an existing stuff list. Add below stuff and
711 * MIDI stuff to the end of the list,
712 * and others to beginning of list, but make sure any
713 * "above all" comes after any above non-all, and that any below non-all
714 * comes before any "below all."
715 */
716
717void
718connect_stuff(staff_p, stufflist_p)
719
720struct STAFF *staff_p; /* connect to stuff off of this staff */
721struct STUFF *stufflist_p; /* connect this list of stuff */
722
723{
724 struct STUFF *st_p; /* to find link place in STUFF list */
725 struct STUFF *s_p; /* to find end of stufflist_p */
726 struct STUFF **ins_p_p; /* where to insert in list */
727
728
729 if (staff_p == (struct STAFF *) 0 || stufflist_p == (struct STUFF *) 0) {
730 return;
731 }
732
733 if (staff_p->stuff_p == (struct STUFF *) 0) {
734 /* no list before, so attach this one
735 * directly to STAFF */
736 staff_p->stuff_p = stufflist_p;
737 }
738
739 else if (Place == PL_BELOW || stufflist_p->stuff_type == ST_MIDI) {
740 /* if this set of stuff isn't associated with
741 * "all", then it goes before any below "all" stuff */
742 if (stufflist_p->all == NO) {
743 for (ins_p_p = &(staff_p->stuff_p);
744 *ins_p_p != (struct STUFF *) 0;
745 ins_p_p = &((*ins_p_p)->next)) {
746 if ( (*ins_p_p)->place == PL_BELOW &&
747 (*ins_p_p)->all == YES) {
748 break;
749 }
750 }
751 /* find end of list to be inserted */
752 for (s_p = stufflist_p; s_p->next != (struct STUFF *) 0;
753 s_p = s_p->next) {
754 ;
755 }
756
757 /* insert */
758 s_p->next = *ins_p_p;
759 *ins_p_p = stufflist_p;
760 }
761
762 else {
763 /* goes at end of list. find the end */
764 for (st_p = staff_p->stuff_p;
765 st_p->next != (struct STUFF *)0;
766 st_p = st_p->next) {
767 ;
768 }
769
770 /* connect in the new list */
771 st_p->next = stufflist_p;
772 }
773 }
774 else {
775 /* find end of new list */
776 for (s_p = stufflist_p;
777 s_p->next != (struct STUFF *) 0;
778 s_p = s_p->next) {
779 ;
780 }
781
782 if (stufflist_p->all == NO) {
783 /* goes at the head of the list */
784 s_p->next = staff_p->stuff_p;
785 staff_p->stuff_p = stufflist_p;
786 }
787 else {
788 /* goes before any existing above all */
789 for (ins_p_p = &(staff_p->stuff_p);
790 *ins_p_p != (struct STUFF *) 0;
791 ins_p_p = &((*ins_p_p)->next)) {
792 if ( (*ins_p_p)->place == PL_ABOVE &&
793 (*ins_p_p)->all == YES) {
794 break;
795 }
796 }
797 /* find end of list to be inserted */
798 for (s_p = stufflist_p; s_p->next != (struct STUFF *) 0;
799 s_p = s_p->next) {
800 ;
801 }
802
803 /* insert */
804 s_p->next = *ins_p_p;
805 *ins_p_p = stufflist_p;
806 }
807 }
808}
809\f
810
811/* given a list of STUFF, return a clone of the list */
812
813static struct STUFF *
814clone_stufflist(stufflist_p, staffno, all)
815
816struct STUFF *stufflist_p; /* what stuff to clone */
817int staffno; /* which staff, to get proper point size */
818int all; /* YES if was "above all" or "below all" */
819
820{
821 struct STUFF *new_p; /* copy of STUFF */
822 char *newstring; /* copy of text string */
823 int font;
824 int fontfamily;
825 int size;
826
827
828 if (stufflist_p == (struct STUFF *) 0) {
829 return( (struct STUFF *) 0 );
830 }
831
832 /* make copy of string with appropriate font and size */
833 if (stufflist_p->string != (char *) 0) {
834 switch(stufflist_p->stuff_type) {
835 case ST_BOLD:
836 font = FONT_TB;
837 break;
838 case ST_OCTAVE:
839 Stuff_size = DFLT_SIZE;
840 font = FONT_TI;
841 break;
842 case ST_ITAL:
843 font = FONT_TI;
844 break;
845 case ST_BOLDITAL:
846 font = FONT_TX;
847 break;
848 default:
849 font = FONT_TR;
850 break;
851 }
852
853 /* figure out the proper size if not already determined */
854 if (Stuff_size < 0) {
855 if (all == YES) {
856 size = Score.size;
857 }
858 else {
859 size = svpath(staffno, SIZE)->size;
860 }
861 }
862 else {
863 size = Stuff_size;
864 }
865
866 /* determine fontfamily and font if not already known */
867 if (Curr_family == FAMILY_DFLT) {
868 if (all == YES) {
869 fontfamily = Score.fontfamily;
870 }
871 else {
872 fontfamily = svpath(staffno, FONTFAMILY)->
873 fontfamily;
874 }
875 }
876 else {
877 fontfamily = Curr_family;
878 }
879
880 /* clone text string */
881 newstring = copy_string(stufflist_p->string + 2, font, size);
882 if (IS_CHORDLIKE(Modifier)) {
883 newstring = modify_chstr(newstring, Modifier);
884 }
885 fix_string(newstring, fontfamily + font, size,
886 stufflist_p->inputfile, stufflist_p->inputlineno);
887 if (Modifier == TM_FIGBASS || Modifier == TM_ANALYSIS) {
888 newstring = acc_trans(newstring);
889 }
890 }
891 else {
892 newstring = (char *) 0;
893 }
894
895 /* create and fill in clone of stuff, then return it */
896 new_p = newSTUFF(newstring, stufflist_p->dist,
897 stufflist_p->dist_usage,
898 stufflist_p->start.count,
899 stufflist_p->start.steps,
900 stufflist_p->gracebackup,
901 stufflist_p->end.bars, stufflist_p->end.count,
902 stufflist_p->stuff_type, stufflist_p->modifier,
903 stufflist_p->place, stufflist_p->inputfile,
904 stufflist_p->inputlineno);
905 new_p->all = (short) all;
906 new_p->next = clone_stufflist(stufflist_p->next, staffno, all);
907 return(new_p);
908}
909\f
910
911/* allocate a STUFF and fill in all the values given. Initialize carry fields
912 * and "all" to NO. Leave coordinates and next link as 0.
913 * Note that the string pointer
914 * is copied; it does not make a copy of the string itself, so never call this
915 * function more than once with the same string--make a copy. */
916
917struct STUFF *
918newSTUFF(string, dist, dist_usage, start_count, start_steps, gracebackup, bars, count,
919 stuff_type, modifier, place, inputfile, inputlineno)
920
921char *string; /* text string of stuff */
922int dist; /* dist for this STUFF to override dist parameter */
923int dist_usage; /* meaning of dist, SD_* */
924double start_count; /* count at which to begin stuff */
925double start_steps; /* offset by this many steps */
926int gracebackup; /* how many grace notes to back up from start */
927int bars; /* bars in "til" clasue */
928double count; /* counts in "til" clause */
929int stuff_type; /* ST_* */
930int modifier; /* TM_* */
931int place; /* PL_* */
932char *inputfile; /* which file stuff was defined in */
933int inputlineno; /* where stuff was defined in input file */
934
935{
936 struct STUFF *new_p; /* the new STUFF to fill in */
937
938
939 CALLOC(STUFF, new_p, 1);
940 new_p->string = string;
941 new_p->start.count = start_count;
942 new_p->start.steps = start_steps;
943 new_p->gracebackup = (short) gracebackup;
944 new_p->dist = (short) dist;
945 new_p->dist_usage = (short) dist_usage;
946 new_p->end.bars = (short) bars;
947 new_p->end.count = count;
948 new_p->stuff_type = (short) stuff_type;
949 new_p->modifier = (short) modifier;
950 new_p->place = (short) place;
951 new_p->carryin = new_p->carryout = new_p->all = NO;
952 new_p->costuff_p = 0;
953 new_p->inputfile = inputfile;
954 new_p->inputlineno = (short) inputlineno;
955
956 return(new_p);
957}
958\f
959
960/* recursively free up a stufflist and any strings hanging off of it */
961
962static void
963free_stufflist(stuff_p)
964
965struct STUFF *stuff_p;
966
967{
968 if (stuff_p == (struct STUFF *) 0 ) {
969 return;
970 }
971
972 free_stufflist(stuff_p->next);
973 if (stuff_p->string != (char *) 0) {
974 FREE(stuff_p->string);
975 }
976 FREE(stuff_p);
977}
978\f
979
980/* at each bar line, see if there are any "til" clauses that are supposed
981 * to end in this measure. If so, make sure they end within the time
982 * signature for this measure. */
983
984void
985meas_stuff_chk()
986
987{
988 struct TIL_INFO *til_info_p; /* to index thru list */
989 struct TIL_INFO **del_place_p_p; /* for deleting from list */
990 struct TIL_INFO *one2free_p; /* pointer to which element
991 * to free */
992
993 debug(2, "meas_chk_stuff");
994
995 /* update measure number to conpensate for any multirests */
996 Measnum += Multi_adjust;
997 Multi_adjust = 0;
998
999 /* go through list of in-progress til clauses */
1000 for (til_info_p = Til_info_list_p, del_place_p_p = &Til_info_list_p;
1001 til_info_p != (struct TIL_INFO *) 0; ) {
1002
1003 if (til_info_p->measnum == Measnum) {
1004
1005 /* at measure where this til clause ends */
1006 /* check if within time signature */
1007 if (til_info_p->count > Score.timenum + 1.0) {
1008 l_yyerror(til_info_p->inputfile,
1009 til_info_p->inputlineno,
1010 "beats in 'til' clause must be <= numerator of time signature + 1 of the measure in which the 'til' clause ends (i.e., <= %d)",
1011 Score.timenum);
1012 }
1013
1014 /* this one has been taken care of: delete from list */
1015 *del_place_p_p = til_info_p->next;
1016 one2free_p = til_info_p;
1017 }
1018 else if (til_info_p->measnum < Measnum) {
1019 /* must have ended inside a multirest, so delete
1020 * from list */
1021 *del_place_p_p = til_info_p->next;
1022 one2free_p = til_info_p;
1023 }
1024 else {
1025 /* this one stays on the list for now, so move pointer
1026 * to where to potentially delete to next element */
1027 del_place_p_p = &(til_info_p->next);
1028 one2free_p = (struct TIL_INFO *) 0;
1029 }
1030
1031 /* have to move to next element
1032 * before freeing the current one */
1033 til_info_p = til_info_p->next;
1034
1035 if (one2free_p != (struct TIL_INFO *) 0) {
1036 FREE(one2free_p);
1037 }
1038 }
1039
1040 /* update number of measures. */
1041 Measnum++;
1042
1043 /* make sure pedal marks are in proper order */
1044 ped_order_chk();
1045}
1046\f
1047
1048/* adjust number of measures to account for multirests. Called when there is
1049 * a multirest. Saved the number of measures in the multirest (minus 1 since
1050 * the barline at the end will count for one measure) */
1051
1052void
1053multi_stuff(nmeas)
1054
1055int nmeas; /* number of measures in multirest */
1056
1057{
1058 /* subtract 1 to account for the fact that at the bar line at the
1059 * end of the multirest we will peg the measure counter */
1060 Multi_adjust = nmeas - 1;
1061}
1062\f
1063
1064/* handle pedal going into endings. When we hit a first ending, save the
1065 * state of the pedal for all staffs. On subsequent endings in the set,
1066 * reset the pedal state to what it was at the beginning of the first ending.
1067 * At the endending, go back to normal operation. This is similar to
1068 * the saveped() function used at print time. */
1069
1070void
1071ped_endings(endingloc)
1072
1073int endingloc; /* STARTITEM, INITEM, etc */
1074
1075{
1076 register int s; /* staff index */
1077
1078
1079 if (endingloc == STARTITEM) {
1080 if (Ped_snapshot[0] == YES) {
1081
1082 /* starting 2nd ending: restore pedal state as it was
1083 * at beginning of first ending */
1084 for (s = 1; s <= MAXSTAFFS; s++) {
1085 Pedal_state[s] = Ped_snapshot[s];
1086 }
1087 }
1088
1089 else {
1090 /* starting a set of endings,
1091 * need to save pedal state at this
1092 * point so we can carry it into subsequent endings */
1093 for (s = 1; s <= Score.staffs; s++) {
1094 Ped_snapshot[s] = Pedal_state[s];
1095 }
1096 /* make sure any remaining staffs are set to pedal off,
1097 * in case user increases the number of staffs
1098 * during the endings... */
1099 for ( ; s <= MAXSTAFFS; s++) {
1100 Ped_snapshot[s] = NO;
1101 }
1102
1103 /* mark that we now have a snapshot */
1104 Ped_snapshot[0] = YES;
1105 }
1106 }
1107
1108 else if (endingloc == ENDITEM) {
1109 /* at end of endings, discard snapshot of pedal states */
1110 Ped_snapshot[0] = NO;
1111 }
1112}
1113\f
1114
1115/* When all input has been processed, or when changing the number
1116 * of staffs, we better not have any 'til' clauses
1117 * still unfinished. If we do, print a warning message. */
1118
1119void
1120chk4dangling_til_clauses(boundary_desc)
1121
1122char *boundary_desc; /* "the end of the song" or
1123 * "a change in number of staffs" */
1124
1125{
1126 struct TIL_INFO *til_info_p;
1127
1128
1129 debug(2, "chk4dangling_til_clauses");
1130
1131 /* Go through the whole list of remaining til clauses,
1132 * and print a warning message for each. */
1133 for (til_info_p = Til_info_list_p; til_info_p != (struct TIL_INFO *) 0;
1134 til_info_p = til_info_p->next) {
1135
1136 /* If right on the boundary or spills over only a very tiny
1137 * amount, don't bother to complain */
1138 if (til_info_p->measnum - Measnum == 0
1139 && til_info_p->count < .001) {
1140 continue;
1141 }
1142
1143 l_warning(til_info_p->inputfile, til_info_p->inputlineno,
1144 "'til' clause extends beyond %s by %dm + %.3f",
1145 boundary_desc, til_info_p->measnum - Measnum,
1146 til_info_p->count);
1147 }
1148
1149 /* mop up. */
1150 free_tils(Til_info_list_p);
1151 Til_info_list_p = (struct TIL_INFO *) 0;
1152}
1153\f
1154
1155/* recursively free a list of TIL_INFO structs */
1156
1157static void
1158free_tils(til_p)
1159
1160struct TIL_INFO *til_p; /* free this list */
1161
1162{
1163 if (til_p == (struct TIL_INFO *) 0) {
1164 return;
1165 }
1166
1167 free_tils(til_p->next);
1168 FREE(til_p);
1169}
1170\f
1171
1172/* user only has to specify when pedal marks end. We deduce from current
1173 * pedal state whether a pedal mark is begin or up/down. This gets called
1174 * whenever we have a list of pedal STUFFs. Later we enforce that pedal
1175 * marks are put in in ascending order only, so that if user enters more
1176 * than one pedal line for the same staff, that will be handled properly. */
1177
1178static void
1179fix_pedal(staffno, stuff_p)
1180
1181int staffno; /* pedal is for this staff */
1182struct STUFF *stuff_p; /* list of pedal mark info */
1183
1184{
1185 /* walk through list of pedal marks */
1186 for ( ; stuff_p != (struct STUFF *) 0; stuff_p = stuff_p->next) {
1187
1188 if (stuff_p->string == (char *) 0) {
1189 /* no star, so have to deduce state */
1190
1191 if (Pedal_state[staffno] == NO) {
1192 /* pedal currently off, so begin pedal */
1193 Pedal_state[staffno] = YES;
1194 stuff_p->string = copy_string(Ped_begin_str + 2,
1195 (int) Ped_begin_str[0],
1196 (int) Ped_begin_str[1]);
1197 }
1198 else {
1199 /* pedal currently down, so pedal up/down */
1200 stuff_p->string = copy_string(Ped_up_down_str + 2,
1201 (int) Ped_up_down_str[0],
1202 (int) Ped_up_down_str[1]);
1203 }
1204 }
1205
1206 else if (Pedal_state[staffno] == NO) {
1207 yyerror("can't end pedal -- none in progress");
1208 }
1209
1210 else {
1211 /* user gave star, so end pedal */
1212 Pedal_state[staffno] = NO;
1213 }
1214 }
1215}
1216\f
1217
1218/* reset pedal states for all staffs. This should be called at init time
1219 * and at any time when the number of staffs changes. This function also
1220 * initializes the Ped_begin_str and Ped_up_down_str. */
1221
1222void
1223reset_ped_state()
1224
1225{
1226 static int first_time = YES; /* flag if function called before */
1227 register int s; /* index through staffs */
1228
1229
1230 /* mark pedal off for all staffs */
1231 for (s = 1; s <= Score.staffs; s++) {
1232 Pedal_state[s] = NO;
1233 }
1234 Ped_snapshot[0] = NO;
1235
1236 /* the first time this function is called, initialize the strings
1237 * for pedal begin and pedal end. We just have one copy of these
1238 * and then make as many copies from these as necessary */
1239 if (first_time == YES) {
1240 first_time = NO;
1241 Ped_begin_str = copy_string("\\(begped)", FONT_MUSIC,
1242 DFLT_SIZE);
1243 Ped_up_down_str = copy_string("\\(pedal)", FONT_MUSIC,
1244 DFLT_SIZE);
1245 fix_string(Ped_begin_str, FONT_MUSIC, DFLT_SIZE,
1246 Curr_filename, -1);
1247 fix_string(Ped_up_down_str, FONT_MUSIC, DFLT_SIZE,
1248 Curr_filename, -1);
1249 }
1250}
1251\f
1252
1253/* fill in rehearsal mark string. This doesn't go in a STUFF, but it's
1254 * sort of like stuff and there didn't seem to be any more appropriate file for
1255 * this function */
1256
1257
1258static int Reh_let = 0; /* current value of rehearsal letter. 0 == "A",
1259 * 25 == "Z", 26 == "AA", etc to 701 == "ZZ" */
1260static int Reh_num = 1; /* current value of rehearsal number */
1261
1262
1263void
1264set_reh_string(bar_p, fontfamily, font, size, string)
1265
1266struct BAR *bar_p; /* which bar gets the rehearsal mark */
1267int fontfamily; /* what font family to use, or FAMILY_DFLT
1268 * if to use current default */
1269int font; /* what font to use, or FONT_UNKNOWN if to use the
1270 * current default font */
1271int size; /* font size to use, or -1 if to use current default */
1272char *string; /* string for rehearsal mark */
1273
1274{
1275 char reh_str[12]; /* temporary buff for string version of
1276 * rehearsal number or letter */
1277 static int reh_size = DFLT_SIZE; /* size to use for reh marks */
1278 static int reh_family = FAMILY_DFLT; /* font family to use */
1279 static int reh_font = FONT_TB; /* font to use */
1280
1281
1282 /* if first time through, init the font family to the score family */
1283 if (reh_family == FAMILY_DFLT) {
1284 reh_family = Score.fontfamily;
1285 }
1286
1287 /* if user specified a new size, save that */
1288 if (size != -1) {
1289 if (size > 100) {
1290 yyerror("reh mark size too large");
1291 return;
1292 }
1293 else {
1294 reh_size = size;
1295 }
1296 }
1297
1298 /* if user specified new font or font family, save that */
1299 if (font != FONT_UNKNOWN) {
1300 reh_font = font;
1301 }
1302 if (fontfamily != FAMILY_DFLT) {
1303 reh_family = fontfamily;
1304 }
1305
1306 switch(bar_p->reh_type) {
1307
1308 case REH_NUM:
1309 /* get string version of current rehearsal number, and
1310 * incrment it */
1311 bar_p->reh_string = copy_string(num2str(Reh_num++) + 2,
1312 reh_family + reh_font, reh_size);
1313 break;
1314
1315 case REH_LET:
1316 /* Get string version of current rehearsal letter.
1317 * Start with A-Z, then AA, AB, AC, ... BA, BB, ... up to ZZ.
1318 */
1319 if (Reh_let < 26) {
1320 /* 1-letter long mark */
1321 (void) sprintf(reh_str, "%c", Reh_let + 'A');
1322 }
1323 else if (Reh_let < 27 * 26) {
1324 /* 2-letter long mark */
1325 (void) sprintf(reh_str, "%c%c",
1326 (Reh_let / 26) + 'A' - 1, (Reh_let % 26) + 'A');
1327 }
1328 else {
1329 ufatal("too many rehearsal letters!");
1330 }
1331 bar_p->reh_string = copy_string(reh_str,
1332 reh_family + reh_font, reh_size);
1333 /* increment for next time around */
1334 Reh_let++;
1335 break;
1336
1337 case REH_MNUM:
1338 /* get string version of current measure number */
1339 bar_p->reh_string = copy_string(num2str(Meas_num) + 2,
1340 reh_family + reh_font, reh_size);
1341 break;
1342
1343 case REH_STRING:
1344 /* user-specified string */
1345 bar_p->reh_string = fix_string(string,
1346 reh_family + reh_font, reh_size,
1347 Curr_filename, yylineno);
1348 break;
1349
1350 case REH_NONE:
1351 break;
1352
1353 default:
1354 pfatal("set_reh_string passed bad value");
1355 break;
1356 }
1357}
1358\f
1359
1360/* Set rehearsal letter or number to user-specified value.
1361 * If the current bar has a rehearsal mark of the type being changed,
1362 * also replace its current mark with the changed one. This allows user
1363 * to say either
1364 * reh num num=5
1365 * or
1366 * num=5 reh num
1367 * and get the same results, which is consistent with how mnum= setting
1368 * had worked.
1369 */
1370
1371void
1372init_reh(rehnumber, rehletter, mainbar_p)
1373
1374int rehnumber; /* New value for Reh_num or negative if setting Reh_let */
1375char *rehletter; /* "A" to "ZZ" or null if setting number */
1376struct MAINLL *mainbar_p; /* points to the current BAR */
1377
1378{
1379 struct BAR *bar_p;
1380 char *oldstr; /* previous reh_string */
1381
1382 if (mainbar_p == 0 || mainbar_p->str != S_BAR) {
1383 pfatal("bad mainbar_p passed to init_reh");
1384 }
1385 bar_p = mainbar_p->u.bar_p;
1386 oldstr = bar_p->reh_string;
1387
1388 if (rehnumber >= 0) {
1389 Reh_num = rehnumber;
1390 /* If this bar has a rehearsal number on this bar,
1391 * replace it, and free the old one. */
1392 if (bar_p->reh_type == REH_NUM) {
1393 set_reh_string(bar_p, FAMILY_DFLT, FONT_UNKNOWN, -1,
1394 (char *) 0);
1395 FREE(oldstr);
1396 }
1397 }
1398
1399 if (rehletter != 0) {
1400 /* Letter is stored internally as a number,
1401 * which is then converted, so we have to convert in reverse.
1402 * We only allow "A" through "ZZ" */
1403 if (isupper(rehletter[0]) && rehletter[1] == '\0') {
1404 Reh_let = rehletter[0] - 'A';
1405 }
1406 else if (isupper(rehletter[0]) && isupper(rehletter[1])
1407 && rehletter[2] == '\0') {
1408 Reh_let = 26 + (rehletter[1] - 'A')
1409 + (rehletter[0] - 'A') * 26;
1410 }
1411 else {
1412 yyerror("rehearsal letter setting must be \"A\" through \"ZZ\"");
1413 return;
1414 }
1415 /* If this bar has a rehearsal letter on this bar,
1416 * replace it, and free the old one. */
1417 if (bar_p->reh_type == REH_LET) {
1418 set_reh_string(bar_p, FAMILY_DFLT, FONT_UNKNOWN, -1,
1419 (char *) 0);
1420 FREE(oldstr);
1421 }
1422 }
1423}
1424\f
1425
1426/* go through all stuff lists and verify that pedal marks are given in
1427 * ascending order. If not, error. Some code in both parse and
1428 * placement phases requires that pedal marks be in order. */
1429
1430static void
1431ped_order_chk()
1432
1433{
1434 int staffno;
1435 struct STUFF *stuff_p; /* walk through stuff list */
1436 float last_ped_count; /* count where last pedal occurred */
1437 int last_backup; /* gracebackup of last pedal */
1438
1439
1440 /* check every staff */
1441 for (staffno = 1; staffno <= Score.staffs; staffno++) {
1442
1443 /* initialize for current staff */
1444 last_ped_count = -1.0;
1445 last_backup = 0;
1446
1447 /* go through stuff list for current staff, looking for pedal */
1448 for (stuff_p = Staffmap_p[staffno]->u.staff_p->stuff_p;
1449 stuff_p != (struct STUFF *) 0;
1450 stuff_p = stuff_p->next) {
1451 if (stuff_p->stuff_type == ST_PEDAL) {
1452
1453 /* found a pedal. Make sure it is later than
1454 * the previous pedal */
1455 if (stuff_p->start.count < last_ped_count ||
1456 (stuff_p->start.count
1457 == last_ped_count
1458 && stuff_p->gracebackup
1459 > last_backup) ) {
1460 l_yyerror(stuff_p->inputfile,
1461 stuff_p->inputlineno,
1462 "pedal must be specified in ascending order");
1463 /* no need to print error more than
1464 * once if multiple errors */
1465 continue;
1466 }
1467
1468 /* keep track of where this pedal is, for
1469 * comparing with the next one */
1470 last_ped_count = stuff_p->start.count;
1471 last_backup = stuff_p->gracebackup;
1472 }
1473 }
1474 }
1475}
1476\f
1477
1478/* Translate STUFF text modifier to a printable string. */
1479
1480char *
1481stuff_modifier(modifier)
1482
1483int modifier;
1484
1485{
1486 switch (modifier) {
1487
1488 case TM_CHORD:
1489 return("chord");
1490 case TM_ANALYSIS:
1491 return("analysis");
1492 case TM_FIGBASS:
1493 return("figbass");
1494 case TM_DYN:
1495 return("dyn");
1496 case TM_NONE:
1497 return("(no modifier)");
1498 default:
1499 return("(invalid modifier)");
1500 }
1501}