chiark / gitweb /
Merge branch 'arkkra' into shiny
[mup] / mup / mup / assign.c
diff --git a/mup/mup/assign.c b/mup/mup/assign.c
new file mode 100644 (file)
index 0000000..9c938dd
--- /dev/null
@@ -0,0 +1,2181 @@
+
+/* 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);
+}