--- /dev/null
+
+/* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2002, 2003, 2004 by Arkkra Enterprises */
+/* All rights reserved */
+
+/* parser functions related to STUFF */
+
+
+#include <string.h>
+#include "defines.h"
+#include "structs.h"
+#include "globals.h"
+
+/* if user specifies a "til" clause on stuff with a number of measures > 0,
+ * we need to save away info about where the til clause will end, to make sure
+ * that it doesn't fall off the end of the measure or the piece. This is the
+ * struct we use to save this info. */
+static struct TIL_INFO {
+ char *inputfile; /* where STUFF was defined */
+ int inputlineno; /* where STUFF was defined */
+ int measnum; /* number of measure in which til clause ends */
+ float count; /* count in measure where til clause ends */
+ struct TIL_INFO *next; /* for linked list */
+} *Til_info_list_p;
+
+/* info about the STUFF currently being collected from input */
+static int Curr_stuff_type; /* ST_* */
+static int Stuff_size; /* point size of stuff text string */
+static int Modifier; /* TM_* for text, L_* for phrase */
+static int Measnum = 1; /* to check til clauses. Can't use Meas_num
+ * global because it doesn't count invisible
+ * bars but til clauses do */
+static int Multi_adjust; /* adjustment to Measnum to account
+ * for multirests */
+
+/* head and tail of list of STUFF currently being collected from input */
+static struct STUFF *Head_stufflist_p;
+static struct STUFF *Tail_stufflist_p;
+
+/* current pedal state for each staff. YES if in the middle of doing pedal,
+ * NO if not. */
+static short Pedal_state[MAXSTAFFS + 1];
+static char *Ped_begin_str; /* will point to "\(begped)" */
+static char *Ped_up_down_str; /* will point to "\(pedal)" */
+
+/* static functions */
+static struct STUFF *clone_stufflist P((struct STUFF *stufflist_p,
+ int staffno, int all));
+static void do_attach P((int staffno, int all, struct RANGELIST *vno_range_p));
+static void midi_attach P((int staffno, struct STAFF *staff_p,
+ struct RANGELIST *vno_range_p, int all));
+static void free_stufflist P((struct STUFF *stuff_P));
+static void free_tils P((struct TIL_INFO *til_p));
+static void fix_pedal P((int staffno, struct STUFF *stuff_p));
+static void ped_order_chk P((void));
+\f
+
+/* save current stuff type value. Also check that we are in data (music)
+ * context */
+
+void
+set_stuff_type(stuff_type)
+
+int stuff_type;
+
+{
+ Curr_stuff_type = stuff_type;
+
+ (void) contextcheck(C_MUSIC, "statement");
+}
+\f
+
+/* return current stuff type */
+
+int
+get_stuff_type()
+
+{
+ return(Curr_stuff_type);
+}
+\f
+
+/* check all the things in an input line of stuff, up to the colon,
+ * for consistency, and save interesting info away for later use. */
+
+void
+chk_stuff_header(size, modifier, place, dist_usage)
+
+int size; /* point size, or -1 if to use default */
+int modifier; /* TM_* for text, L_* for phrase */
+int place; /* PL_* */
+int dist_usage; /* SD_* */
+
+{
+
+ debug(4, "chk_stuff_header");
+
+ switch (Curr_stuff_type) {
+ case ST_ROM:
+ case ST_BOLD:
+ case ST_ITAL:
+ case ST_BOLDITAL:
+ case ST_MUSSYM:
+ break;
+ case ST_PEDAL:
+ if (place != PL_BELOW && place != PL_UNKNOWN) {
+ yyerror("pedal must be below");
+ }
+ /*FALLTHRU*/
+ default:
+ if (size != -1) {
+ yyerror("can't specify size except with a font or mussym");
+ }
+ if (modifier != TM_NONE && Curr_stuff_type != ST_PHRASE) {
+ l_yyerror(Curr_filename, yylineno,
+ "can't specify %s except with a font",
+ stuff_modifier(modifier));
+ }
+ if (Curr_stuff_type == ST_PHRASE && modifier != L_NORMAL &&
+ modifier != L_DOTTED && modifier != L_DASHED) {
+ l_yyerror(Curr_filename, yylineno,
+ "only dotted or dashed line type can be specified for phrase");
+ }
+ break;
+ }
+
+ if (Curr_stuff_type == ST_OCTAVE) {
+ if (is_tab_range() == YES) {
+ yyerror("octave not allowed on tablature staff");
+ }
+ else if(place == PL_BETWEEN) {
+ yyerror("octave must be above or below");
+ place = PL_ABOVE;
+ }
+ }
+
+ if (Curr_stuff_type == ST_PHRASE && place == PL_BETWEEN) {
+ yyerror("phrase must be above, below, or omitted");
+ place = PL_ABOVE;
+ }
+
+ if (dist_usage != SD_NONE) {
+ if (Curr_stuff_type == ST_PEDAL) {
+ yyerror("dist not allowed on pedal");
+ }
+ else if (Curr_stuff_type == ST_PHRASE) {
+ yyerror("dist not allowed on phrase");
+ }
+ else if (Curr_stuff_type == ST_MIDI) {
+ yyerror("dist not allowed on midi");
+ }
+
+ if (place == PL_BETWEEN) {
+ yyerror("dist not allowed with 'between'");
+ }
+ }
+
+ /* Save the modifier value.
+ * Have to set this before calling dflt_place() */
+ Modifier = modifier;
+
+ /* fill in default values if user didn't specify */
+ if (place == PL_UNKNOWN) {
+ place = dflt_place();
+ }
+
+ Stuff_size = size;
+ Place = (short) place;
+
+ /* make sure current list of stuff is empty */
+ Head_stufflist_p = Tail_stufflist_p = (struct STUFF *) 0;
+}
+\f
+
+/* return default value for place depending on value of Curr_stuff_type */
+
+int
+dflt_place()
+
+{
+ switch (Curr_stuff_type) {
+
+ case ST_PEDAL:
+ return(PL_BELOW);
+
+ case ST_OCTAVE:
+ yyerror("must specify above or below with octave");
+ /* arbitrarily return above. If we leave it as unknown,
+ * we can get double error messages in some cases */
+ return(PL_ABOVE);
+
+ case ST_PHRASE:
+ /* stays unknown at this point */
+ return(PL_UNKNOWN);
+
+ default:
+ if (Modifier == TM_ANALYSIS || Modifier == TM_FIGBASS) {
+ return(PL_BELOW);
+ }
+ /* default for everything else is above */
+ return(PL_ABOVE);
+ }
+}
+\f
+
+/* Add a space padding to a string (except if is it boxed).
+ * If padding was added, free the passed-in string and return the padded string,
+ * else return the string as is. The incoming string
+ * is expected to already be converted to font/size/string
+ * internal format by this time, although still in input ASCII form.
+ */
+
+char *
+pad_string(string, modifier)
+
+char *string;
+int modifier; /* TM_* */
+
+{
+ char *padded_string; /* string with 1-space padding at end */
+ char *str_p; /* walk through padded_string */
+ int len; /* length of string */
+ int last_was_backslash; /* YES/NO */
+ int count_backslashed; /* YES/NO if to count backslashed or
+ * unbackslashed colons */
+ int colons; /* how many colons found */
+ int extra; /* how many extra bytes to malloc */
+
+ /* Boxed and circled strings don't get any extra padding,
+ * so we can use what we have */
+ if (string[2] == '\\' && (string[3] == '[' || string[3] == '{')) {
+ return(string);
+ }
+
+ /* Make a new copy with a space at the end.
+ * But if the string ends in the middle of a pile,
+ * we need to implicitly end the pile before adding the space.
+ * Since the string is still in ASCII form,
+ * we have to count up the number of colons
+ * to see if we are mid-pile. In chord/analysis/figbass
+ * we need to count unbackslashed colon,
+ * otherwise backslashed.*/
+ count_backslashed = (IS_CHORDLIKE(modifier) ? NO : YES);
+ /* figbass implicitly begins with a pile */
+ colons = (modifier == TM_FIGBASS ? 1 : 0);
+ last_was_backslash = NO;
+ for (str_p = string + 2; *str_p != '\0'; str_p++) {
+ if (last_was_backslash == YES) {
+ if (*str_p == ':' && count_backslashed == YES) {
+ colons++;
+ }
+ last_was_backslash = NO;
+ }
+ else {
+ if (*str_p == ':' && count_backslashed == NO) {
+ colons++;
+ }
+ last_was_backslash = (*str_p == '\\' ? YES : NO);
+ }
+ }
+
+ /* If odd number of colons, we are mid-pile. Will need
+ * add extra byte to hold the colon to implicitly end the
+ * pile, and if it needs to be a backslashed colon,
+ * another extra byte for that. */
+ if (colons & 1) {
+ extra = (count_backslashed == YES ? 2 : 1);
+ }
+ else {
+ extra = 0;
+ }
+
+ len = strlen(string);
+
+ /* +2 is for space/null at end */
+ MALLOCA(char, padded_string, len + 2 + extra);
+ (void) memcpy(padded_string, string, len);
+ str_p = padded_string + len;
+
+ /* add implicit end-pile if needed */
+ if (extra == 2) {
+ *str_p++ = '\\';
+ }
+ if (extra > 0) {
+ *str_p++ = ':';
+ }
+
+ /* now add space padding */
+ *str_p++ = ' ';
+ *str_p = '\0';
+ FREE(string);
+ return(padded_string);
+}
+\f
+
+/* check a "stuff" item and add to list */
+
+void
+add_stuff_item(start_count, start_steps, gracebackup, string, bars, count,
+ dist, dist_usage)
+
+double start_count; /* where in measure to start this stuff */
+double start_steps; /* offset by this many stepsizes */
+int gracebackup; /* how many grace notes to back up from start */
+char *string; /* what to print */
+int bars; /* how many bar lines to cross with this stuff */
+double count; /* how many beats into last measure */
+int dist; /* dist for this specific STUFF, to override param */
+int dist_usage; /* meaning of dist, SD_* */
+
+{
+ struct STUFF *new_p; /* where to store STUFF */
+ struct TIL_INFO *til_info_p; /* to save info about til clause */
+ int len; /* length of stuff text string */
+ char *padded_string; /* string with 1-space padding at end */
+ char lch; /* last character of string */
+
+
+ if (bars != 0 || count != 0.0) {
+ /* has a "til" clause. Check if that is valid */
+ if (Curr_stuff_type == ST_MUSSYM) {
+ if (string == (char *) 0) {
+ yyerror("missing string");
+ return;
+ }
+
+ /* not yet changed to internal form, need to compare
+ * in ASCII form */
+ if ((strcmp(string + 2, "tr") != 0) &&
+ (strcmp(string + 2, "\\(tr)") != 0)) {
+ yyerror("til not allowed on mussym except on trills");
+ }
+ }
+
+ else if (Curr_stuff_type == ST_PEDAL) {
+ yyerror("til not allowed on pedal");
+ }
+
+ else if (Curr_stuff_type == ST_MIDI) {
+ yyerror("til not allowed on midi");
+ }
+
+ if (Curr_stuff_type != ST_PHRASE &&
+ (Modifier == TM_CHORD || Modifier == TM_ANALYSIS) ) {
+ l_yyerror(Curr_filename, yylineno,
+ "til not allowed with %s",
+ stuff_modifier(Modifier));
+ }
+
+ if (bars == 0) {
+ if (count > Score.timenum + 1) {
+ yyerror("'til' value must be <= numerator of time signature + 1");
+ }
+
+ if (count < start_count) {
+ yyerror("til value must be >= start value");
+ }
+ }
+
+ }
+ else {
+ /* doesn't have a "til" clause. Check if one is required */
+ if (Curr_stuff_type == ST_CRESC ||
+ Curr_stuff_type == ST_DECRESC) {
+ yyerror("til required on cresc/decresc");
+ }
+ }
+
+ if (start_count > Score.timenum + 1) {
+ yyerror("beat offset must be <= numerator of time signature + 1");
+ }
+
+ if (Curr_stuff_type == ST_CRESC || Curr_stuff_type == ST_DECRESC) {
+ if (string != (char *) 0) {
+ yyerror("string not allowed with cresc/decresc");
+ }
+ Modifier = TM_DYN;
+ }
+
+ else if (Curr_stuff_type == ST_PHRASE) {
+ if (string != (char *) 0) {
+ yyerror("string not allowed with phrase");
+ }
+ }
+
+ else if (Curr_stuff_type == ST_PEDAL) {
+ if ( (string != (char *) 0)
+ && (strcmp(string + 2, "\\(endped)") != 0) ) {
+ yyerror("pedal string must be either blank or *");
+ }
+ }
+
+ else {
+ if (string == (char *) 0) {
+ yyerror("string is required");
+ return;
+ }
+ }
+
+ if (gracebackup != 0 && Place == PL_BETWEEN) {
+ yyerror("grace backup not allowed with 'between'");
+ }
+
+ /* we can't deal with step offset on phrase marks very well,
+ * so warn and ignore if we get one */
+ if (start_steps != 0.0 && Curr_stuff_type == ST_PHRASE) {
+ l_warning(Curr_filename, yylineno, "step offset ignored on phrase mark");
+ start_steps = 0.0;
+ }
+
+ switch (Curr_stuff_type) {
+ case ST_ROM:
+ case ST_BOLD:
+ case ST_ITAL:
+ case ST_BOLDITAL:
+ /* the text-type stuffs are supposed to have a 1-space padding
+ * at the end of them */
+ if (bars != 0 || count != 0.0) {
+ /* don't add padding if has wavy or solid line
+ * til clause */
+ lch = last_char(string);
+ if (lch == '~' || lch == '_') {
+ break;
+ }
+ }
+ string = pad_string(string, Modifier);
+ break;
+
+ case ST_MUSSYM:
+ /* in mussym, user can specify things without the usual
+ * \(---) convention. Change to include them */
+ if (string[2] == '\\' && string[3] == '(') {
+ /* if user unnecessarily put in the \(--), leave it */
+ break;
+ }
+
+ len = strlen(string + 2);
+ MALLOCA(char, padded_string, len + 6);
+ (void) sprintf(padded_string, "%c%c\\(%s)", FONT_TR, DFLT_SIZE,
+ string + 2);
+ FREE(string);
+ string = padded_string;
+ break;
+
+ default:
+ break;
+ }
+
+ /* fill in a new STUFF struct with appropriate info */
+ new_p = newSTUFF(string, dist, dist_usage, start_count, start_steps,
+ gracebackup, bars, count,
+ Curr_stuff_type, Modifier, Place, Curr_filename, yylineno);
+
+ /* if bars > 0, need to save away til info for later error
+ * checking */
+ if (bars > 0) {
+ CALLOC(TIL_INFO, til_info_p, 1);
+ til_info_p->measnum = Measnum + bars;
+ til_info_p->count = count;
+ til_info_p->inputfile = new_p->inputfile;
+ til_info_p->inputlineno = new_p->inputlineno;
+ til_info_p->next = Til_info_list_p;
+ Til_info_list_p = til_info_p;
+ }
+
+ /* above/between go on the head of the list, below goes on the
+ * tail of the list, so that things come out in the right order.
+ * Midi always goes at the end */
+ if (Place == PL_BELOW || Curr_stuff_type == ST_MIDI) {
+ /* link onto list tail */
+ if ( Tail_stufflist_p == (struct STUFF *) 0) {
+ Head_stufflist_p = new_p;
+ }
+ else {
+ Tail_stufflist_p->next = new_p;
+ }
+ Tail_stufflist_p = new_p;
+ }
+ else {
+ /* link onto head of list */
+ new_p->next = Head_stufflist_p;
+ Head_stufflist_p = new_p;
+ if (Tail_stufflist_p == (struct STUFF *) 0) {
+ Tail_stufflist_p = new_p;
+ }
+ }
+}
+\f
+
+/* return YES if given string consists entirely of the specific music symbol */
+/* the string should be in the internal format of font/size/string */
+
+int
+string_is_sym(string, sym, symfont)
+
+char *string; /* which string to check */
+int sym; /* check for this music symbol */
+int symfont; /* FONT_MUSIC* */
+
+{
+ int font, size;
+
+
+ if (string == (char *) 0) {
+ return(NO);
+ }
+
+ font = *string++;
+ size = *string++;
+ if (next_str_char(&string, &font, &size) != sym) {
+ return(NO);
+ }
+ if (font != symfont) {
+ return(NO);
+ }
+ if (next_str_char(&string, &font, &size) == '\0') {
+ return(YES);
+ }
+ return (NO);
+}
+\f
+
+/* connect a list of STUFF to a STAFF. If there is already something on
+ * that STAFF's STUFF list, attach at the end or beginning as appropriate
+ * depending on place. */
+
+void
+attach_stuff()
+
+{
+ struct SVRANGELIST *svr_p; /* to walk through Svrangelist */
+ struct RANGELIST *r_p; /* to walk through staff range list */
+ short staffno;
+
+
+ debug(4, "attach_stuff");
+
+ /* make sure we've got STAFF structs for this measure */
+ create_staffs();
+
+ for (svr_p = Svrangelist_p; svr_p != (struct SVRANGELIST *) 0;
+ svr_p = svr_p->next) {
+ for (r_p = svr_p->stafflist_p; r_p != (struct RANGELIST *) 0;
+ r_p = r_p->next) {
+
+ for (staffno = r_p->begin; staffno <= r_p->end
+ && staffno <= MAXSTAFFS; staffno++) {
+ do_attach(staffno, r_p->all, svr_p->vnolist_p);
+
+ if (Place == PL_BETWEEN) {
+ /* between has 2 staffs in its range,
+ * but stuff is only associated
+ * with the top staff */
+ break;
+ }
+ }
+ }
+ }
+
+ free_rlists();
+
+ /* have made copies of stuff for each staff that gets one, with
+ * the proper font/size etc, so need to free master stufflist copy */
+ free_stufflist(Head_stufflist_p);
+}
+\f
+
+/* Attach STUFF for a specific staff. */
+
+static void
+do_attach(staffno, all, vno_range_p)
+
+int staffno;
+int all;
+struct RANGELIST *vno_range_p;
+
+{
+ struct STAFF *staff_p; /* where to attach STUFF */
+ struct STUFF *stufflist_p; /* current copy of STUFF list */
+
+
+ if (staffno > Score.staffs) {
+ l_yyerror(Head_stufflist_p->inputfile,
+ Head_stufflist_p->inputlineno,
+ "staff number out of range");
+ return;
+ }
+
+ staff_p = Staffmap_p[staffno]->u.staff_p;
+
+ if (Place == PL_BETWEEN) {
+ if (staffno + 1 > Score.staffs) {
+ /* will have already exclaimed about
+ * this error before, so no need to print message,
+ * but better skip next check */
+ return;
+ }
+
+ /* if either staff of a between is invisible,
+ * throw this stuff away */
+ if (svpath(staffno, VISIBLE)->visible == NO ||
+ svpath(staffno + 1,
+ VISIBLE)->visible == NO) {
+ return;
+ }
+ }
+
+ /* handle MIDI stuff specially */
+ if (Curr_stuff_type == ST_MIDI) {
+ if (all == YES) {
+ /* need to find top visible staff/voice to attach to */
+ int s; /* staff number */
+ int v; /* voice number */
+ struct RANGELIST range;
+
+ v = 1; /* avoid bogus "used before set" warning */
+ for (s = 1; s <= MAXSTAFFS; s++) {
+ if (svpath(s, VISIBLE)->visible == YES) {
+ for (v = 1; v <= MAXVOICES; v++) {
+ if (vvpath(s, v, VISIBLE)->visible == YES) {
+ break;
+ }
+ }
+ if (v <= MAXVOICES) {
+ break;
+ }
+ }
+ }
+ if (s > MAXSTAFFS || v > MAXVOICES) {
+ pfatal("failed to find top visible staff/voice");
+ }
+ /* make a special RANGELIST for this */
+ range.begin = range.end = v;
+ range.all = YES;
+ range.next = 0;
+ midi_attach(s, Staffmap_p[s]->u.staff_p, &range, all);
+ }
+ else {
+ midi_attach(staffno, staff_p, vno_range_p, all);
+ }
+ }
+
+ else {
+ /* make the copy for this staff from master copy */
+ stufflist_p = clone_stufflist(Head_stufflist_p, staffno, all);
+
+ if (Curr_stuff_type == ST_PEDAL) {
+ fix_pedal(staffno, stufflist_p);
+ }
+
+ connect_stuff(staff_p, stufflist_p);
+ }
+}
+\f
+
+/* attach MIDI stuff. This is slightly different than other stuff because
+ * it can be applied to one or both voices. */
+
+static void
+midi_attach(staffno, staff_p, vno_range_p, all)
+
+int staffno; /* attach to this staff number */
+struct STAFF *staff_p; /* attach to this staff struct */
+struct RANGELIST *vno_range_p;
+int all; /* if associated with "all" */
+
+{
+ struct RANGELIST *r_p; /* walk through vno_range_p */
+ int vno; /* voice number */
+ struct STUFF *stufflist_p; /* copy of stuff */
+ struct STUFF *st_p; /* walk through stufflist_p */
+ short place;
+
+
+ /* do for each voice that MIDI stuff applies to */
+ for (r_p = vno_range_p; r_p != (struct RANGELIST *) 0; r_p = r_p->next) {
+ for (vno = r_p->begin; vno <= r_p->end; vno++) {
+
+ /* make the copy for this staff from master copy */
+ stufflist_p = clone_stufflist(Head_stufflist_p,
+ staffno, all);
+
+ /* fix up place based on voice number */
+ switch (vno) {
+ case 1:
+ place = PL_ABOVE;
+ break;
+ case 2:
+ place = PL_BELOW;
+ break;
+ case 3:
+ place = PL_BETWEEN;
+ break;
+ default:
+ pfatal("illegal vno for midi");
+ /*NOTREACHED*/
+ place = PL_UNKNOWN; /* avoid "used before set" warning */
+ break;
+ }
+ for (st_p = stufflist_p; st_p != (struct STUFF *) 0;
+ st_p = st_p->next) {
+ st_p->place = place;
+ }
+
+ connect_stuff(staff_p, stufflist_p);
+ }
+ }
+}
+\f
+
+/* connect a new stuff list into an existing stuff list. Add below stuff and
+ * MIDI stuff to the end of the list,
+ * and others to beginning of list, but make sure any
+ * "above all" comes after any above non-all, and that any below non-all
+ * comes before any "below all."
+ */
+
+void
+connect_stuff(staff_p, stufflist_p)
+
+struct STAFF *staff_p; /* connect to stuff off of this staff */
+struct STUFF *stufflist_p; /* connect this list of stuff */
+
+{
+ struct STUFF *st_p; /* to find link place in STUFF list */
+ struct STUFF *s_p; /* to find end of stufflist_p */
+ struct STUFF **ins_p_p; /* where to insert in list */
+
+
+ if (staff_p == (struct STAFF *) 0 || stufflist_p == (struct STUFF *) 0) {
+ return;
+ }
+
+ if (staff_p->stuff_p == (struct STUFF *) 0) {
+ /* no list before, so attach this one
+ * directly to STAFF */
+ staff_p->stuff_p = stufflist_p;
+ }
+
+ else if (Place == PL_BELOW || stufflist_p->stuff_type == ST_MIDI) {
+ /* if this set of stuff isn't associated with
+ * "all", then it goes before any below "all" stuff */
+ if (stufflist_p->all == NO) {
+ for (ins_p_p = &(staff_p->stuff_p);
+ *ins_p_p != (struct STUFF *) 0;
+ ins_p_p = &((*ins_p_p)->next)) {
+ if ( (*ins_p_p)->place == PL_BELOW &&
+ (*ins_p_p)->all == YES) {
+ break;
+ }
+ }
+ /* find end of list to be inserted */
+ for (s_p = stufflist_p; s_p->next != (struct STUFF *) 0;
+ s_p = s_p->next) {
+ ;
+ }
+
+ /* insert */
+ s_p->next = *ins_p_p;
+ *ins_p_p = stufflist_p;
+ }
+
+ else {
+ /* goes at end of list. find the end */
+ for (st_p = staff_p->stuff_p;
+ st_p->next != (struct STUFF *)0;
+ st_p = st_p->next) {
+ ;
+ }
+
+ /* connect in the new list */
+ st_p->next = stufflist_p;
+ }
+ }
+ else {
+ /* find end of new list */
+ for (s_p = stufflist_p;
+ s_p->next != (struct STUFF *) 0;
+ s_p = s_p->next) {
+ ;
+ }
+
+ if (stufflist_p->all == NO) {
+ /* goes at the head of the list */
+ s_p->next = staff_p->stuff_p;
+ staff_p->stuff_p = stufflist_p;
+ }
+ else {
+ /* goes before any existing above all */
+ for (ins_p_p = &(staff_p->stuff_p);
+ *ins_p_p != (struct STUFF *) 0;
+ ins_p_p = &((*ins_p_p)->next)) {
+ if ( (*ins_p_p)->place == PL_ABOVE &&
+ (*ins_p_p)->all == YES) {
+ break;
+ }
+ }
+ /* find end of list to be inserted */
+ for (s_p = stufflist_p; s_p->next != (struct STUFF *) 0;
+ s_p = s_p->next) {
+ ;
+ }
+
+ /* insert */
+ s_p->next = *ins_p_p;
+ *ins_p_p = stufflist_p;
+ }
+ }
+}
+\f
+
+/* given a list of STUFF, return a clone of the list */
+
+static struct STUFF *
+clone_stufflist(stufflist_p, staffno, all)
+
+struct STUFF *stufflist_p; /* what stuff to clone */
+int staffno; /* which staff, to get proper point size */
+int all; /* YES if was "above all" or "below all" */
+
+{
+ struct STUFF *new_p; /* copy of STUFF */
+ char *newstring; /* copy of text string */
+ int font;
+ int fontfamily;
+ int size;
+
+
+ if (stufflist_p == (struct STUFF *) 0) {
+ return( (struct STUFF *) 0 );
+ }
+
+ /* make copy of string with appropriate font and size */
+ if (stufflist_p->string != (char *) 0) {
+ switch(stufflist_p->stuff_type) {
+ case ST_BOLD:
+ font = FONT_TB;
+ break;
+ case ST_OCTAVE:
+ Stuff_size = DFLT_SIZE;
+ font = FONT_TI;
+ break;
+ case ST_ITAL:
+ font = FONT_TI;
+ break;
+ case ST_BOLDITAL:
+ font = FONT_TX;
+ break;
+ default:
+ font = FONT_TR;
+ break;
+ }
+
+ /* figure out the proper size if not already determined */
+ if (Stuff_size < 0) {
+ if (all == YES) {
+ size = Score.size;
+ }
+ else {
+ size = svpath(staffno, SIZE)->size;
+ }
+ }
+ else {
+ size = Stuff_size;
+ }
+
+ /* determine fontfamily and font if not already known */
+ if (Curr_family == FAMILY_DFLT) {
+ if (all == YES) {
+ fontfamily = Score.fontfamily;
+ }
+ else {
+ fontfamily = svpath(staffno, FONTFAMILY)->
+ fontfamily;
+ }
+ }
+ else {
+ fontfamily = Curr_family;
+ }
+
+ /* clone text string */
+ newstring = copy_string(stufflist_p->string + 2, font, size);
+ if (IS_CHORDLIKE(Modifier)) {
+ newstring = modify_chstr(newstring, Modifier);
+ }
+ fix_string(newstring, fontfamily + font, size,
+ stufflist_p->inputfile, stufflist_p->inputlineno);
+ if (Modifier == TM_FIGBASS || Modifier == TM_ANALYSIS) {
+ newstring = acc_trans(newstring);
+ }
+ }
+ else {
+ newstring = (char *) 0;
+ }
+
+ /* create and fill in clone of stuff, then return it */
+ new_p = newSTUFF(newstring, stufflist_p->dist,
+ stufflist_p->dist_usage,
+ stufflist_p->start.count,
+ stufflist_p->start.steps,
+ stufflist_p->gracebackup,
+ stufflist_p->end.bars, stufflist_p->end.count,
+ stufflist_p->stuff_type, stufflist_p->modifier,
+ stufflist_p->place, stufflist_p->inputfile,
+ stufflist_p->inputlineno);
+ new_p->all = (short) all;
+ new_p->next = clone_stufflist(stufflist_p->next, staffno, all);
+ return(new_p);
+}
+\f
+
+/* allocate a STUFF and fill in all the values given. Initialize carry fields
+ * and "all" to NO. Leave coordinates and next link as 0.
+ * Note that the string pointer
+ * is copied; it does not make a copy of the string itself, so never call this
+ * function more than once with the same string--make a copy. */
+
+struct STUFF *
+newSTUFF(string, dist, dist_usage, start_count, start_steps, gracebackup, bars, count,
+ stuff_type, modifier, place, inputfile, inputlineno)
+
+char *string; /* text string of stuff */
+int dist; /* dist for this STUFF to override dist parameter */
+int dist_usage; /* meaning of dist, SD_* */
+double start_count; /* count at which to begin stuff */
+double start_steps; /* offset by this many steps */
+int gracebackup; /* how many grace notes to back up from start */
+int bars; /* bars in "til" clasue */
+double count; /* counts in "til" clause */
+int stuff_type; /* ST_* */
+int modifier; /* TM_* */
+int place; /* PL_* */
+char *inputfile; /* which file stuff was defined in */
+int inputlineno; /* where stuff was defined in input file */
+
+{
+ struct STUFF *new_p; /* the new STUFF to fill in */
+
+
+ CALLOC(STUFF, new_p, 1);
+ new_p->string = string;
+ new_p->start.count = start_count;
+ new_p->start.steps = start_steps;
+ new_p->gracebackup = (short) gracebackup;
+ new_p->dist = (short) dist;
+ new_p->dist_usage = (short) dist_usage;
+ new_p->end.bars = (short) bars;
+ new_p->end.count = count;
+ new_p->stuff_type = (short) stuff_type;
+ new_p->modifier = (short) modifier;
+ new_p->place = (short) place;
+ new_p->carryin = new_p->carryout = new_p->all = NO;
+ new_p->costuff_p = 0;
+ new_p->inputfile = inputfile;
+ new_p->inputlineno = (short) inputlineno;
+
+ return(new_p);
+}
+\f
+
+/* recursively free up a stufflist and any strings hanging off of it */
+
+static void
+free_stufflist(stuff_p)
+
+struct STUFF *stuff_p;
+
+{
+ if (stuff_p == (struct STUFF *) 0 ) {
+ return;
+ }
+
+ free_stufflist(stuff_p->next);
+ if (stuff_p->string != (char *) 0) {
+ FREE(stuff_p->string);
+ }
+ FREE(stuff_p);
+}
+\f
+
+/* at each bar line, see if there are any "til" clauses that are supposed
+ * to end in this measure. If so, make sure they end within the time
+ * signature for this measure. */
+
+void
+meas_stuff_chk()
+
+{
+ struct TIL_INFO *til_info_p; /* to index thru list */
+ struct TIL_INFO **del_place_p_p; /* for deleting from list */
+ struct TIL_INFO *one2free_p; /* pointer to which element
+ * to free */
+
+ debug(2, "meas_chk_stuff");
+
+ /* update measure number to conpensate for any multirests */
+ Measnum += Multi_adjust;
+ Multi_adjust = 0;
+
+ /* go through list of in-progress til clauses */
+ for (til_info_p = Til_info_list_p, del_place_p_p = &Til_info_list_p;
+ til_info_p != (struct TIL_INFO *) 0; ) {
+
+ if (til_info_p->measnum == Measnum) {
+
+ /* at measure where this til clause ends */
+ /* check if within time signature */
+ if (til_info_p->count > Score.timenum + 1.0) {
+ l_yyerror(til_info_p->inputfile,
+ til_info_p->inputlineno,
+ "beats in 'til' clause must be <= numerator of time signature + 1 of the measure in which the 'til' clause ends (i.e., <= %d)",
+ Score.timenum);
+ }
+
+ /* this one has been taken care of: delete from list */
+ *del_place_p_p = til_info_p->next;
+ one2free_p = til_info_p;
+ }
+ else if (til_info_p->measnum < Measnum) {
+ /* must have ended inside a multirest, so delete
+ * from list */
+ *del_place_p_p = til_info_p->next;
+ one2free_p = til_info_p;
+ }
+ else {
+ /* this one stays on the list for now, so move pointer
+ * to where to potentially delete to next element */
+ del_place_p_p = &(til_info_p->next);
+ one2free_p = (struct TIL_INFO *) 0;
+ }
+
+ /* have to move to next element
+ * before freeing the current one */
+ til_info_p = til_info_p->next;
+
+ if (one2free_p != (struct TIL_INFO *) 0) {
+ FREE(one2free_p);
+ }
+ }
+
+ /* update number of measures. */
+ Measnum++;
+
+ /* make sure pedal marks are in proper order */
+ ped_order_chk();
+}
+\f
+
+/* adjust number of measures to account for multirests. Called when there is
+ * a multirest. Saved the number of measures in the multirest (minus 1 since
+ * the barline at the end will count for one measure) */
+
+void
+multi_stuff(nmeas)
+
+int nmeas; /* number of measures in multirest */
+
+{
+ /* subtract 1 to account for the fact that at the bar line at the
+ * end of the multirest we will peg the measure counter */
+ Multi_adjust = nmeas - 1;
+}
+\f
+
+/* handle pedal going into endings. When we hit a first ending, save the
+ * state of the pedal for all staffs. On subsequent endings in the set,
+ * reset the pedal state to what it was at the beginning of the first ending.
+ * At the endending, go back to normal operation. This is similar to
+ * the saveped() function used at print time. */
+
+void
+ped_endings(endingloc)
+
+int endingloc; /* STARTITEM, INITEM, etc */
+
+{
+ register int s; /* staff index */
+
+
+ if (endingloc == STARTITEM) {
+ if (Ped_snapshot[0] == YES) {
+
+ /* starting 2nd ending: restore pedal state as it was
+ * at beginning of first ending */
+ for (s = 1; s <= MAXSTAFFS; s++) {
+ Pedal_state[s] = Ped_snapshot[s];
+ }
+ }
+
+ else {
+ /* starting a set of endings,
+ * need to save pedal state at this
+ * point so we can carry it into subsequent endings */
+ for (s = 1; s <= Score.staffs; s++) {
+ Ped_snapshot[s] = Pedal_state[s];
+ }
+ /* make sure any remaining staffs are set to pedal off,
+ * in case user increases the number of staffs
+ * during the endings... */
+ for ( ; s <= MAXSTAFFS; s++) {
+ Ped_snapshot[s] = NO;
+ }
+
+ /* mark that we now have a snapshot */
+ Ped_snapshot[0] = YES;
+ }
+ }
+
+ else if (endingloc == ENDITEM) {
+ /* at end of endings, discard snapshot of pedal states */
+ Ped_snapshot[0] = NO;
+ }
+}
+\f
+
+/* When all input has been processed, or when changing the number
+ * of staffs, we better not have any 'til' clauses
+ * still unfinished. If we do, print a warning message. */
+
+void
+chk4dangling_til_clauses(boundary_desc)
+
+char *boundary_desc; /* "the end of the song" or
+ * "a change in number of staffs" */
+
+{
+ struct TIL_INFO *til_info_p;
+
+
+ debug(2, "chk4dangling_til_clauses");
+
+ /* Go through the whole list of remaining til clauses,
+ * and print a warning message for each. */
+ for (til_info_p = Til_info_list_p; til_info_p != (struct TIL_INFO *) 0;
+ til_info_p = til_info_p->next) {
+
+ /* If right on the boundary or spills over only a very tiny
+ * amount, don't bother to complain */
+ if (til_info_p->measnum - Measnum == 0
+ && til_info_p->count < .001) {
+ continue;
+ }
+
+ l_warning(til_info_p->inputfile, til_info_p->inputlineno,
+ "'til' clause extends beyond %s by %dm + %.3f",
+ boundary_desc, til_info_p->measnum - Measnum,
+ til_info_p->count);
+ }
+
+ /* mop up. */
+ free_tils(Til_info_list_p);
+ Til_info_list_p = (struct TIL_INFO *) 0;
+}
+\f
+
+/* recursively free a list of TIL_INFO structs */
+
+static void
+free_tils(til_p)
+
+struct TIL_INFO *til_p; /* free this list */
+
+{
+ if (til_p == (struct TIL_INFO *) 0) {
+ return;
+ }
+
+ free_tils(til_p->next);
+ FREE(til_p);
+}
+\f
+
+/* user only has to specify when pedal marks end. We deduce from current
+ * pedal state whether a pedal mark is begin or up/down. This gets called
+ * whenever we have a list of pedal STUFFs. Later we enforce that pedal
+ * marks are put in in ascending order only, so that if user enters more
+ * than one pedal line for the same staff, that will be handled properly. */
+
+static void
+fix_pedal(staffno, stuff_p)
+
+int staffno; /* pedal is for this staff */
+struct STUFF *stuff_p; /* list of pedal mark info */
+
+{
+ /* walk through list of pedal marks */
+ for ( ; stuff_p != (struct STUFF *) 0; stuff_p = stuff_p->next) {
+
+ if (stuff_p->string == (char *) 0) {
+ /* no star, so have to deduce state */
+
+ if (Pedal_state[staffno] == NO) {
+ /* pedal currently off, so begin pedal */
+ Pedal_state[staffno] = YES;
+ stuff_p->string = copy_string(Ped_begin_str + 2,
+ (int) Ped_begin_str[0],
+ (int) Ped_begin_str[1]);
+ }
+ else {
+ /* pedal currently down, so pedal up/down */
+ stuff_p->string = copy_string(Ped_up_down_str + 2,
+ (int) Ped_up_down_str[0],
+ (int) Ped_up_down_str[1]);
+ }
+ }
+
+ else if (Pedal_state[staffno] == NO) {
+ yyerror("can't end pedal -- none in progress");
+ }
+
+ else {
+ /* user gave star, so end pedal */
+ Pedal_state[staffno] = NO;
+ }
+ }
+}
+\f
+
+/* reset pedal states for all staffs. This should be called at init time
+ * and at any time when the number of staffs changes. This function also
+ * initializes the Ped_begin_str and Ped_up_down_str. */
+
+void
+reset_ped_state()
+
+{
+ static int first_time = YES; /* flag if function called before */
+ register int s; /* index through staffs */
+
+
+ /* mark pedal off for all staffs */
+ for (s = 1; s <= Score.staffs; s++) {
+ Pedal_state[s] = NO;
+ }
+ Ped_snapshot[0] = NO;
+
+ /* the first time this function is called, initialize the strings
+ * for pedal begin and pedal end. We just have one copy of these
+ * and then make as many copies from these as necessary */
+ if (first_time == YES) {
+ first_time = NO;
+ Ped_begin_str = copy_string("\\(begped)", FONT_MUSIC,
+ DFLT_SIZE);
+ Ped_up_down_str = copy_string("\\(pedal)", FONT_MUSIC,
+ DFLT_SIZE);
+ fix_string(Ped_begin_str, FONT_MUSIC, DFLT_SIZE,
+ Curr_filename, -1);
+ fix_string(Ped_up_down_str, FONT_MUSIC, DFLT_SIZE,
+ Curr_filename, -1);
+ }
+}
+\f
+
+/* fill in rehearsal mark string. This doesn't go in a STUFF, but it's
+ * sort of like stuff and there didn't seem to be any more appropriate file for
+ * this function */
+
+
+static int Reh_let = 0; /* current value of rehearsal letter. 0 == "A",
+ * 25 == "Z", 26 == "AA", etc to 701 == "ZZ" */
+static int Reh_num = 1; /* current value of rehearsal number */
+
+
+void
+set_reh_string(bar_p, fontfamily, font, size, string)
+
+struct BAR *bar_p; /* which bar gets the rehearsal mark */
+int fontfamily; /* what font family to use, or FAMILY_DFLT
+ * if to use current default */
+int font; /* what font to use, or FONT_UNKNOWN if to use the
+ * current default font */
+int size; /* font size to use, or -1 if to use current default */
+char *string; /* string for rehearsal mark */
+
+{
+ char reh_str[12]; /* temporary buff for string version of
+ * rehearsal number or letter */
+ static int reh_size = DFLT_SIZE; /* size to use for reh marks */
+ static int reh_family = FAMILY_DFLT; /* font family to use */
+ static int reh_font = FONT_TB; /* font to use */
+
+
+ /* if first time through, init the font family to the score family */
+ if (reh_family == FAMILY_DFLT) {
+ reh_family = Score.fontfamily;
+ }
+
+ /* if user specified a new size, save that */
+ if (size != -1) {
+ if (size > 100) {
+ yyerror("reh mark size too large");
+ return;
+ }
+ else {
+ reh_size = size;
+ }
+ }
+
+ /* if user specified new font or font family, save that */
+ if (font != FONT_UNKNOWN) {
+ reh_font = font;
+ }
+ if (fontfamily != FAMILY_DFLT) {
+ reh_family = fontfamily;
+ }
+
+ switch(bar_p->reh_type) {
+
+ case REH_NUM:
+ /* get string version of current rehearsal number, and
+ * incrment it */
+ bar_p->reh_string = copy_string(num2str(Reh_num++) + 2,
+ reh_family + reh_font, reh_size);
+ break;
+
+ case REH_LET:
+ /* Get string version of current rehearsal letter.
+ * Start with A-Z, then AA, AB, AC, ... BA, BB, ... up to ZZ.
+ */
+ if (Reh_let < 26) {
+ /* 1-letter long mark */
+ (void) sprintf(reh_str, "%c", Reh_let + 'A');
+ }
+ else if (Reh_let < 27 * 26) {
+ /* 2-letter long mark */
+ (void) sprintf(reh_str, "%c%c",
+ (Reh_let / 26) + 'A' - 1, (Reh_let % 26) + 'A');
+ }
+ else {
+ ufatal("too many rehearsal letters!");
+ }
+ bar_p->reh_string = copy_string(reh_str,
+ reh_family + reh_font, reh_size);
+ /* increment for next time around */
+ Reh_let++;
+ break;
+
+ case REH_MNUM:
+ /* get string version of current measure number */
+ bar_p->reh_string = copy_string(num2str(Meas_num) + 2,
+ reh_family + reh_font, reh_size);
+ break;
+
+ case REH_STRING:
+ /* user-specified string */
+ bar_p->reh_string = fix_string(string,
+ reh_family + reh_font, reh_size,
+ Curr_filename, yylineno);
+ break;
+
+ case REH_NONE:
+ break;
+
+ default:
+ pfatal("set_reh_string passed bad value");
+ break;
+ }
+}
+\f
+
+/* Set rehearsal letter or number to user-specified value.
+ * If the current bar has a rehearsal mark of the type being changed,
+ * also replace its current mark with the changed one. This allows user
+ * to say either
+ * reh num num=5
+ * or
+ * num=5 reh num
+ * and get the same results, which is consistent with how mnum= setting
+ * had worked.
+ */
+
+void
+init_reh(rehnumber, rehletter, mainbar_p)
+
+int rehnumber; /* New value for Reh_num or negative if setting Reh_let */
+char *rehletter; /* "A" to "ZZ" or null if setting number */
+struct MAINLL *mainbar_p; /* points to the current BAR */
+
+{
+ struct BAR *bar_p;
+ char *oldstr; /* previous reh_string */
+
+ if (mainbar_p == 0 || mainbar_p->str != S_BAR) {
+ pfatal("bad mainbar_p passed to init_reh");
+ }
+ bar_p = mainbar_p->u.bar_p;
+ oldstr = bar_p->reh_string;
+
+ if (rehnumber >= 0) {
+ Reh_num = rehnumber;
+ /* If this bar has a rehearsal number on this bar,
+ * replace it, and free the old one. */
+ if (bar_p->reh_type == REH_NUM) {
+ set_reh_string(bar_p, FAMILY_DFLT, FONT_UNKNOWN, -1,
+ (char *) 0);
+ FREE(oldstr);
+ }
+ }
+
+ if (rehletter != 0) {
+ /* Letter is stored internally as a number,
+ * which is then converted, so we have to convert in reverse.
+ * We only allow "A" through "ZZ" */
+ if (isupper(rehletter[0]) && rehletter[1] == '\0') {
+ Reh_let = rehletter[0] - 'A';
+ }
+ else if (isupper(rehletter[0]) && isupper(rehletter[1])
+ && rehletter[2] == '\0') {
+ Reh_let = 26 + (rehletter[1] - 'A')
+ + (rehletter[0] - 'A') * 26;
+ }
+ else {
+ yyerror("rehearsal letter setting must be \"A\" through \"ZZ\"");
+ return;
+ }
+ /* If this bar has a rehearsal letter on this bar,
+ * replace it, and free the old one. */
+ if (bar_p->reh_type == REH_LET) {
+ set_reh_string(bar_p, FAMILY_DFLT, FONT_UNKNOWN, -1,
+ (char *) 0);
+ FREE(oldstr);
+ }
+ }
+}
+\f
+
+/* go through all stuff lists and verify that pedal marks are given in
+ * ascending order. If not, error. Some code in both parse and
+ * placement phases requires that pedal marks be in order. */
+
+static void
+ped_order_chk()
+
+{
+ int staffno;
+ struct STUFF *stuff_p; /* walk through stuff list */
+ float last_ped_count; /* count where last pedal occurred */
+ int last_backup; /* gracebackup of last pedal */
+
+
+ /* check every staff */
+ for (staffno = 1; staffno <= Score.staffs; staffno++) {
+
+ /* initialize for current staff */
+ last_ped_count = -1.0;
+ last_backup = 0;
+
+ /* go through stuff list for current staff, looking for pedal */
+ for (stuff_p = Staffmap_p[staffno]->u.staff_p->stuff_p;
+ stuff_p != (struct STUFF *) 0;
+ stuff_p = stuff_p->next) {
+ if (stuff_p->stuff_type == ST_PEDAL) {
+
+ /* found a pedal. Make sure it is later than
+ * the previous pedal */
+ if (stuff_p->start.count < last_ped_count ||
+ (stuff_p->start.count
+ == last_ped_count
+ && stuff_p->gracebackup
+ > last_backup) ) {
+ l_yyerror(stuff_p->inputfile,
+ stuff_p->inputlineno,
+ "pedal must be specified in ascending order");
+ /* no need to print error more than
+ * once if multiple errors */
+ continue;
+ }
+
+ /* keep track of where this pedal is, for
+ * comparing with the next one */
+ last_ped_count = stuff_p->start.count;
+ last_backup = stuff_p->gracebackup;
+ }
+ }
+ }
+}
+\f
+
+/* Translate STUFF text modifier to a printable string. */
+
+char *
+stuff_modifier(modifier)
+
+int modifier;
+
+{
+ switch (modifier) {
+
+ case TM_CHORD:
+ return("chord");
+ case TM_ANALYSIS:
+ return("analysis");
+ case TM_FIGBASS:
+ return("figbass");
+ case TM_DYN:
+ return("dyn");
+ case TM_NONE:
+ return("(no modifier)");
+ default:
+ return("(invalid modifier)");
+ }
+}