--- /dev/null
+
+/* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 by Arkkra Enterprises */
+/* All rights reserved */
+
+/* Assign values to internal variables in an SSV struct. The functions in
+ * this file are called from the parse phase. */
+
+
+#include <string.h>
+#include "defines.h"
+#include "structs.h"
+#include "globals.h"
+
+
+/* there are several cases where we ultimately want arrays, but don't
+ * know in advance how many elements will be in the array. This could be
+ * done by building a linked list first, then malloc-ing the right size and
+ * copying everything. However, what we'll do is allocate CHUNK elements
+ * to start, and realloc if necessary. When we're done, we realloc down
+ * to the actual size */
+#define CHUNK (8)
+
+/* minimum allowable height or width of page (in inches)
+ * after subtracting off the margins */
+#define MIN_USABLE_SPACE 0.5
+
+
+/* Macros to adjust numbers between inches to centimeters . */
+
+/* Given a number in inches, use that number if in inches mode.
+ * If in centimeter mode, then use the equivalent distance in centimeters,
+ * rounded to the nearest quarter of a centimeter. */
+#define ADJUST4UNITS(n) (Score.units == INCHES ? n : NEARESTQUARTER(n * CMPERINCH))
+
+/* Given an input number, leave as is if in inches mode. If in centimeter
+ * mode, treat the number as being in centimeters, and convert to the inch
+ * equivalent distance. */
+#define ADJUST2INCHES(n) { if (Score.units == CM) n /= CMPERINCH; }
+
+static struct STAFFSET *Curr_staffset_p;/* staffset currently
+ * being filled in */
+static int Ss_count; /* num of elements in Curr_staffset_p
+ * currently actually being used */
+static int Ss_length; /* num of elements allocated
+ * to Curr_staffset_p */
+
+static struct TOP_BOT *Curr_barstlist_p;/* bar style list being filled in */
+static int Barst_count; /* number of elements in
+ * Curr_barstlist_p that are currently
+ * filled in with valid values */
+static int Barst_length; /* number of elements allocated to
+ * Curr_barstlist_p */
+
+struct BEAMLIST {
+ RATIONAL *list_p; /* beam list being filled in */
+ int count; /* elements filled in list_p */
+ int length; /* elements allocated to list_p */
+};
+/* These store info from the beamstyle parameter. */
+static struct BEAMLIST Curr_beamstyle;
+static struct BEAMLIST Curr_subbeamstyle;
+static int Subbeam_index; /* Index into Curr_subbeamstyle where
+ * the most recent '(' was, or -1 if
+ * no pending unmatched parenthesis. */
+static char *parmformat = "%s parameter"; /* for error messages */
+
+/* functions to do all the various checks. They check the context,
+ * range, power_of2, etc, and
+ * mark the variable as used if everything passes the checks. The first
+ * is for int variables (actually short) in SSV, the second for floats */
+static int do_assign P((int var, int value, int min, int max,
+ int cont, int empty_value, char *name,
+ struct MAINLL *mainll_item_p, short *ptr2dest));
+
+static int do_fassign P((int var, double value, double min,
+ double max, int cont, char *name, struct MAINLL *mainll_item_p,
+ float *ptr2dest));
+static void chg_too_late P((char *var_name));
+
+/* comparison functions to be passed to qsort */
+static int comp_staffset P((const void *item1_p,
+ const void *item2_p));
+static int comp_barst P((const void *item1_p, const void *item2_p));
+static int is_tablature_staff P((struct SSV *ssv_p));
+static void init_beamlist P((struct BEAMLIST *beamlist_p));
+static void add2outerbeam P((RATIONAL value));
+\f
+
+/* assign a value to an SSV variable having a domain of short,
+ * after doing any appropriate checks */
+
+void
+assign_int(var, value, mainll_item_p)
+
+int var; /* SSV index of which variable to set */
+int value; /* what to set it to */
+struct MAINLL *mainll_item_p; /* where to store SSV info in main list */
+
+{
+ /* all of these things except font can only go
+ * into score/staff sorts of things,
+ * not head/foot. If we are in a head/foot, the MAINLL will be null */
+ if (Context == C_MUSIC || Context == C_GRIDS || Context == C_HEADSHAPES
+ || (mainll_item_p == 0 && var != SIZE)) {
+ yyerror("trying to set parameter value in wrong context");
+ return;
+ }
+
+
+ if (mainll_item_p != (struct MAINLL *) 0) {
+ debug(4, "assign_int file=%s line=%d var=%d t value=%d",
+ mainll_item_p->inputfile, mainll_item_p->inputlineno, var, value);
+ }
+
+ /* handle each variable appropriately */
+ switch (var) {
+
+ case SIZE:
+ Curr_size = value;
+ if ( Context & C_BLOCKHEAD ) {
+ (void) rangecheck(value, MINSIZE, MAXSIZE, "size");
+
+ /* special case -- size can be set in block,
+ * and there we don't put in SSV struct,
+ * just save its value in Curr_size */
+ return;
+ }
+ else {
+ (void) do_assign(var, value, MINSIZE, MAXSIZE,
+ C_SCORE | C_STAFF, NO, "size",
+ mainll_item_p, &(mainll_item_p->u.ssv_p->size));
+ /* set in Score, in case we get an "all" */
+ if (Context == C_SCORE) {
+ Score.size = (short) value;
+ }
+ }
+ break;
+
+ case LYRICSSIZE:
+ if (do_assign(var, value, MINSIZE, MAXSIZE, C_SCORE | C_STAFF,
+ NO, "lyricssize", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->lyricssize) )
+ == YES) {
+ /* save values for any lyrics that come later */
+ if (Context == C_SCORE) {
+ /* set in case we get some "all" lyrics */
+ Score.lyricssize = (short) value;
+ }
+ setlyrsize(mainll_item_p->u.ssv_p->staffno, value);
+ }
+ break;
+
+ case MEASNUMSIZE:
+ (void) do_assign(var, value, MINSIZE, MAXSIZE, C_SCORE,
+ NO, "measnumsize", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->measnumsize) );
+ break;
+
+ case SYLPOSITION:
+ (void) do_assign(var, value, MINSYLPOSITION, MAXSYLPOSITION,
+ C_SCORE | C_STAFF,
+ NO, "sylposition", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->sylposition) );
+ break;
+
+ case DEFOCT:
+ (void) do_assign(var, value, MINOCTAVE, MAXOCTAVE,
+ C_SSV, NO, "defoct", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->defoct) );
+ if (Context != C_SCORE &&
+ is_tab_staff(mainll_item_p->u.ssv_p->staffno)
+ == YES) {
+ yyerror("defoct not allowed on tablature staff");
+ }
+ else {
+ asgnssv(mainll_item_p->u.ssv_p);
+ }
+ break;
+
+ case NUMSTAFF:
+ if (do_assign(var, value, MINSTAFFS, MAXSTAFFS, C_SCORE, NO,
+ "staffs", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->staffs) )
+ == YES) {
+
+ /* can only change number of staffs if not in the
+ * middle of doing music data. We exclaimed about
+ * the user error in end_prev_context(), so if this
+ * occurs, we just skip over the code in the "if"
+ */
+ if (List_of_staffs_p == (struct MAINLL *) 0) {
+ /* NUMSTAFFS is a special case,
+ * in that several other
+ * items have to do error checking
+ * based on the number of staffs specified,
+ * so we have to set it immediately rather than
+ * waiting for the whole SSV struct
+ * to be built. */
+ Score.staffs = (short) value;
+
+ /* if the number of scores changes,
+ * there can't be any
+ * pending "til" clauses on STUFF */
+ chk4dangling_til_clauses(
+ "change in number of staffs");
+
+ /* any pedal information is no long valid */
+ reset_ped_state();
+ }
+ }
+ break;
+
+ case VISIBLE:
+ /* actually yacc already guarantees will be in range */
+ (void) do_assign(var, value, 0, 1, C_SCORE|C_STAFF|C_VOICE, NO,
+ "visible", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->visible) );
+ break;
+
+ case MEASNUM:
+ /* actually yacc already guarantees will be in range */
+ (void) do_assign(var, value, 0, 1, C_SCORE, NO,
+ "measnum", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->measnum) );
+ break;
+
+ case CANCELKEY:
+ /* actually yacc already guarantees will be in range */
+ (void) do_assign(var, value, 0, 1, C_SCORE | C_STAFF, NO,
+ "cancelkey", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->cancelkey) );
+ break;
+
+ case MINSTSEP:
+ (void) do_assign(var, value, MINMINSTSEP, MAXSEPVAL,
+ C_SCORE | C_STAFF, NO, "staffsep", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->minstsep) );
+ break;
+
+ case MINSCSEP:
+ (void) do_assign(var, value, MINMINSCSEP, MAXSEPVAL,
+ C_SCORE, NO, "min scoresep", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->minscsep) );
+ break;
+
+ case MAXSCSEP:
+ (void) do_assign(var, value, MINMAXSCSEP, MAXSEPVAL,
+ C_SCORE, NO, "max scoresep", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->maxscsep) );
+ break;
+
+ case CHORDDIST:
+ (void) do_assign(var, value, MINCHORDDIST, MAXCHORDDIST,
+ C_SCORE | C_STAFF, NO, "chorddist",
+ mainll_item_p,
+ &(mainll_item_p->u.ssv_p->chorddist) );
+ break;
+
+ case DIST:
+ (void) do_assign(var, value, MINDIST, MAXDIST,
+ C_SCORE | C_STAFF, NO, "dist", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->dist) );
+ break;
+
+ case DYNDIST:
+ (void) do_assign(var, value, MINDYNDIST, MAXDYNDIST,
+ C_SCORE | C_STAFF, NO, "dyndist",
+ mainll_item_p,
+ &(mainll_item_p->u.ssv_p->dyndist) );
+ break;
+
+ case STAFFPAD:
+ (void) do_assign(var,value, MINSTPAD, MAXSTPAD,
+ C_SCORE | C_STAFF, NO,
+ "staffpad", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->staffpad) );
+ break;
+
+ case MINSCPAD:
+ (void) do_assign(var, value, MINMINSCPAD, MAXPADVAL,
+ C_SCORE, NO, "min scorepad", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->minscpad) );
+ break;
+
+ case MAXSCPAD:
+ (void) do_assign(var, value, MINMAXSCPAD, MAXPADVAL,
+ C_SCORE, NO, "max scorepad", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->maxscpad) );
+ break;
+
+ case DIVISION:
+ chg_too_late("division");
+ (void) do_assign(var, value, MINDIVISION, MAXDIVISION,
+ C_SCORE, NO, "division", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->division) );
+ /* division values that aren't divisible by 2 and 3 are
+ * unlikely to be right and are likely to lead to rational
+ * overflow in MIDI code, so give warning */
+ if ((value % 6) != 0) {
+ l_warning(Curr_filename, yylineno,
+ "dubious division value");
+ }
+
+ break;
+
+ case RELEASE:
+ (void) do_assign(var, value, MINRELEASE, MAXRELEASE,
+ C_SCORE | C_STAFF | C_VOICE, NO,
+ "release", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->release) );
+ break;
+
+ case PANELSPERPAGE:
+ chg_too_late("panelsperpage");
+ (void) do_assign(var, value, MINPANELSPERPAGE, MAXPANELSPERPAGE,
+ C_SCORE, NO,
+ "panelsperpage", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->panelsperpage) );
+ break;
+
+ case RESTCOMBINE:
+ (void) do_assign(var, value,
+ MINRESTCOMBINE, MAXRESTCOMBINE,
+ C_SCORE, NORESTCOMBINE,
+ "restcombine", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->restcombine) );
+ break;
+
+ case FIRSTPAGE:
+ chg_too_late("firstpage");
+ (void) do_assign(var, value, MINFIRSTPAGE, MAXFIRSTPAGE,
+ C_SCORE, NO,
+ "firstpage", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->firstpage) );
+ break;
+
+ case GRIDFRET:
+ (void) do_assign(var, value, MINGRIDFRET, MAXGRIDFRET,
+ C_SCORE | C_STAFF, NOGRIDFRET,
+ "gridfret", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->gridfret) );
+ break;
+
+ case GRIDSWHEREUSED:
+ (void) do_assign(var, value, 0, 1,
+ C_SCORE | C_STAFF, NO,
+ "gridswhereused", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->gridswhereused) );
+ break;
+
+ case GRIDSATEND:
+ (void) do_assign(var, value, 0, 1, C_SCORE, NO,
+ "gridsatend", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->gridsatend) );
+ break;
+
+ case TABWHITEBOX:
+ /* actually yacc already guarantees will be in range */
+ (void) do_assign(var, value, 0, 1,
+ C_SCORE | C_STAFF | C_VOICE, NO,
+ "tabwhitebox", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->tabwhitebox) );
+ break;
+
+ case ONTHELINE:
+ /* This only makes sense on 1-line staffs, but we decided
+ * it is best to silently accept it elsewhere. For example,
+ * you might want to set it in score context to apply to all
+ * the 1-line staffs there are. */
+ (void) do_assign(var, value, 0, 1,
+ C_SCORE | C_STAFF | C_VOICE, NO,
+ "ontheline", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->ontheline) );
+ break;
+
+ case WARN:
+ (void) do_assign(var, value, 0, 1,
+ C_SCORE, NO, "warn", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->warn) );
+ break;
+
+ case NUMBERMRPT:
+ (void) do_assign(var, value, 0, 1,
+ C_SCORE | C_STAFF, NO, "numbermrpt", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->numbermrpt) );
+ break;
+
+ case PRINTMULTNUM:
+ (void) do_assign(var, value, 0, 1,
+ C_SCORE | C_STAFF, NO, "printmultnum", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->printmultnum) );
+ break;
+
+ case RESTSYMMULT:
+ (void) do_assign(var, value, 0, 1,
+ C_SCORE | C_STAFF, NO, "restsymmult", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->restsymmult) );
+ break;
+
+ default:
+ pfatal("bad parameter name\n");
+ break;
+ }
+}
+\f
+
+/* do all the error checks for an int variable. If it passes all checks,
+ * set the used flag to YES and return YES. If something fails, return NO. */
+/* Checks are: must be within range, must be in valid context, the MAINLL
+ * struct passed must be non-NULL, and if the pow2 flag is YES, the value
+ * must be a power of two. Also give warning if field already used. Getting
+ * a null pointer is not fatal--it can happen if user tried to do something
+ * in the wrong context. */
+
+static int
+do_assign(var, value, min, max, cont, empty_value, name, mainll_item_p, ptr2dest)
+
+int var; /* which variable to set */
+int value; /* what to set it to */
+int min; /* minimum valid value */
+int max; /* maximum valid value */
+int cont; /* valid context(s) (bitmap) */
+int empty_value; /* if NO, value must be strictly within the
+ * given min/max. If != NO, it is an extra
+ * value, outside the min/max range, that
+ * is legal, and indicates
+ * user set the value to empty */
+char *name; /* of internal variable, for error messages */
+struct MAINLL *mainll_item_p; /* which structure to set it in */
+short *ptr2dest; /* the address of the variable to be set */
+
+{
+ char fullname[50]; /* name + " parameter" */
+ if (mainll_item_p == (struct MAINLL *) 0) {
+ l_yyerror(Curr_filename, yylineno, "wrong context for setting %s",
+ name);
+ return(NO);
+ }
+ (void) sprintf(fullname, parmformat, name);
+ if (contextcheck(cont, fullname) == NO) {
+ return(NO);
+ }
+
+ /* exclaim if already set in this SSV */
+ used_check(mainll_item_p, var, name);
+
+ /* do the checks */
+ if (empty_value != NO && erangecheck(value, min, max, empty_value, name) == NO) {
+ return(NO);
+ }
+ else if (empty_value == NO && rangecheck(value, min, max, name) == NO) {
+ return(NO);
+ }
+ /* passed all the checks-- assign and mark it as used */
+ mainll_item_p->u.ssv_p->used[var] = YES;
+ *ptr2dest = (short) value;
+ return(YES);
+}
+\f
+
+/* do assignment of float type SSV variables */
+
+void
+assign_float(var, value, mainll_item_p)
+
+int var; /* which variable to set */
+double value; /* what to set it to */
+struct MAINLL *mainll_item_p; /* where to store info */
+
+{
+
+ /* all of these things can only go into score/staff sorts of things,
+ * not head/foot. If we are in a head/foot, the MAINLL will be null */
+ if (mainll_item_p == 0 || Context == C_MUSIC) {
+ yyerror("trying to set parameter value in wrong context");
+ return;
+ }
+
+ debug(4, "assign_float file=%s line=%d var=%d value=%f",
+ mainll_item_p->inputfile, mainll_item_p->inputlineno, var, value);
+
+ /* some changes are only allowed before music data is entered */
+ if (Got_some_data == YES) {
+ switch (var) {
+ case TOPMARGIN:
+ case BOTMARGIN:
+ case LEFTMARGIN:
+ case RIGHTMARGIN:
+ chg_too_late( "margin");
+ break;
+ case SCALE_FACTOR:
+ chg_too_late( "scale");
+ break;
+ case PAGEHEIGHT:
+ case PAGEWIDTH:
+ chg_too_late( "page size");
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* if pagesize minus the margins get too small (or even worse,
+ * negative), we better complain */
+ switch (var) {
+ case TOPMARGIN:
+ case BOTMARGIN:
+ case LEFTMARGIN:
+ case RIGHTMARGIN:
+ case PAGEHEIGHT:
+ case PAGEWIDTH:
+ chkmargin(Score.topmargin, Score.botmargin, Score.leftmargin,
+ Score.rightmargin);
+ break;
+ default:
+ break;
+ }
+
+ switch (var) {
+
+ case TOPMARGIN:
+ if (do_fassign(var, (double) value,
+ (double) ADJUST4UNITS(MINVMARGIN),
+ (double) ADJUST4UNITS(Score.pageheight - MIN_USABLE_SPACE),
+ C_SCORE, "topmargin", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->topmargin) )
+ == YES) {
+ ADJUST2INCHES(mainll_item_p->u.ssv_p->topmargin)
+
+ /* put in score so we can check for margins exceeding paper size */
+ Score.topmargin = mainll_item_p->u.ssv_p->topmargin;
+ }
+ break;
+
+ case BOTMARGIN:
+ if (do_fassign(var, (double) value,
+ (double) ADJUST4UNITS(MINVMARGIN),
+ (double) ADJUST4UNITS(Score.pageheight - MIN_USABLE_SPACE),
+ C_SCORE, "bottommargin", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->botmargin) )
+ == YES) {
+ ADJUST2INCHES(mainll_item_p->u.ssv_p->botmargin)
+ Score.botmargin = mainll_item_p->u.ssv_p->botmargin;
+ }
+ break;
+
+ case LEFTMARGIN:
+ if (do_fassign(var, (double) value,
+ (double) ADJUST4UNITS(MINHMARGIN),
+ (double) ADJUST4UNITS(Score.pagewidth - MIN_USABLE_SPACE),
+ C_SCORE, "leftmargin", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->leftmargin) )
+ == YES) {
+ ADJUST2INCHES(mainll_item_p->u.ssv_p->leftmargin)
+ Score.leftmargin = mainll_item_p->u.ssv_p->leftmargin;
+ }
+ break;
+
+ case RIGHTMARGIN:
+ if (do_fassign(var, (double) value,
+ (double) ADJUST4UNITS(MINHMARGIN),
+ (double) ADJUST4UNITS(Score.pagewidth - MIN_USABLE_SPACE),
+ C_SCORE, "rightmargin", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->rightmargin) )
+ == YES) {
+ ADJUST2INCHES(mainll_item_p->u.ssv_p->rightmargin)
+ Score.rightmargin = mainll_item_p->u.ssv_p->rightmargin;
+ }
+ break;
+
+ case PACKFACT:
+ (void) do_fassign(var, (double) value,
+ (double) MINPACKFACT, (double) MAXPACKFACT,
+ C_SCORE, "packfact", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->packfact) );
+ break;
+
+ case PACKEXP:
+ (void) do_fassign(var, (double) value,
+ (double) MINPACKEXP, (double) MAXPACKEXP,
+ C_SCORE, "packexp", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->packexp) );
+ break;
+
+ case SCALE_FACTOR:
+ (void) do_fassign(var, (double) value,
+ (double) MINSCALE, (double) MAXSCALE,
+ C_SCORE, "scale factor", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->scale_factor) );
+ break;
+
+ case STAFFSCALE:
+ (void) do_fassign(var, (double) value,
+ (double) MINSTFSCALE, (double) MAXSTFSCALE,
+ C_SCORE | C_STAFF, "staffscale", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->staffscale) );
+ break;
+
+ case GRIDSCALE:
+ (void) do_fassign(var, (double) value,
+ (double) MINGRIDSCALE, (double) MAXGRIDSCALE,
+ C_SCORE | C_STAFF, "gridscale", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->gridscale) );
+ break;
+
+ case PAGEHEIGHT:
+ if (do_fassign(var, (double) value,
+ (double) ADJUST4UNITS(MINPAGEHEIGHT),
+ (double) ADJUST4UNITS(MAXPAGEHEIGHT),
+ C_SCORE, "pageheight", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->pageheight) )
+ == YES) {
+ ADJUST2INCHES(mainll_item_p->u.ssv_p->pageheight)
+ Score.pageheight = mainll_item_p->u.ssv_p->pageheight;
+ }
+ break;
+
+ case PAGEWIDTH:
+ if (do_fassign(var, (double) value,
+ (double) ADJUST4UNITS(MINPAGEWIDTH),
+ (double) ADJUST4UNITS(MAXPAGEWIDTH),
+ C_SCORE, "pagewidth", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->pagewidth) )
+ == YES) {
+ ADJUST2INCHES(mainll_item_p->u.ssv_p->pagewidth)
+ Score.pagewidth = mainll_item_p->u.ssv_p->pagewidth;
+ }
+ break;
+
+ case LYRICSALIGN:
+ (void) do_fassign(var, (double) value,
+ (double) MINLYRICSALIGN,
+ (double) MAXLYRICSALIGN,
+ C_SCORE | C_STAFF, "lyricsalign", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->lyricsalign) );
+ break;
+
+ case PAD:
+ (void) do_fassign(var, (double) value,
+ (double) MINPAD,
+ (double) MAXPAD,
+ C_SCORE | C_STAFF | C_VOICE, "pad", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->pad) );
+ /* What the user calls zero means notes can
+ * just touch, but internally we want zero to mean the
+ * default of 1 point of padding, so adjust. */
+ mainll_item_p->u.ssv_p->pad -= POINT;
+ break;
+
+ case STEMLEN:
+ (void) do_fassign(var, (double) value,
+ (double) MINSTEMLEN,
+ (double) MAXSTEMLEN,
+ C_SSV, "stemlen", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->stemlen) );
+ break;
+
+
+ case STEMSHORTEN:
+ (void) do_fassign(var, (double) value,
+ (double) MINSTEMSHORTEN,
+ (double) MAXSTEMSHORTEN,
+ C_SSV, "stemshorten", mainll_item_p,
+ &(mainll_item_p->u.ssv_p->stemshorten) );
+ break;
+
+ default:
+ pfatal("invalid float parameter");
+ break;
+ }
+}
+
+\f
+
+/* Handle parameters that have two float numbers as their value */
+
+void
+assign_2floats(var, value1, value2, mainll_item_p)
+
+int var; /* which variable to set */
+double value1, value2; /* which values to set */
+struct MAINLL *mainll_item_p; /* where to store info */
+
+{
+ switch (var) {
+
+ case BEAMSLOPE:
+ /* First float value is the factor */
+ if (do_fassign(var, (double) value1,
+ (double) MINBEAMFACT,
+ (double) MAXBEAMFACT,
+ C_SCORE | C_STAFF | C_VOICE, "beamslope factor",
+ mainll_item_p,
+ &(mainll_item_p->u.ssv_p->beamfact) ) == YES) {
+
+ /* Fool do_fassign into thinking we haven't set the used
+ * flag yet. This is a little kludgy... */
+ mainll_item_p->u.ssv_p->used[var] = NO;
+
+ /* Second value is the max angle in degrees */
+ (void) do_fassign(var, value2,
+ (double) MINBEAMMAX,
+ (double) MAXBEAMMAX,
+ C_SCORE | C_STAFF | C_VOICE,
+ "beamslope maximum slope angle",
+ mainll_item_p,
+ &(mainll_item_p->u.ssv_p->beammax) );
+ }
+ break;
+
+ default:
+ pfatal("bad var value for assign_2floats %d", var);
+ /*NOTREACHED*/
+ break;
+ }
+}
+\f
+
+/* If user tries to change something that can only be changed before music
+ * data is entered, but music has been entered, print error message. */
+
+static void
+chg_too_late(var_name)
+
+char *var_name;
+
+{
+ if (Got_some_data == YES) {
+ l_yyerror(Curr_filename, yylineno,
+ "Can't change %s after music or block data has been entered",
+ var_name);
+ }
+}
+\f
+
+/* Do error checks for a float variable. If it passes all checks,
+ * set the used flag to YES and return YES.
+ * If something fails check, return NO.
+ * Checks are: value within range, valid context, and MAINLL struct pointer
+ * passed non-NULL.
+ * Also give warning if field already used.
+ */
+
+static int
+do_fassign(var, value, min, max, cont, name, mainll_item_p, ptr2dest)
+
+int var; /* which variable to set */
+double value; /* what to set it to */
+double min; /* minimum valid value */
+double max; /* maximum valid value */
+int cont; /* valid context(s) (bitmap) */
+char *name; /* for error messages */
+struct MAINLL *mainll_item_p; /* which structure to set it in */
+float *ptr2dest; /* pointer to the float variable
+ * to be assigned */
+
+{
+ char fullname[50]; /* name + " parameter" */
+
+ if (mainll_item_p == (struct MAINLL *) 0) {
+ l_yyerror(Curr_filename, yylineno, "wrong context for setting %s", name);
+ return(NO);
+ }
+ (void) sprintf(fullname, parmformat, name);
+ if ( contextcheck(cont, fullname) == NO) {
+ return(NO);
+ }
+
+ /* exclaim if already set in this SSV */
+ used_check(mainll_item_p, var, name);
+
+ /* do checks */
+ if (frangecheck(value, min, max, name) == NO) {
+ return(NO);
+ }
+ else {
+ /* passed all the checks-- assign and mark it as used */
+ mainll_item_p->u.ssv_p->used[var] = YES;
+ *ptr2dest = value;
+ return(YES);
+ }
+}
+\f
+
+/* assign value to vscheme variable */
+
+void
+assign_vscheme(numvoices, vtype, mainll_item_p)
+
+int numvoices; /* 1, 2, or 3 */
+int vtype; /* V_1, V_2FREESTEM, or V_2OPSTEM. For 3 voice
+ * case, this is still one of the V_2* values,
+ * and in that case it specifies whether
+ * the stems are free or opposing,
+ * with the numvoices indicating the 3 */
+struct MAINLL *mainll_item_p; /* where to assign it */
+
+{
+ /* check for proper context */
+ if (contextcheck(C_SCORE | C_STAFF, "vscheme parameter") == NO) {
+ return;
+ }
+
+ /* exclaim if already set in this SSV */
+ used_check(mainll_item_p, VSCHEME, "vscheme");
+
+ if (rangecheck(numvoices, MINVOICES, MAXVOICES, "vscheme value") == NO) {
+ return;
+ }
+
+ /* check for valid combination */
+ if ( (numvoices == 1) && (vtype != V_1) ) {
+ yyerror("can't have 'o' or 'f' qualifier when vscheme=1");
+ return;
+ }
+
+ if ( (numvoices == 2 || numvoices == 3) && (vtype == V_1) ) {
+ yyerror("'o' or 'f' qualifier required when vscheme=2 or vscheme=3");
+ return;
+ }
+
+ /* The 3 voice things are really just the 2 voice ones, but a third
+ * voice is allowed. They get passed in as V_2*, so fix that */
+ if (numvoices == 3) {
+ if (vtype == V_2FREESTEM) {
+ vtype = V_3FREESTEM;
+ }
+ else if (vtype == V_2OPSTEM) {
+ vtype = V_3OPSTEM;
+ }
+ }
+
+ /* set variable to requested value */
+ mainll_item_p->u.ssv_p->vscheme = (short) vtype;
+ mainll_item_p->u.ssv_p->used[VSCHEME] = YES;
+
+ asgnssv(mainll_item_p->u.ssv_p);
+}
+\f
+
+/* assign value to voicecombine parameter */
+
+void
+assign_vcombine(qualifier, mainll_p)
+
+int qualifier;
+struct MAINLL * mainll_p;
+
+{
+ short listed[MAXVOICES + 1]; /* If user mentioned the voice */
+ struct RANGELIST * curr_p; /* walk through list of voices */
+ int v; /* voice */
+ int offset; /* index into vcombine array */
+
+
+ if (contextcheck(C_SCORE | C_STAFF, "voicecombine parameter") == NO) {
+ return;
+ }
+ used_check(mainll_p, VCOMBINE, "voicecombine");
+
+ /* Clear list of voices mentioned by user,
+ * and initialize list of voices to combine to none. */
+ for (v = 1; v <= MAXVOICES; v++) {
+ listed[v] = NO;
+ mainll_p->u.ssv_p->vcombine[v-1] = 0;
+ }
+
+ /* We start filling in at beginning of vcombine array */
+ offset = 0;
+
+ /* Add the specified voices in input order to SSV vcombine array */
+ for (curr_p = Vnorange_p; curr_p != 0; curr_p = curr_p->next) {
+ /* add voices into voice combine list after error checks */
+ for (v = curr_p->begin; v <= curr_p->end; v++) {
+ if (listed[v] == YES) {
+ l_yyerror(Curr_filename, yylineno,
+ "voice %d specified more than once", v);
+ }
+ if (offset >= MAXVOICES) {
+ /* The only way this can happen is if user
+ * specified at least one voice more than once,
+ * and we would have already reported that
+ * above, so no need to print another error.
+ * But we must not attempt to write beyond
+ * end of vcombine array, so jump out of loop.
+ */
+ break;
+ }
+ mainll_p->u.ssv_p->vcombine[offset++] = v;
+ listed[v] = YES;
+ }
+ }
+
+ free_vnorange();
+ mainll_p->u.ssv_p->vcombinequal = (short) qualifier;
+ mainll_p->u.ssv_p->used[VCOMBINE] = YES;
+ /* Since voicecombine is relatively rare, we set a flag if it is
+ * ever used. If flag is not set, all the voicecombine placement
+ * code can be skipped entirely. If turning off or only a
+ * single voice is specified, that doesn't really count as being used.
+ */
+ if (offset > 1) {
+ Vcombused = YES;
+ }
+}
+\f
+
+/* assign key signature */
+
+void
+assign_key(num, acc, is_minor, mainll_item_p)
+
+int num; /* number of sharps or flats */
+int acc; /* # or & for sharp or flat */
+int is_minor; /* YES or NO */
+struct MAINLL *mainll_item_p; /* where to assign */
+
+{
+ if (contextcheck( C_SCORE | C_STAFF, "key parameter") == NO) {
+ return;
+ }
+
+ /* exclaim if already set in this SSV */
+ used_check(mainll_item_p, SHARPS, "key");
+
+ /* error check. Must be no more than 7 flats or sharps, and can only
+ * be set in score or staff contexts */
+ if (rangecheck(num, 0, MAXSHARPS,
+ "number of flats or sharps in key signature") == NO) {
+ return;
+ }
+
+ /* looks okay, so make assignment */
+ /* NOTE: num of flats == negative number of sharps */
+ mainll_item_p->u.ssv_p->sharps = num * (acc == '#' ? 1 : -1);
+ mainll_item_p->u.ssv_p->used[SHARPS] = YES;
+ mainll_item_p->u.ssv_p->is_minor = (short) is_minor;
+
+ asgnssv(mainll_item_p->u.ssv_p);
+}
+\f
+
+/* Assign a string to an SSV variable. It just assigns the pointer for labels,
+ * so temporary strings should be copied before being passed.
+ * For NOTEHEADS, it parses the string and saves the numeric internal numbers.
+ */
+
+void
+assign_string(var, string, mainll_item_p)
+
+int var; /* LABEL, LABEL2, or NOTEHEADS */
+char *string; /* the string to assign */
+struct MAINLL *mainll_item_p; /* where to assign it */
+
+{
+ int n; /* note shape index */
+ char namebuff[100]; /* For note shape names. Builtin names are
+ * fairly short, but user could define longer
+ * ones. We figure 100 should be plenty,
+ * and ufatal if they try to go longer. */
+ int nameleng; /* strlen of a name shape name */
+ int context; /* which context to check */
+ char *error_msg; /* what to print in error message */
+
+ if (var == NOTEHEADS) {
+ context = C_SSV;
+ error_msg = "noteheads parameter";
+ }
+ else {
+ context = C_SCORE | C_STAFF;
+ error_msg = (var == LABEL ? "label parameter"
+ : "label2 parameter");
+ }
+
+ if (contextcheck(context, error_msg) == YES) {
+
+ /* get string into proper internal format */
+ (void) fix_string(string, string[0], string[1],
+ Curr_filename, yylineno);
+
+ switch (var) {
+
+ case LABEL:
+ used_check(mainll_item_p, var, "label");
+ mainll_item_p->u.ssv_p->label = string;
+ break;
+
+ case LABEL2:
+ used_check(mainll_item_p, var, "label2");
+ mainll_item_p->u.ssv_p->label2 = string;
+ break;
+
+ case NOTEHEADS:
+ if (is_tab_staff(mainll_item_p->u.ssv_p->staffno) == YES
+ && strcmp(string+2, "allx") != 0
+ && strcmp(string+2, "norm") != 0) {
+ warning("noteheads parameter ignored on tablature staffs (unless allx or norm)");
+ }
+
+ /* skip past font/size */
+ string += 2;
+ /* split into tokens */
+ for (n = 0; n < 7; n++) {
+ /* skip past white space */
+ while ( isspace(*string) ) {
+ string++;
+ }
+
+ if ( *string == '\0') {
+ break;
+ }
+
+ nameleng = strcspn(string, " \t\r\n");
+ if (nameleng > sizeof(namebuff) - 1) {
+ ufatal("note head name too long");
+ }
+ strncpy(namebuff, string, nameleng);
+ namebuff[nameleng] = '\0';
+ if ((mainll_item_p->u.ssv_p->noteheads[n] =
+ get_shape_num(namebuff))
+ == HS_UNKNOWN) {
+ l_yyerror(Curr_filename, yylineno,
+ "'%s' is not a valid headshape name",
+ namebuff);
+ }
+ string += nameleng;
+ }
+ if (n == 1) {
+ /* copy same shape for all 7 */
+ for ( ; n < 7; n++) {
+ mainll_item_p->u.ssv_p->noteheads[n] =
+ mainll_item_p->u.ssv_p->noteheads[0];
+ }
+ }
+
+ /* Skip past trailing white space, and make sure we got
+ * right number of tokens. */
+ while ( isspace(*string) ) {
+ string++;
+ }
+ if (n != 7 || *string != '\0') {
+ yyerror("wrong number of notehead names: expecting either 1 or 7");
+ }
+ break;
+ default:
+ pfatal("invalid string variable type");
+ /*NOTREACHED*/
+ break;
+ }
+
+ mainll_item_p->u.ssv_p->used[var] = YES;
+ }
+}
+\f
+
+/* make a copy of a string and return pointer to it,
+ * or return NULL if string was NULL. The incoming string is a regular C-style
+ * string. The returned string is 2 bytes longer, with the font in the first
+ * byte, size in the second byte, and the copy of the original string in the
+ * remainder. */
+
+char *
+copy_string(string, font, size)
+
+char *string; /* make a copy of this string */
+int font; /* use this font */
+int size; /* use this point size */
+
+{
+ char *copy; /* pointer to new copy of string */
+
+
+ if (string == (char *) 0) {
+ return (string);
+ }
+
+ /* need 2 extra bytes at beginning for font and size,
+ * and 1 at end for '\0' */
+ MALLOCA(char, copy, strlen(string) + 3);
+
+ /* fill in font and size in first 2 bytes */
+ *copy = (char) font;
+ *(copy + 1) = (char) size;
+
+ /* copy the string and return pointer to copy */
+ (void) strcpy(copy + 2, string);
+ return(copy);
+}
+\f
+
+/* Assign time signature in SSV.
+ * Derives the effective numerator/denominator and the RATIONAL time value
+ * from the timerep, and fills them in the SSV. If there are alternating
+ * time signatures, that will be for the first of them, and a pointer
+ * to the remaining signature(s) will be returned via next_alternation_p.
+ * If there are additive time signatures, the effective num/den will
+ * be based on the largest denominator.
+ */
+
+void
+assign_timesig(mainll_item_p, visibility, next_alternation_p)
+
+struct MAINLL *mainll_item_p; /* SSV to assign time signature in */
+int visibility; /* YES, NO, or EACHMEAS */
+char **next_alternation_p; /* If this time signature includes alternating
+ * time signatures, as in 3/4 4/4,
+ * this will be filled in with a pointer to
+ * where the next alternate time signature
+ * begins in the timerep. If there are no
+ * alternating time signatures, it will be
+ * filled by a null pointer. */
+
+{
+ struct SSV *ssv_p; /* mainll_item_p->u.ssv_p */
+ RATIONAL curr_value; /* There may be compound time
+ * signatures, and each of those
+ * may have multiple numerator
+ * components, so this is used for
+ * getting value of one fraction */
+ int biggest_denominator; /* for calculating effective
+ * numerator and denominator */
+ char *t; /* to walk through timerep */
+
+
+ if (contextcheck(C_SCORE, "time parameter") == NO) {
+ return;
+ }
+
+ /* exclaim if already set in this SSV */
+ used_check(mainll_item_p, TIME, "time signature");
+
+ ssv_p = mainll_item_p->u.ssv_p;
+
+ ssv_p->timevis = visibility;
+
+ curr_value = Zero;
+ ssv_p->time = Zero;
+ biggest_denominator = 0;
+ *next_alternation_p = 0;
+
+ for (t = ssv_p->timerep; *t != TSR_END; t++) {
+ if (*t == TSR_CUT) {
+ curr_value.n = 2;
+ curr_value.d = 2;
+ }
+ else if (*t == TSR_COMMON) {
+ curr_value.n = 4;
+ curr_value.d = 4;
+ }
+ else if (*t == TSR_SLASH) {
+ curr_value.d = *++t;
+ }
+ else if (*t == TSR_ALTERNATING) {
+ *next_alternation_p = ++t;
+ break;
+ }
+ else if (*t == TSR_ADD) {
+ continue;
+ }
+ else {
+ curr_value.n += *t;
+ continue;
+ }
+ biggest_denominator = MAX(biggest_denominator, curr_value.d);
+ rred(&curr_value);
+ ssv_p->time = radd(ssv_p->time, curr_value);
+ curr_value = Zero;
+ }
+
+ /* If there were mixed denominators, use the biggest for the
+ * purpose of effective numerator and denominator */
+ if (biggest_denominator > ssv_p->time.d) {
+ ssv_p->timenum = ssv_p->time.n * (biggest_denominator / ssv_p->time.d);
+ }
+ else {
+ ssv_p->timenum = ssv_p->time.n;
+ }
+ ssv_p->timeden = biggest_denominator;
+
+ /* mark time as used */
+ mainll_item_p->u.ssv_p->used[TIME] = YES;
+
+ /* We have to set this for real immediately, since beamstyle and
+ * other things may need to have it set */
+ asgnssv(mainll_item_p->u.ssv_p);
+
+ if (mainll_item_p->u.ssv_p->used[BEAMSTLIST] == YES) {
+ l_warning(Curr_filename, yylineno,
+ "changing time signature clears beamstyle");
+ /* have to actually clear it here, because otherwise it
+ * would still get assigned in ssv.c because beamstyle is
+ * done after the code for time signature */
+ mainll_item_p->u.ssv_p->used[BEAMSTLIST] = NO;
+ mainll_item_p->u.ssv_p->nbeam = 0;
+ if (mainll_item_p->u.ssv_p->beamstlist != (RATIONAL *) 0) {
+ FREE(mainll_item_p->u.ssv_p->beamstlist);
+ }
+ }
+}
+\f
+
+/* assign a font variable (FONT, LYRICSFONT, FONTFAMILY, LYRICSFAMILY) */
+
+void
+set_font(var, value, mainll_item_p)
+
+int var; /* which variable to set */
+int value; /* which font to set it too */
+struct MAINLL *mainll_item_p; /* where to assign it in main list */
+
+{
+ char *varname; /* name of variable, for error messages */
+ char fullname[50]; /* varname + " parameter" */
+
+
+ /* determine the name of the variable, for error messages */
+ switch(var) {
+ case FONT:
+ varname = "font";
+ break;
+ case LYRICSFONT:
+ varname = "lyricsfont";
+ break;
+ case MEASNUMFONT:
+ varname = "measnumfont";
+ break;
+ case FONTFAMILY:
+ varname = "fontfamily";
+ break;
+ case LYRICSFAMILY:
+ varname = "lyricsfontfamily";
+ break;
+ case MEASNUMFAMILY:
+ varname = "measnumfontfamily";
+ break;
+ default:
+ pfatal("bad font variable");
+ /*NOTREACHED*/
+ return;
+ }
+ (void) sprintf(fullname, parmformat, varname);
+
+ /* if being called from SSV, exclaim if already set */
+ if ((Context & C_SSV) != 0) {
+ used_check(mainll_item_p, var, varname);
+ }
+
+ switch (var) {
+
+ case FONT:
+ Curr_font = value;
+
+ if (Context & C_BLOCKHEAD) {
+ /* Special case. In block, we just
+ * keep track of the current font */
+ return;
+ }
+ else if (contextcheck(C_SCORE | C_STAFF, fullname) == YES) {
+ mainll_item_p->u.ssv_p->font = (short) value;
+ }
+ else {
+ return;
+ }
+
+ break;
+
+ case FONTFAMILY:
+ Curr_family = value;
+
+ if (Context & C_BLOCKHEAD) {
+ /* Special case. In block, we just
+ * keep track of the current font */
+ return;
+ }
+ else if (contextcheck(C_SCORE | C_STAFF, fullname) == YES) {
+ mainll_item_p->u.ssv_p->fontfamily = (short) value;
+ }
+ else {
+ return;
+ }
+
+ break;
+
+ case LYRICSFONT:
+ if (contextcheck(C_SCORE | C_STAFF, fullname) == YES) {
+ mainll_item_p->u.ssv_p->lyricsfont = (short) value;
+
+ /* assign immediately in case there is a following
+ * font family change that needs to read it */
+ mainll_item_p->u.ssv_p->used[var] = YES;
+ asgnssv(mainll_item_p->u.ssv_p);
+ setlyrfont(mainll_item_p->u.ssv_p->staffno, value);
+ return;
+ }
+ else {
+ return;
+ }
+
+ /*NOTREACHED*/
+ break;
+
+ case LYRICSFAMILY:
+ if (contextcheck(C_SCORE | C_STAFF, fullname) == YES) {
+ mainll_item_p->u.ssv_p->lyricsfamily = (short) value;
+ /* assign immediately, so we can reset all
+ * lyrics info for this staff */
+ mainll_item_p->u.ssv_p->used[var] = YES;
+ asgnssv(mainll_item_p->u.ssv_p);
+
+ setlyrfont(mainll_item_p->u.ssv_p->staffno,
+ svpath(mainll_item_p->u.ssv_p->staffno,
+ LYRICSFONT)->lyricsfont);
+ return;
+ }
+ else {
+ return;
+ }
+
+ /*NOTREACHED*/
+ break;
+
+ case MEASNUMFONT:
+ if (contextcheck(C_SCORE, fullname) == YES) {
+ mainll_item_p->u.ssv_p->measnumfont = (short) value;
+ }
+ else {
+ return;
+ }
+ break;
+
+ case MEASNUMFAMILY:
+ if (contextcheck(C_SCORE, fullname) == YES) {
+ mainll_item_p->u.ssv_p->measnumfamily = (short) value;
+ }
+ else {
+ return;
+ }
+ break;
+
+ default:
+ pfatal("unknown font variable");
+ break;
+ }
+
+ mainll_item_p->u.ssv_p->used[var] = YES;
+}
+\f
+
+/* set number of stafflines and whether or not to print clef. Number of
+ * lines must be 1 or 5, unless it's a tablature staff,
+ * in which case it can be anything from MINTABLINES to MAXTABLINES.
+ * In any case, it must be set before any music data. */
+
+void
+asgn_stafflines(numlines, printclef, mainll_item_p)
+
+int numlines; /* 1 or 5 for normal, or MINTABLINES to MAXTABLINES for tablature */
+int printclef; /* SS_* */
+struct MAINLL *mainll_item_p; /* where to set value */
+
+{
+ int is_tab; /* YES if is tablature staff */
+ int staff_index;
+
+
+ if (mainll_item_p == (struct MAINLL *) 0) {
+ /* must be in here due to some user syntax error */
+ return;
+ }
+
+ is_tab = is_tablature_staff(mainll_item_p->u.ssv_p);
+ if (is_tab == YES) {
+ if (contextcheck(C_STAFF, "stafflines=tab") == NO) {
+ return;
+ }
+ }
+ else {
+ if (contextcheck(C_SCORE | C_STAFF, "stafflines parameter") == NO) {
+ return;
+ }
+ }
+
+ /* exclaim if already set in this SSV */
+ used_check(mainll_item_p, STAFFLINES, "stafflines");
+
+ if (is_tab == YES) {
+ /* is a tablature staff */
+ (void) rangecheck(numlines, MINTABLINES, MAXTABLINES,
+ "number of tab strings specified");
+ }
+ else {
+ /* not a tablature staff */
+ if (numlines != 5 && numlines != 1) {
+ yyerror("stafflines must be 1 or 5");
+ }
+ }
+
+
+ mainll_item_p->u.ssv_p->stafflines = (short) numlines;
+
+ /* single line never has clef except the drum clef,
+ * so if user didn't explictly set 'n' we do it for them */
+ if (numlines == 1 && printclef == SS_NORMAL) {
+ printclef = NO;
+ }
+ mainll_item_p->u.ssv_p->printclef = (short) printclef;
+ mainll_item_p->u.ssv_p->used[STAFFLINES] = YES;
+
+ /* index into Staff array is staffno - 1 */
+ staff_index = mainll_item_p->u.ssv_p->staffno - 1;
+
+ /* need to do extra consistency check for tablature staffs */
+ if (is_tab == YES) {
+
+ if (staff_index == 0) {
+ yyerror("staff 1 can't be a tablature staff");
+ }
+ else {
+ if (is_tablature_staff( &(Staff[staff_index - 1]) ) == YES
+ || (staff_index < MAXSTAFFS - 1 &&
+ is_tablature_staff( &(Staff[staff_index + 1]) )
+ == YES) ) {
+ yyerror("can't have two consecutive tablature staffs");
+ }
+ if (svpath(mainll_item_p->u.ssv_p->staffno - 1,
+ STAFFLINES)->stafflines != 5) {
+ yyerror("staff before a tablature staff must be a 5-line staff");
+ }
+ }
+ }
+ else {
+ /* if trying to establish a non-5-line non-tablature staff,
+ * and it's not the bottom staff, and the staff below is a
+ * tablature staff, that's a no-no */
+ if (numlines != 5 && staff_index < MAXSTAFFS - 1 &&
+ is_tablature_staff( &(Staff[staff_index + 1]) )
+ == YES) {
+ yyerror("staff before a tablature staff must be a 5-line staff");
+ }
+ }
+
+ /* assign, so we can do error checking on tablature staffs
+ * for future SSVs that we process */
+ asgnssv(mainll_item_p->u.ssv_p);
+}
+\f
+
+/* When the input contains a rangelist, we need to allocate some space for
+ * the information. Allocate an array of length CHUNK. Set the Ss_count to start
+ * filling in element 0, and mark the Ss_length as CHUNK. As we add elements,
+ * Ss_count will be incremented, and the array size enlarged if it overflows.
+ */
+
+void
+new_staffset()
+
+{
+ CALLOC(STAFFSET, Curr_staffset_p, CHUNK);
+
+ Ss_count = 0;
+ Ss_length = CHUNK;
+}
+\f
+
+/* add information about one staffset, at the current offset in the list,
+ * re-allocating additional space if necessary */
+
+void
+add_staffset(start, end, label1, label2)
+
+int start, end; /* of the range */
+char *label1, *label2; /* malloc-ed copies of labels for the range, or NULL */
+
+{
+ /* Murphey's Law insurance */
+ if (Curr_staffset_p == (struct STAFFSET *) 0) {
+ pfatal("NULL staffset");
+ }
+
+ /* swap if backwards */
+ if ( start > end) {
+ int tmp;
+
+ tmp = start;
+ start = end;
+ end = tmp;
+ }
+
+ /* if we guessed too small, need to make a bigger array */
+ if (Ss_count >= Ss_length) {
+ Ss_length += CHUNK;
+ REALLOC(STAFFSET, Curr_staffset_p, Ss_length);
+ }
+
+ /* fill in values */
+ Curr_staffset_p[Ss_count].topstaff = (short) start;
+ Curr_staffset_p[Ss_count].botstaff = (short) end;
+ if (label1 != (char *) 0) {
+ (void) fix_string(label1, label1[0], label1[1],
+ Curr_filename, yylineno);
+ }
+ Curr_staffset_p[Ss_count].label = label1;
+ if (label2 != (char *) 0) {
+ (void) fix_string(label2, label2[0], label2[1],
+ Curr_filename, yylineno);
+ }
+ Curr_staffset_p[Ss_count].label2 = label2;
+
+ /* one more item in list */
+ Ss_count++;
+}
+\f
+
+/* When we have collected an entire list of ranges, assign the
+ * list to the appropriate place in the SSV struct.
+ * (Using "set" instead of "assign" for function name because some
+ * people's compilers are too stupid to tell the difference in names
+ * after 8 characters, which would clash with another function name) */
+
+void
+set_staffset(var, mainll_item_p)
+
+int var; /* which rangelist to set */
+struct MAINLL *mainll_item_p; /* which struct to assign it in */
+
+{
+ register int i; /* index through ranges */
+ short okay = NO; /* if passed all overlap checks */
+
+
+ /* can only do this in score context */
+ if (contextcheck(C_SCORE, "list of staff ranges") == NO) {
+ return;
+ }
+
+ /* first we need to make sure no ranges are out of range */
+ /* go through the list of ranges */
+ for (i = 0; i < Ss_count; i++) {
+
+ /* range check. Make sure it is within number of staffs that
+ * user specified. Since when we assign the user-specified
+ * number, we make sure that is within MAXSTAFFS, it will
+ * be within the absolute maximum as well.
+ */
+ if (rangecheck(Curr_staffset_p[i].botstaff, 1, Score.staffs,
+ "brace/bracket staff number") == NO) {
+ return;
+ }
+ }
+
+ /* if explicitly empty, can free space */
+ if (Ss_count == 0) {
+ FREE(Curr_staffset_p);
+ }
+ else {
+ /* we probably have too much space allocated, shed the rest */
+ REALLOC(STAFFSET, Curr_staffset_p, Ss_count);
+
+ /* sort lowest to highest */
+ qsort( (char *) Curr_staffset_p, (unsigned int) Ss_count,
+ sizeof(struct STAFFSET), comp_staffset);
+ }
+
+ if (mainll_item_p == (struct MAINLL *) 0) {
+ pfatal("NULL SSV for staffset");
+ }
+
+ /* now assign to appropriate variable */
+ switch (var) {
+
+ case BRACELIST:
+ mainll_item_p->u.ssv_p->bracelist = (Ss_count == 0 ?
+ (struct STAFFSET *) 0 : Curr_staffset_p);
+ mainll_item_p->u.ssv_p->nbrace = (short) Ss_count;
+ okay = brac_check(Curr_staffset_p, Ss_count,
+ Score.bracklist, Score.nbrack);
+ used_check(mainll_item_p, var, "brace");
+ break;
+
+ case BRACKLIST:
+ mainll_item_p->u.ssv_p->bracklist = (Ss_count == 0 ?
+ (struct STAFFSET *) 0 : Curr_staffset_p);
+ mainll_item_p->u.ssv_p->nbrack = (short) Ss_count;
+ okay = brac_check(Score.bracelist, Score.nbrace,
+ Curr_staffset_p, Ss_count);
+ used_check(mainll_item_p, var, "bracket");
+ break;
+
+ default:
+ pfatal("unknown staffset type");
+ break;
+ }
+
+ if (okay == YES) {
+ mainll_item_p->u.ssv_p->used[var] = YES;
+
+ /* assign now, so we can check for overlap */
+ asgnssv(mainll_item_p->u.ssv_p);
+ }
+
+ /* the list has been attached to its permanent place, so
+ * reset the temporary pointer to an empty list */
+ Curr_staffset_p = (struct STAFFSET *) 0;
+ Ss_count = 0;
+}
+\f
+
+/* compare 2 STAFFSETs for sorting using qsort */
+
+static int
+comp_staffset(item1_p, item2_p)
+
+#ifdef __STDC__
+const void *item1_p; /* the two items to compare */
+const void *item2_p;
+#else
+char *item1_p; /* the two items to compare */
+char *item2_p;
+#endif
+
+{
+ int top1, top2;
+ int bot1, bot2;
+
+ top1 = ((struct STAFFSET *)item1_p)->topstaff;
+ top2 = ((struct STAFFSET *)item2_p)->topstaff;
+ bot1 = ((struct STAFFSET *)item1_p)->botstaff;
+ bot2 = ((struct STAFFSET *)item2_p)->botstaff;
+
+ if (top1 < top2) {
+ return(-1);
+ }
+
+ else if (top1 > top2) {
+ return(1);
+ }
+
+ else if (bot1 < bot2) {
+ return(-1);
+ }
+
+ else if (bot1 > bot2) {
+ return(1);
+ }
+
+ else {
+ return(0);
+ }
+}
+\f
+
+/* allocate an array of TOP_BOT structs for building up a list of bar style
+ * information (which staffs to bar together) */
+
+void
+new_barstlist()
+
+{
+ CALLOC(TOP_BOT, Curr_barstlist_p, CHUNK);
+
+ /* initialize used and allocated lengths */
+ Barst_count = 0;
+ Barst_length = CHUNK;
+}
+\f
+
+/* add a pair of staff numbers to bar style list */
+
+void
+add_barst(start, end)
+
+int start; /* first staff to bar together */
+int end; /* last staff to bar together */
+
+{
+ if (Curr_barstlist_p == (struct TOP_BOT *) 0) {
+ pfatal("NULL barstlist");
+ }
+
+ /* swap if backwards */
+ if ( start > end) {
+ int tmp;
+
+ tmp = start;
+ start = end;
+ end = tmp;
+ }
+
+ /* if we guessed too small, make a bigger array */
+ if (Barst_count >= Barst_length) {
+
+ Barst_length += CHUNK;
+
+ REALLOC(TOP_BOT, Curr_barstlist_p, Barst_length);
+ }
+
+ Curr_barstlist_p[Barst_count].top = (short) start;
+ Curr_barstlist_p[Barst_count].bottom = (short) end;
+
+ /* one more item on list */
+ Barst_count++;
+}
+\f
+
+/* When we have collected an entire list of ranges, assign the
+ * list to the appropriate place in the SSV struct */
+
+void
+set_barstlist(mainll_item_p)
+
+struct MAINLL *mainll_item_p; /* which struct to assign it in */
+
+{
+ register int i, s; /* index for ranges and staffs */
+ short mentioned[MAXSTAFFS + 1]; /* mark whether each staff occurs in
+ * this list somewhere. Element 0 is
+ * unused */
+
+
+ if (contextcheck(C_SCORE, "barstyle parameter") == NO) {
+ return;
+ }
+
+ /* exclaim if alrady set in this SSV */
+ used_check(mainll_item_p, BARSTLIST, "barstyle");
+
+ /* first we need to make sure no ranges overlap or are out of range */
+
+ /* initialize that we haven't seen anything yet */
+ for (s = 1; s < MAXSTAFFS + 1; s++) {
+ mentioned[s] = NO;
+ }
+
+ /* go through the list of ranges */
+ for (i = 0; i < Barst_count; i++) {
+
+ /* range check. */
+ if (rangecheck(Curr_barstlist_p[i].bottom, 1, Score.staffs,
+ "barstyle staff number") == NO) {
+ continue;
+ }
+
+ /* fill in each in the range as having been mentioned.
+ * If already mentioned, we have a problem */
+ for (s = Curr_barstlist_p[i].top;
+ s <= Curr_barstlist_p[i].bottom; s++) {
+
+ if (mentioned[s] == YES) {
+ yyerror("overlapping range in bar list");
+ }
+
+ else {
+ mentioned[s] = YES;
+ }
+ }
+ }
+
+ /* if explicitly empty, free space */
+ if (Barst_count == 0) {
+ FREE(Curr_barstlist_p);
+ }
+
+ else {
+ /* we probably have too much space allocated, shed the rest */
+ REALLOC(TOP_BOT, Curr_barstlist_p, Barst_count);
+
+ /* sort lowest to highest */
+ qsort ( (char *) Curr_barstlist_p, (unsigned int) Barst_count,
+ sizeof(struct TOP_BOT), comp_barst);
+ }
+
+ if (mainll_item_p == (struct MAINLL *) 0) {
+ pfatal("NULL SSV for barstlist");
+ }
+
+ /* fill in data */
+ mainll_item_p->u.ssv_p->nbarst = (short) Barst_count;
+ if (Barst_count > 0) {
+ mainll_item_p->u.ssv_p->barstlist = Curr_barstlist_p;
+ }
+
+ mainll_item_p->u.ssv_p->used[BARSTLIST] = YES;
+
+ /* now that list has been assigned to its proper place,
+ * re-initialize pointer to null to prepare for another list */
+ Curr_barstlist_p = (struct TOP_BOT *) 0;
+ Barst_count = 0;
+}
+\f
+
+/* compare 2 barslist items for sorting using qsort */
+
+static int
+comp_barst(item1_p, item2_p)
+
+#ifdef __STDC__
+const void *item1_p; /* the two items to compare */
+const void *item2_p;
+#else
+char *item1_p; /* the two items to compare */
+char *item2_p;
+#endif
+
+{
+ if ( ((struct TOP_BOT *)item1_p)->top
+ < ((struct TOP_BOT *)item2_p)->top) {
+ return(-1);
+ }
+
+ else if ( ((struct TOP_BOT *)item1_p)->top
+ > ((struct TOP_BOT *)item2_p)->top) {
+ return(1);
+ }
+
+ else {
+ /* actually this should never occur */
+ return(0);
+ }
+}
+\f
+
+/* Initialize and allocate space for beamstyle information */
+
+void
+new_beamlist()
+
+{
+ init_beamlist(&Curr_beamstyle);
+ init_beamlist(&Curr_subbeamstyle);
+ Subbeam_index = -1;
+}
+
+
+/* Initalize a BEAMLIST struct.
+ * Allocate CHUNK entries, and mark that 0 of them are currently used.
+ */
+
+static void
+init_beamlist(beamlist_p)
+
+struct BEAMLIST *beamlist_p;
+
+{
+ MALLOCA(RATIONAL, beamlist_p->list_p, CHUNK);
+ beamlist_p->count = 0;
+ beamlist_p->length = CHUNK;
+}
+\f
+
+/* This function is called at the parenthesis to begin a sub-beam grouping.
+ * It saves the current index into the subbeam list. At the ending parenthesis,
+ * we add up add the subbeam list time values since that saved index.
+ */
+
+void
+begin_subbeam()
+
+{
+ if (Subbeam_index >= 0) {
+ yyerror("Nested sub-beam groups not allowed");
+ return;
+ }
+ Subbeam_index = Curr_subbeamstyle.count;
+}
+
+void
+end_subbeam()
+{
+ RATIONAL tot_time;
+
+ /* Do error checks */
+ if (Subbeam_index < 0) {
+ yyerror("Missing '(' for sub-beam grouping");
+ return;
+ }
+ if (Subbeam_index >= Curr_subbeamstyle.count - 1) {
+ warning("sub-beam grouping needs at least two values");
+ }
+
+ /* Count up all the time values of subbeams that make up the
+ * single outer beam. */
+ for (tot_time = Zero; Subbeam_index < Curr_subbeamstyle.count;
+ Subbeam_index++) {
+ tot_time = radd(tot_time, Curr_subbeamstyle.list_p[Subbeam_index]);
+ }
+ add2outerbeam(tot_time);
+ Subbeam_index = -1;
+}
+\f
+
+/* Add an entry to the current beam list */
+
+void
+add_beamlist(value)
+
+RATIONAL value; /* what to add to beam list */
+
+{
+ /* If we guessed too small, make a bigger array */
+ if (Curr_subbeamstyle.count >= Curr_subbeamstyle.length) {
+ Curr_subbeamstyle.length += CHUNK;
+ REALLOCA(RATIONAL, Curr_subbeamstyle.list_p, Curr_subbeamstyle.length);
+ }
+
+ Curr_subbeamstyle.list_p[Curr_subbeamstyle.count] = value;
+ (Curr_subbeamstyle.count)++;
+
+ /* If not in a subbeam grouping, goes into Curr_beamstyle too */
+ if (Subbeam_index < 0) {
+ add2outerbeam(value);
+ }
+}
+\f
+
+/* Add entry to time values for the outermost beam. In the case of subbeaming,
+ * the value will be the sum of the subbeams values; otherwise it will be
+ * the same as that in the subbeam list.
+ */
+
+static void
+add2outerbeam(value)
+
+RATIONAL value;
+
+{
+ /* If we guessed too small, make a bigger array */
+ if (Curr_beamstyle.count >= Curr_beamstyle.length) {
+ Curr_beamstyle.length += CHUNK;
+ REALLOCA(RATIONAL, Curr_beamstyle.list_p, Curr_beamstyle.length);
+ }
+ Curr_beamstyle.list_p[Curr_beamstyle.count] = value;
+ (Curr_beamstyle.count)++;
+}
+\f
+
+/* Assign current beam list to SSV structure in main list */
+
+void
+set_beamlist(mainll_item_p)
+
+struct MAINLL *mainll_item_p; /* where to attach list */
+
+{
+ if (contextcheck(C_SSV, "beamstyle parameter") == NO) {
+ return;
+ }
+
+ /* exclaim if already set in this SSV */
+ used_check(mainll_item_p, BEAMSTLIST, "beamstyle");
+
+ /* Shed any extra allocated space */
+ if (Curr_beamstyle.count == 0) {
+ FREE(Curr_beamstyle.list_p);
+ Curr_beamstyle.list_p = 0;
+ }
+ else if (Curr_beamstyle.count < Curr_beamstyle.length) {
+ REALLOCA(RATIONAL, Curr_beamstyle.list_p, Curr_beamstyle.count);
+ }
+ if (Curr_subbeamstyle.count == 0) {
+ FREE(Curr_subbeamstyle.list_p);
+ Curr_subbeamstyle.list_p = 0;
+ }
+ else if (Curr_subbeamstyle.count < Curr_subbeamstyle.length) {
+ REALLOCA(RATIONAL, Curr_subbeamstyle.list_p, Curr_subbeamstyle.count);
+ }
+
+ if (mainll_item_p == (struct MAINLL *) 0) {
+ pfatal("NULL SSV for beamlist");
+ }
+
+ if (Context != C_SCORE && is_tab_staff(mainll_item_p->u.ssv_p->staffno)
+ == YES) {
+ yyerror("beamstyle not allowed on tablature staff");
+ return;
+ }
+
+ /* attach to the SSV struct and mark as used */
+ mainll_item_p->u.ssv_p->beamstlist = Curr_beamstyle.list_p;
+ mainll_item_p->u.ssv_p->subbeamstlist = Curr_subbeamstyle.list_p;
+ mainll_item_p->u.ssv_p->nbeam = (short) Curr_beamstyle.count;
+ mainll_item_p->u.ssv_p->nsubbeam = (short) Curr_subbeamstyle.count;
+ mainll_item_p->u.ssv_p->used[BEAMSTLIST] = YES;
+
+ if (Curr_beamstyle.count > 0) {
+ /* make sure time adds up to exactly a measure */
+ RATIONAL tot_time; /* sum of times in beamstyle list */
+ register int n;
+
+ tot_time = Zero;
+
+ for (n = 0; n < Curr_beamstyle.count; n++) {
+ tot_time = radd(tot_time, Curr_beamstyle.list_p[n]);
+ }
+
+ if (NE(tot_time, Score.time)) {
+ yyerror("beam list does not add up to a measure");
+ }
+ }
+
+ /* Mark temporary lists as invalid */
+ Curr_beamstyle.list_p = 0;
+ Curr_subbeamstyle.list_p = 0;
+
+ asgnssv(mainll_item_p->u.ssv_p);
+}
+\f
+
+void
+assign_unit(unittype, mainll_p)
+
+int unittype;
+struct MAINLL *mainll_p;
+
+{
+ if (contextcheck(C_SCORE, "units parameter") == NO) {
+ return;
+ }
+
+ mainll_p->u.ssv_p->units = unittype;
+ Score.units = unittype;
+}
+\f
+
+/* return YES if given SSV refers to a tablature staff, NO if not.
+ * This function is different than the is_tab_staff() function in that
+ * this takes an ssv_p and thus can be used on a user's SSV, whereas
+ * is_tab_staff takes a staff number and only works on the SSVs in
+ * the Staff array. */
+
+static int
+is_tablature_staff(ssv_p)
+
+struct SSV *ssv_p;
+
+{
+ return (ssv_p->strinfo != (struct STRINGINFO *) 0 ? YES : NO);
+}
+\f
+
+/* add information about a string for a tablature staff. This gets put
+ * in a malloc-ed strinfo array off the ssv_p struct */
+
+void
+add_tab_string_info(letter, accidental, nticks, octave, ssv_p)
+
+int letter; /* pitch letter 'a' to 'g' */
+int accidental; /* #, &, or \0, others are blocked by parser */
+int nticks; /* how many tick marks, to distinguish multiple
+ * strings with the same pitch/accidental */
+int octave; /* for MIDI and translating to tabnote staff */
+struct SSV *ssv_p; /* add the info to this struct */
+
+{
+ int index; /* which strinfo array element we are filling in */
+ int i;
+
+
+ /* increment number of stafflines. This gets done for real later
+ * by asgn_stafflines(), but we do it here to keep track of how
+ * many structs we have malloc-ed from previous calls to this function
+ * for previous strings */
+ (ssv_p->stafflines)++;
+
+ /* first get space. If first one to add, malloc, otherwise realloc */
+ if (ssv_p->stafflines == 1) {
+ MALLOC(STRINGINFO, ssv_p->strinfo, ssv_p->stafflines);
+ }
+ else {
+ REALLOC(STRINGINFO, ssv_p->strinfo, ssv_p->stafflines);
+ }
+
+ /* get the index of the new element we are adding */
+ index = ssv_p->stafflines - 1;
+
+ /* fill in the data */
+ ssv_p->strinfo[index].letter = (char) letter;
+ ssv_p->strinfo[index].accidental = (char) accidental;
+ ssv_p->strinfo[index].nticks = (short) nticks;
+ ssv_p->strinfo[index].octave
+ = (short) (octave == USE_DFLT_OCTAVE ? TABDEFOCT : octave);
+
+ /* check for duplicate strings */
+ for (i = 0; i < index; i++) {
+ if (ssv_p->strinfo[i].letter == letter
+ && ssv_p->strinfo[i].accidental == accidental
+ && ssv_p->strinfo[i].nticks == nticks) {
+ l_yyerror(Curr_filename, yylineno,
+ "duplicate %c%c%sstring, use ' marks to distinguish",
+ letter, accidental ? accidental : ' ',
+ accidental ? " " : "");
+ }
+ }
+}
+\f
+
+/* Save user-specified measure number in given bar struct,
+ * after verifying it is valid (that is it > 0).
+ */
+
+void
+set_mnum(bar_p, mnum)
+
+struct BAR *bar_p;
+int mnum;
+
+{
+ char *old_reh_string;
+ char num_string[8];
+
+ if (mnum < 1) {
+ l_yyerror(Curr_filename, yylineno, "mnum must be > 0");
+ }
+ else if (bar_p->mnum != 0) {
+ l_yyerror(Curr_filename, yylineno,
+ "mnum cannot be specified more than once per bar");
+ }
+ else {
+ Meas_num = bar_p->mnum = mnum;
+ /* If user had already specified " reh mnum" on this bar,
+ * we would already have made a reh_string for it, so we
+ * have to undo that. It would be nicer to delay the call
+ * to set_reh_string till after we've set mnum, but by then
+ * we would have lost the information we needed unless we
+ * added a bunch more code, so even though this approach
+ * isn't ideal, it's easiest. This shouldn't happen very
+ * often anyway. */
+ if ( (bar_p->reh_type == REH_MNUM) &&
+ (bar_p->reh_string != (char *) 0) ) {
+ old_reh_string = bar_p->reh_string;
+ (void) sprintf(num_string, "%d", mnum);
+ bar_p->reh_string = copy_string(num_string,
+ (int) old_reh_string[0],
+ (int) old_reh_string[1]);
+ FREE(old_reh_string);
+ }
+ }
+}
+\f
+
+/* Give error if margin is too wide, If any is negative, use from Score */
+
+void
+chkmargin(topmargin, botmargin, leftmargin, rightmargin)
+
+double topmargin;
+double botmargin;
+double leftmargin;
+double rightmargin;
+
+{
+ if (topmargin < 0.0) {
+ topmargin = Score.topmargin;
+ }
+ if (botmargin < 0.0) {
+ botmargin = Score.botmargin;
+ }
+ if (leftmargin < 0.0) {
+ leftmargin = Score.leftmargin;
+ }
+ if (rightmargin < 0.0) {
+ rightmargin = Score.rightmargin;
+ }
+ if (((Score.pageheight - topmargin - botmargin) < MIN_USABLE_SPACE)
+ || ((Score.pagewidth - leftmargin - rightmargin)
+ < MIN_USABLE_SPACE)) {
+ yyerror("page size minus margins is too small");
+ }
+}
+\f
+
+/* function to let other files get to the ADJUST2INCHES macro */
+
+double
+adjust2inches(value)
+
+double value;
+
+{
+ ADJUST2INCHES(value);
+ return(value);
+}