chiark / gitweb /
Merge branch 'arkkra' into shiny
[mup] / mup / mup / abshorz.c
diff --git a/mup/mup/abshorz.c b/mup/mup/abshorz.c
new file mode 100644 (file)
index 0000000..8d157db
--- /dev/null
@@ -0,0 +1,3446 @@
+/* Copyright (c) 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2004, 2005 by Arkkra Enterprises */
+/* All rights reserved */
+/*
+ * Name:       abshorz.c
+ *
+ * Description:        This file contains functions for setting absolute
+ *             horizontal coordinates.
+ */
+
+#include <string.h>
+#include "defines.h"
+#include "structs.h"
+#include "globals.h"
+
+static void barclefsigs P((void));
+static int barwithssv P((struct MAINLL *mainll_p));
+static void setclefsigwid P((struct MAINLL *mainll_p, struct CHHEAD *chhead_p));
+static void abschunk P((struct MAINLL *mainll_p, struct MAINLL *end_p));
+static void tryabs P((struct MAINLL *mainll_p, double scale, int *scores_p,
+               short measinscore[]));
+static int endchunk P((struct MAINLL *mainll_p));
+static double adjust_measwid4mrpt P((double oldmeaswid, struct CHORD *ch_p));
+static void fillclefsig P((struct CLEFSIG *clefsig_p, struct MAINLL *feed_p));
+static struct MAINLL *trymeasure P((struct MAINLL *mainll_p, double scale,
+               float *measwidth_p, float *adjust_p, int *ressv_p));
+static void setabs P((struct MAINLL *start_p, int scores, short measinscore[]));
+static void chkrestart P((struct MAINLL *start_p, struct MAINLL *end_p));
+static void setabsscore P((struct MAINLL *start_p, struct MAINLL *end_p));
+static void setabschord P((struct CHORD *ch_p, double nomx));
+static double effwidth P((struct CHORD *ch_p));
+static double bardiff P((struct MAINLL *mainll_p, struct MAINLL *end_p));
+static void fixfullmeas P((struct CHORD *ch_p, double x));
+static void restore_grpsyl_west P((void));
+static void setipw P((void));
+static void setipwgrpsyl P((struct MAINLL *mainll_p, struct GRPSYL *gs_p));
+static void setipwchord P((struct MAINLL *mainll_p));
+static void fixendings P((void));
+static void fixreh P((void));
+static void clrinhprint P((void));
+static int hidestaffs P((struct MAINLL *mainll_p, struct MAINLL *ml2_p));
+static int silent P((struct MAINLL *mainll_p, struct MAINLL *ml2_p, int s,
+               int *ressv_p));
+static int getmultinum P((struct MAINLL *mll_p));
+
+/*
+ * Depending on which type of clefsig, get its full width, or its effective
+ * width.  The latter is for user requested ones, which may overlap notes.
+ */
+#define EFF_WIDCLEFSIG(clefsig_p) \
+       ((clefsig_p)->clefsize == DFLT_SIZE ? width_clefsig(clefsig_p) : \
+       (clefsig_p)->effwidth)
+
+/*
+ * Define the padding after a clefsig.  It's greater when there's a pseudobar,
+ * because we need to allow room for carry-in ties/slurs.
+ */
+#define CSP(clefsig_p) (((clefsig_p)->bar_p == 0 ? 2.0 : 9.0) * STDPAD)
+\f
+/*
+ * Name:        abshorz()
+ *
+ * Abstract:    Set absolute horizontal coordinates of everything.
+ *
+ * Returns:     void
+ *
+ * Description: This function inserts the initial FEED of the piece.  Then, for
+ *             each section of the piece delimited by FEEDs (initial one and
+ *             user-requested ones), it breaks it into the necessary number
+ *             of scores (inserting more FEEDs), and sets all the horizontal
+ *             absolute coordinates.  Finally, it does some fix-up work.
+ */
+
+void
+abshorz()
+
+{
+       struct MAINLL *mainll_p;        /* point at any MAINLL member */
+       struct MAINLL *ml2_p;           /* point at another MAINLL member */
+       struct MAINLL *mainfeed_p;      /* point at MAINLL containing a FEED */
+       struct MAINLL *end_p;           /* point to end of a chunk of MAINLL */
+       int gotbar;                     /* found a bar in this chunk */
+
+
+       debug(16, "abshorz");
+       ml2_p = 0;              /* prevent useless 'used before set' warning */
+
+       /*
+        * The parse phase put any user-requested score feeds in the main
+        * linked list.  We must now insert a FEED before the first measure,
+        * unless the user already requested one there (unlikely!).  Later
+        * we'll add more as needed.
+        */
+       for (mainll_p = Mainllhc_p;
+            mainll_p != 0 && mainll_p->str == S_SSV;
+            mainll_p = mainll_p->next)
+               ;       /* skip by all initial SSVs */
+
+       if (mainll_p == 0)
+               pfatal("main linked list has nothing but SSVs");
+
+       if (mainll_p->str != S_FEED) {
+               mainfeed_p = newMAINLLstruct(S_FEED, 0);
+               insertMAINLL(mainfeed_p, mainll_p->prev);
+       }
+
+       /* whenever a CLEFSIG is needed after a bar line, put one there */
+       barclefsigs();
+
+       /*
+        * Find each section of the main linked list, delimited by FEEDs.
+        * For each such section, call abschunk() to set the absolute
+        * horizontal coords for things in that chunk.  At the end of each
+        * chunk, back up to avoid including SSVs/PRHEADs/LINEs/CURVEs which
+        * might follow the last measure of the chunk.  If a chunk contains no
+        * bar lines (like if there was a useless scorefeed at the end),
+        * don't call abschunk().
+        */
+       /* skip anything before first FEED first */
+       for (mainll_p = Mainllhc_p; mainll_p->str != S_FEED;
+                       mainll_p = mainll_p->next)
+               ;
+       for (;;) {
+               gotbar = NO;
+               for (end_p = mainll_p->next;
+                    end_p != 0 && end_p->str != S_FEED;
+                    ml2_p = end_p, end_p = end_p->next) {
+
+                       if (end_p->str == S_BAR)
+                               gotbar = YES;
+               }
+
+               if (gotbar == NO) {
+                       /*
+                        * If end_p is 0, this must be the end of the MLL, and
+                        * there was a final feed after all the music data.
+                        * There is no need to process this chunk.
+                        */
+                       if (end_p == 0)
+                               break;
+                       /*
+                        * This chunk must contain a BLOCKHEAD.  There is no
+                        * need to process it.  Set the absolute horizontal
+                        * coords.  Then point mainll_p at the FEED at the end
+                        * of the chunk, and go to the next loop.
+                        */
+                       mainll_p->u.feed_p->c[AW] = eff_leftmargin(mainll_p);
+                       mainll_p->u.feed_p->c[AE] = PGWIDTH -
+                                       eff_rightmargin(end_p);
+                       mainll_p->u.feed_p->c[AX] = (mainll_p->u.feed_p->c[AW]
+                                       + mainll_p->u.feed_p->c[AE]) / 2.0;
+                       mainll_p = end_p;
+                       continue;
+               }
+
+               while (ml2_p->str == S_SSV || ml2_p->str == S_PRHEAD ||
+                               ml2_p->str == S_LINE || ml2_p->str == S_CURVE)
+                       ml2_p = ml2_p->prev;
+               abschunk(mainll_p, ml2_p->next);
+               if (end_p == 0)
+                       break;
+               mainll_p = end_p;
+       }
+
+       /* restore west boundaries of GRPSYLs that have associated clefs */
+       restore_grpsyl_west();
+
+       /* set inches per whole ( c[INCHPERWHOLE] ) in the relevant structs */
+       setipw();
+
+       /* move endings that start at end of score to the next score */
+       fixendings();
+
+       /* move rehearsal marks at end of a score to the next score */
+       fixreh();
+
+       /* clear the inhibitprint flag on tablature staffs when appropriate */
+       clrinhprint();
+}
+\f
+/*
+ * Name:        barclefsigs()
+ *
+ * Abstract:    Put a CLEFSIG after each bar line that requires one.
+ *
+ * Returns:     void
+ *
+ * Description: This function loops through the main linked list, applying
+ *             SSVs as it goes.  Whenever an SSV changes clef, key, or time,
+ *             it inserts a CLEFSIG into the list to show what will need to
+ *             be printed at that point.  It also inserts one after any bar
+ *             that is a restart.
+ */
+
+static void
+barclefsigs()
+
+{
+       struct MAINLL *mainll_p;        /* point at items in main linked list*/
+       struct MAINLL *maincs_p;        /* point at MAINLL with CLEFSIG */
+       struct MAINLL *mll_p;           /* point along MLL */
+       struct CLEFSIG *clefsig_p;      /* point at CLEFSIG being filled in */
+       struct BAR *bar_p;              /* point at the bar */
+       struct CHHEAD *chhead_p;        /* point at the latest CHHEAD seen */
+       struct TIMEDSSV *tssv_p;        /* point along timed SSV list */
+       struct GRPSYL *gs_p;            /* point at a group */
+       int oldstaffs;                  /* no. of staffs before SSVs */
+       int oldvis[MAXSTAFFS + 1];      /* visibility of staffs before SSVs */
+       int oldclef[MAXSTAFFS + 1];     /* clefs before SSVs */
+       int newclef[MAXSTAFFS + 1];     /* clefs after SSVs */
+       int premidclef[MAXSTAFFS + 1];  /* clefs before applying midmeas SSVs*/
+       int oldkey[MAXSTAFFS + 1];      /* effective keys before SSVs */
+       int newkey[MAXSTAFFS + 1];      /* effective keys after SSVs */
+       int oldnorm[MAXSTAFFS + 1];     /* is staff "normal" (5 line, nontab)?*/
+       int newnorm[MAXSTAFFS + 1];     /* is staff "normal" (5 line, nontab)?*/
+       char oldtimerep[MAXTSLEN];      /* time sig before SSVs */
+       int oldclefsequal, oldkeysequal;/* were they equal on all staffs? */
+       int newclefsequal, newkeysequal;/* are they equal on all staffs? */
+       int gottimedssv;                /* were there any timed SSVs? */
+       int vidx;                       /* voice index */
+       int lastclef;                   /* last clef printed */
+       RATIONAL offset;                /* offset of a group in a measure */
+       RATIONAL lastclefoffset;        /* offset of last midmeasure clef */
+       int timechg;                    /* did they change time sig info? */
+       int change;                     /* did they change clefs/keys/time? */
+       register int s;                 /* staff number */
+
+
+       debug(16, "barclefsigs");
+       initstructs();                  /* clean out old SSV info */
+
+       /* apply any SSVs that come before the first measure */
+       mainll_p = Mainllhc_p;
+       while (mainll_p != 0 && mainll_p->str == S_SSV) {
+               asgnssv(mainll_p->u.ssv_p);
+               mainll_p = mainll_p->next;
+       }
+
+       chhead_p = 0;           /* keep lint happy; will be set before used */
+
+       /*
+        * Loop once for each bar line in the piece that has SSV(s) after it,
+        * or is a RESTART.  Whenever this occurs, insert a CLEFSIG after them
+        * if required.  RESTART always requires it.
+        */
+       for (;;) {
+               /*
+                * Find the next bar that is either followed by an SSV or has
+                * timed SSVs for the preceding measure or is a restart.  These
+                * are the cases where a CLEFSIG may be needed.  If we hit the
+                * end of the MLL, break out.
+                */
+               while (mainll_p != 0 && ! (mainll_p->str == S_BAR &&
+                               (barwithssv(mainll_p) == YES ||
+                               mainll_p->u.bar_p->timedssv_p != 0 ||
+                               mainll_p->u.bar_p->bartype == RESTART))) {
+                       switch (mainll_p->str) {
+                       case S_CHHEAD:
+                               /* remember the last chhead */
+                               chhead_p = mainll_p->u.chhead_p;
+                               break;
+                       case S_SSV:
+                               /* apply SSVs */
+                               asgnssv(mainll_p->u.ssv_p);
+                               break;
+                       }
+                       mainll_p = mainll_p->next;
+               }
+               if (mainll_p == 0) {
+                       break;
+               }
+               bar_p = mainll_p->u.bar_p;
+
+               /*
+                * If there were timed SSVs in the measure just ended, we need
+                * to make sure that any clef changes requested actually got
+                * printed before some GRPSYL.  If the clef was changed on one
+                * staff by <<score clef = whatever>>, some other staff may be
+                * affected and yet not have a GRPSYL after that point before
+                * which the clef can be printed.  In that case, we want to
+                * generate a CLEFSIG at this bar line, to print it.
+                */
+               tssv_p = bar_p->timedssv_p;
+               gottimedssv = tssv_p != 0;      /* remember if we had any */
+               if (gottimedssv) {
+                       /* get clef state before the timed SSVs */
+                       for (s = 1; s <= Score.staffs; s++) {
+                               premidclef[s] = svpath(s, CLEF)->clef;
+                       }
+                       /* assign the timed SSVs */
+                       for ( ; tssv_p != 0; tssv_p = tssv_p->next) {
+                               asgnssv(&tssv_p->ssv);
+                       }
+               }
+
+               /*
+                * Save the current number of staffs, whether they are visible,
+                * and all clefs and effective keys in case the SSVs coming
+                * up will change some of these things.  Also, save the timesig
+                * info so we can check if it changed (it is settable only in
+                * the score).
+                * Set oldnorm according to whether the staff is "normal",
+                * capable of having a clef or key sig.
+                * Set oldclefsequal and oldkeysequal according to whether all
+                * staffs have the same values for clef and key.
+                */
+               oldclefsequal = oldkeysequal = YES;
+               for (s = 1; s <= Score.staffs; s++) {
+                       oldvis[s] = svpath(s, VISIBLE)->visible;
+                       oldclef[s] = svpath(s, CLEF)->clef;
+                       oldkey[s] = eff_key(s);
+                       oldnorm[s] = svpath(s, STAFFLINES)->stafflines == 5 &&
+                                    svpath(s, STAFFLINES)->printclef == YES &&
+                                       ! is_tab_staff(s) ? YES : NO;
+                       if (s > 1 && oldclef[s - 1] != oldclef[s])
+                               oldclefsequal = NO;
+                       if (s > 1 && oldkey[s - 1] != oldkey[s])
+                               oldkeysequal = NO;
+               }
+               oldstaffs = Score.staffs;
+               strcpy(oldtimerep, Score.timerep);
+
+               /* see if clefs need printing due to timed SSVs */
+               if (gottimedssv) {
+                       /* check this on every staff */
+                       for (s = 1; s <= Score.staffs; s++) {
+                               /* find this staff's MLL structure */
+                               for (mll_p = mainll_p; mll_p->str != S_STAFF ||
+                                    mll_p->u.staff_p->staffno != s;
+                                    mll_p = mll_p->prev) {
+                                       ;
+                               }
+                               /* don't force clefsig for an invisible staff*/
+                               if (mll_p->u.staff_p->visible == NO) {
+                                       continue;
+                               }
+                               /*
+                                * Find last clef that was printed for this
+                                * measure.  Start with the value of the clef
+                                * at the previous bar (before any midmeasure
+                                * clefs).  Then update with the ones that were
+                                * printed midmeasure, until we have the last.
+                                */
+                               lastclef = premidclef[s];
+                               lastclefoffset = rneg(One);
+                               for (vidx = 0; vidx < MAXVOICES; vidx++) {
+                                       /* look down this voice */
+                                       offset = Zero;
+                                       for (gs_p = mll_p->u.staff_p->groups_p[
+                                       vidx]; gs_p != 0; gs_p = gs_p->next) {
+                                               if (gs_p->clef != NOCLEF &&
+                                               GT(offset, lastclefoffset)) {
+                                                       lastclef = gs_p->clef;
+                                                       lastclefoffset = offset;
+                                               }
+                                               offset = radd(offset,
+                                                       gs_p->fulltime);
+                                       }
+                               }
+                               /*
+                                * Set the oldclef to the last one printed.
+                                * Then later code will create a CLEFSIG, if
+                                * necessary.
+                                */
+                               oldclef[s] = lastclef;
+                       }
+               }
+
+               /*
+                * Loop through this set of SSV(s), applying them.  If we hit
+                * the end of the main linked list, break out.  We don't want
+                * to put a CLEFSIG after the last bar line, regardless of
+                * whether it changed anything.
+                */
+               mainll_p = mainll_p->next;
+               while (mainll_p != 0 && mainll_p->str == S_SSV) {
+                       asgnssv(mainll_p->u.ssv_p);
+                       mainll_p = mainll_p->next;
+               }
+               /* Retain mainll_p for later; loop onwards using mll_p.  Need */
+               /* to keep looking for SSVs in case there is a block here. */
+               mll_p = mainll_p;
+               while (mll_p != 0 && (mll_p->str == S_SSV ||
+                                     mll_p->str == S_FEED ||
+                                     mll_p->str == S_BLOCKHEAD)) {
+                       if (mll_p->str == S_SSV) {
+                               asgnssv(mll_p->u.ssv_p);
+                       }
+                       mll_p = mll_p->next;
+               }
+               if (mainll_p == 0)
+                       break;
+
+
+               /*
+                * Get the new clefs and effective keys.
+                * Again, find out if the clefs and keys are equal on all
+                * staffs.
+                */
+               newclefsequal = newkeysequal = YES;
+               for (s = 1; s <= Score.staffs; s++) {
+                       newclef[s] = svpath(s, CLEF)->clef;
+                       newkey[s] = eff_key(s);
+                       newnorm[s] = svpath(s, STAFFLINES)->stafflines == 5 &&
+                                    svpath(s, STAFFLINES)->printclef == YES &&
+                                       ! is_tab_staff(s) ? YES : NO;
+                       if (s > 1 && newclef[s - 1] != newclef[s])
+                               newclefsequal = NO;
+                       if (s > 1 && newkey[s - 1] != newkey[s])
+                               newkeysequal = NO;
+               }
+
+               /* first check if any time sig info changed */
+               if (strcmp(Score.timerep, oldtimerep) != 0)
+                       timechg = YES;
+               else
+                       timechg = NO;
+
+               /*
+                * If the bar was a restart, we treat it as if it were at the
+                * start of a score.  That is, we always print the clefs and
+                * key signatures.  The clefs are full size, and no naturals
+                * are printed.  Print the time signature only if it changed.
+                */
+               if (bar_p->bartype == RESTART) {
+                       /*
+                        * We always require a CLEFSIG.  Allocate one and put
+                        * it between where we are now and the preceding
+                        * structure.
+                        */
+                       maincs_p = newMAINLLstruct(S_CLEFSIG, 0);
+                       insertMAINLL(maincs_p, mainll_p->prev);
+                       clefsig_p = maincs_p->u.clefsig_p;
+
+                       clefsig_p->clefsize = DFLT_SIZE;        /* full size */
+                       clefsig_p->multinum = getmultinum(maincs_p);
+
+                       /*
+                        * Note: If the number of staffs is changing here, the
+                        * following might not be right.  But it doesn't
+                        * matter, because in that case this CLEFSIG will be
+                        * thrown away later anyway.
+                        */
+                       for (s = 1; s <= oldstaffs; s++) {
+                               /* draw nothing if this staff is not "normal" */
+                               if (oldnorm[s] == NO || newnorm[s] == NO)
+                                       continue;
+                               clefsig_p->prclef[s] = YES;
+                               clefsig_p->sharps[s] = newkey[s];
+                       }
+
+                       /* print the time signature if it changed */
+                       clefsig_p->prtimesig = timechg;
+
+                       continue;
+               }
+
+               /*
+                * When the number of staffs changes, special rules apply.
+                * Handle this situation and continue.
+                */
+               if (oldstaffs != Score.staffs) {
+                       /*
+                        * Identify the cases where no clefsig is needed, and
+                        * continue.  This is when time didn't change, and no
+                        * clefs or keys are to be printed.  Clefs are to be
+                        * printed only when all the old staffs had the same
+                        * clef, all the new ones had the same clef, and the
+                        * old and new clefs are different.  The analogous rule
+                        * holds for keys.  This is because, when the number of
+                        * staffs changes, we can't really tell which old staff
+                        * corresponds to which new staff (if any), so it's
+                        * silly to print a clef or key change on any.
+                        */
+                       if (timechg == NO &&
+                                       (oldclefsequal == NO ||
+                                        newclefsequal == NO ||
+                                        oldclef[1] == newclef[1]) &&
+                                       (oldkeysequal == NO ||
+                                        newkeysequal == NO ||
+                                        oldkey[1] == newkey[1])) {
+                               /* no CLEFSIG needed here */
+                               continue;
+                       }
+
+                       /*
+                        * Something changed that requires a CLEFSIG.  Allocate
+                        * one and put it between where we are now and the
+                        * preceding structure, the last SSV we applied.
+                        */
+                       maincs_p = newMAINLLstruct(S_CLEFSIG, 0);
+                       insertMAINLL(maincs_p, mainll_p->prev);
+                       clefsig_p = maincs_p->u.clefsig_p;
+
+                       /* any clefs to be printed should be small size */
+                       clefsig_p->clefsize = SMALLSIZE;
+                       clefsig_p->multinum = getmultinum(maincs_p);
+
+                       /*
+                        * Since the number of staffs is changing, there will
+                        * be a scorefeed here, and this CLEFSIG will be at the
+                        * end of the first score.  Every old staff will have
+                        * the same info printed by the CLEFSIG, except that
+                        * clef and key never exist on "abnormal" staffs. Check
+                        * against staff 1 since we know that has to exist on
+                        * both sides.  It doesn't hurt to mark even invisible
+                        * ones.
+                        */
+                       for (s = 1; s <= oldstaffs; s++) {
+
+                               /* draw nothing if this staff is not "normal" */
+                               if (oldnorm[s] == NO || newnorm[s] == NO)
+                                       continue;
+                               /*
+                                * Draw the new clef if the clefs on each score
+                                * are consistent and they changed.
+                                */
+                               if (oldclefsequal && newclefsequal &&
+                                               oldclef[1] != newclef[1])
+                                       clefsig_p->prclef[s] = YES;
+
+                               /*
+                                * Draw the new key if the keys on each score
+                                * are consistent and they changed.  See below
+                                * for a more detailed explanation.
+                                */
+                               if (oldkeysequal && newkeysequal &&
+                                               oldkey[1] != newkey[1]) {
+                                       clefsig_p->sharps[s] = newkey[1];
+
+                                       if (newkey[1] == 0) {
+                                               clefsig_p->naturals[s] =
+                                                               oldkey[1];
+                                       } else if (svpath(s, CANCELKEY)->
+                                                       cancelkey == YES) {
+                                               if (oldkey[1] * newkey[1] < 0) {
+                                                       /* 1 has #s, 1 has &s */
+                                                       clefsig_p->naturals[s] =
+                                                               oldkey[1];
+                                               } else if (abs(oldkey[1]) >
+                                                               abs(newkey[1])){
+                                                       /* new has fewer accs*/
+                                                       clefsig_p->naturals[s] =
+                                                       oldkey[1] - newkey[1];
+                                               }
+                                       }
+                               }
+                       }
+
+                       /* print the time signature if it changed */
+                       if (timechg == YES)
+                               clefsig_p->prtimesig = YES;
+
+                       /* set clefsig's effective width */
+                       setclefsigwid(maincs_p, chhead_p);
+
+                       continue;
+               }
+
+
+               change = timechg;
+
+               /* see if anything else requiring a CLEFSIG changed */
+               for (s = 1; s <= oldstaffs; s++) {
+                       if (oldvis[s] == NO ||
+                           svpath(s, VISIBLE)->visible == NO)
+                               continue;
+                       if (oldclef[s] != newclef[s])
+                               change = YES;
+                       if (oldkey[s] != newkey[s])
+                               change = YES;
+                       if (change == YES)
+                               break;  /* don't waste any more time looping */
+               }
+               if (change == NO)
+                       continue;       /* no visible time, key, clef changed*/
+
+               /*
+                * If we get here, it means either the clef, effective key, or
+                * time changed on some visible staff.  Allocate a CLEFSIG and
+                * put it between where we are now and the preceding structure,
+                * which is the last SSV we applied.
+                */
+               maincs_p = newMAINLLstruct(S_CLEFSIG, 0);
+               insertMAINLL(maincs_p, mainll_p->prev);
+               clefsig_p = maincs_p->u.clefsig_p;
+
+               /* any clefs to be printed should be small size */
+               clefsig_p->clefsize = SMALLSIZE;
+               clefsig_p->multinum = getmultinum(maincs_p);
+
+               /*
+                * Loop through the staffs, marking in the CLEFSIG what should
+                * be drawn.
+                */
+               for (s = 1; s <= Score.staffs; s++) {
+                       /* draw nothing if this staff is invisible */
+                       if (oldvis[s] == NO)
+                               continue;
+
+                       /* draw nothing if this staff is not "normal" */
+                       if (oldnorm[s] == NO || newnorm[s] == NO)
+                               continue;
+
+                       /* draw the new clef if the clef changed */
+                       if (oldclef[s] != newclef[s])
+                               clefsig_p->prclef[s] = YES;
+
+                       /*
+                        * If the effective key changed, draw the new key
+                        * signature.  But if the new key has 0 sharps, we
+                        * should draw naturals in the shape of the old key
+                        * signature.  And if cancelkey is set, and a sharp key
+                        * is changing to a flat key or vice versa, or the
+                        * number of sharps or flats is being reduced, we need
+                        * enough naturals to cancel the ones being removed.
+                        */
+                       if (oldkey[s] != newkey[s]) {
+                               clefsig_p->sharps[s] = newkey[s];
+                               if (newkey[s] == 0) {
+                                       clefsig_p->naturals[s] = oldkey[s];
+                               } else if (svpath(s, CANCELKEY)->
+                                               cancelkey == YES) {
+                                       if (oldkey[s] * newkey[s] < 0) {
+                                               /* 1 has sharps, 1 has flats */
+                                               clefsig_p->naturals[s] =
+                                                       oldkey[s];
+                                       } else if (abs(oldkey[s]) >
+                                                       abs(newkey[s])) {
+                                               /* new has fewer accidentals */
+                                               clefsig_p->naturals[s] =
+                                                       oldkey[s] - newkey[s];
+                                       }
+                               }
+                       }
+               }
+
+               /*
+                * Finally, print the time signature if it changed.
+                */
+               if (timechg == YES)
+                       clefsig_p->prtimesig = YES;
+
+               /* set clefsig's effective width, and widestclef */
+               setclefsigwid(maincs_p, chhead_p);
+       }
+}
+\f
+/*
+ * Name:        barwithssv()
+ *
+ * Abstract:    Is this bar followed by SSV(s), ignoring FEEDs and BLOCKHEADs?
+ *
+ * Returns:     YES or NO
+ *
+ * Description: This function is called with the MLL structure for a BAR.
+ *             Ignoring the possible presence of FEEDs and BLOCKHEADs, return
+ *             YES if the next structure is an SSV.
+ */
+
+static int
+barwithssv(mainll_p)
+
+struct MAINLL *mainll_p;       /* for the BAR */
+
+{
+       struct MAINLL *mll_p;           /* loop after the BAR */
+
+
+       for (mll_p = mainll_p->next; mll_p != 0; mll_p = mll_p->next) {
+               switch (mll_p->str) {
+               case S_SSV:
+                       return (YES);
+               case S_FEED:
+               case S_BLOCKHEAD:
+                       break;          /* ignore these, keep looking */
+               default:
+                       return (NO);
+               }
+       }
+
+       return (NO);            /* hit end of MLL */
+}
+\f
+/*
+ * Name:        setclefsigwid()
+ *
+ * Abstract:    Set effective width and widest clef of a user requested clefsig.
+ *
+ * Returns:     void
+ *
+ * Description: This function is called with a user-requested clefsig.  If it
+ *             contains clefs, they should be printed before the bar line.  If
+ *             we are lucky, part or all of the clef's widths can overlap notes
+ *             on other staffs.  The effective width is the full width minus
+ *             the amount that can overlap.  This function sets the effective
+ *             width of the clefsig, and the widest clef that it contains.  We
+ *             need to do this now, because we need to use the pseudo absolute
+ *             coords set by relxchord() before they are overwritten later in
+ *             abshorz.c.
+ */
+
+static void
+setclefsigwid(mainll_p, chhead_p)
+
+struct MAINLL *mainll_p;       /* point at the given clefsig's MLL struct */
+struct CHHEAD *chhead_p;       /* point at the preceding chhead */
+
+{
+       struct MAINLL *m2_p;            /* another pointer down the MLL */
+       struct CLEFSIG *clefsig_p;      /* point at a clefsig */
+       struct CHORD *ch_p;             /* point at a chord */
+       struct STAFF *staff_p;          /* point at a staff */
+       struct GRPSYL *gs_p;            /* point at a group in a voice */
+       struct GRPSYL *gs2_p;           /* point at a group in a chord */
+       float lasteast;                 /* phony AE of last chord in measure */
+       float size;                     /* to be used for clef */
+       float clefspace;                /* space needed for printing clefs */
+       float clefwid;                  /* width of a clef */
+       float widestclef;               /* width of the widest to be printed */
+       float staffscale;               /* scale for a staff */
+       float gap;                      /* between last group & last chord */
+       int staffno;                    /* staff number, 1 to MAXSTAFFS */
+       int clef;                       /* clef type */
+       int v;                          /* voice number, 0 to 2 */
+
+
+       debug(16, "setclefsigwid");
+
+       clefsig_p = mainll_p->u.clefsig_p;      /* convenient pointer */
+
+       /*
+        * Although relxchord() overlaps chords in various ways, it does not
+        * overlap the last chord in the measure with anything following.
+        * And it sets phony absolute coordinates for each chord, based on
+        * pretending that everything is packed as tight as possible.  So, as
+        * the rightmost coord of all groups, we can use the AE of the last
+        * chord of the measure just ended.
+        */
+       for (ch_p = chhead_p->ch_p; ch_p->ch_p != 0; ch_p = ch_p->ch_p)
+               ;
+       lasteast = ch_p->c[AE];
+
+       /*
+        * Init the amount of space needed for clefs to be printed.  We start
+        * at zero, and whenever a clef is to be printed, we find out how much
+        * of it can't be overlapped; and clefspace keeps track of the maximum
+        * of these for all staffs.
+        */
+       clefspace = 0.0;
+
+       widestclef = 0.0;       /* max width of any clef to be printed */
+
+       size = 3.0/4.0 * DFLT_SIZE;     /* small size clefs */
+
+       /*
+        * Loop backwards through the preceding measure, looking for visible
+        * staffs to process.
+        */
+       for (m2_p = mainll_p; m2_p->str != S_CHHEAD; m2_p = m2_p->prev) {
+
+               if (m2_p->str != S_STAFF || m2_p->u.staff_p->visible == NO)
+                       continue;
+
+               staff_p = m2_p->u.staff_p;
+               staffno = staff_p->staffno;
+
+               /* if no clef, it doesn't need any space */
+               if (clefsig_p->prclef[staffno] == NO)
+                       continue;
+
+               /* find width of this clef, including padding */
+               clef = svpath(staffno, CLEF)->clef;
+               staffscale = svpath(staffno, STAFFSCALE)->staffscale;
+               clefwid = (clefwidth(clef, YES) + CLEFPAD) * staffscale;
+
+               /* remember biggest clef width */
+               if (clefwid > widestclef)
+                       widestclef = clefwid;
+
+               /* loop through all voices on this staff */
+               for (v = 0; v < MAXVOICES; v++) {
+
+                       /* find last group in this voice */
+                       gs_p = staff_p->groups_p[v];
+                       if (gs_p == 0)
+                               continue;
+                       for ( ; gs_p->next != 0; gs_p = gs_p->next)
+                               ;
+
+                       /*
+                        * Find out what chord this group belongs to, by
+                        * searching down the GRPSYL list hanging off each
+                        * chord in this measure.
+                        */
+                       for (ch_p = chhead_p->ch_p; ch_p != 0;
+                                       ch_p = ch_p->ch_p) {
+                               for (gs2_p = ch_p->gs_p; gs2_p != 0;
+                                               gs2_p = gs2_p->gs_p) {
+                                       /* if found, or after the right staff*/
+                                       if (gs2_p == gs_p || gs2_p->staffno >
+                                                            gs_p->staffno)
+                                               break;
+                               }
+
+                               /*
+                                * If we found it, find the gap between this
+                                * group's AE and the last chord's.  If the
+                                * amount of the clef's width that does not fit
+                                * in that gap is the greatest so far, save it.
+                                */
+                               if (gs2_p == gs_p) {
+                                       gap = lasteast -
+                                               (ch_p->c[AX] + gs_p->c[RE]);
+
+                                       if (clefwid - gap > clefspace)
+                                               clefspace = clefwid - gap;
+
+                                       break;  /* look no more */
+                               }
+                       }
+               } /* loop through voices on a staff */
+       } /* loop through staffs */
+
+       clefsig_p->widestclef = widestclef;
+
+       /* (effective width) = (real width) - (what can overlap) */
+       clefsig_p->effwidth = width_clefsig(clefsig_p) -
+                       (widestclef - clefspace);
+}
+\f
+/*
+ * Name:        abschunk()
+ *
+ * Abstract:    Set the absolute horz. coords of everything in one chunk.
+ *
+ * Returns:     void
+ *
+ * Description: This function is given a chunk of the piece, which is
+ *             delimited by FEEDs.  It estimates how many inches should
+ *             be allocated to each whole note of time.  Then it calls
+ *             tryabs() repeatedly, trying to find a scale factor that
+ *             will avoid having the last score be too empty.  Finally,
+ *             it calls setabs() to set the absolute horizontal coordinates
+ *             of everything in the chunk.
+ */
+
+static void
+abschunk(start_p, end_p)
+
+struct MAINLL *start_p;                /* FEED at start of chunk of MAINLL */
+struct MAINLL *end_p;          /* points after last struct in chunk (or 0) */
+
+{
+       float totwidth;         /* total minimal width of this chunk */
+       float totpseudodur;     /* total pseudodur of measures in this chunk */
+       struct MAINLL *mainll_p;/* point at items in main linked list*/
+       struct CHORD *ch_p;     /* point at a chord */
+       short *measinscore;     /* malloc'ed array; bars in each score */
+       float packfact;         /* as it was at the start of this score */
+       float lowscale;         /* small guess at inches per whole */
+       float highscale;        /* large guess at inches per whole */
+       float midscale;         /* average of low and high scale */
+       float measwidth;        /* width of a measure */
+       int numbars;            /* number of measures in this chunk */
+       int scores;             /* number of scores needed for this chunk */
+       int reqscores;          /* the number of score required */
+       int trial;              /* trial number for getting correct scale */
+
+
+       debug(16, "abschunk file=%s line=%d", start_p->inputfile,
+                       start_p->inputlineno);
+
+       /*
+        * For our first estimate of how wide to make everything, we need to
+        * add up the total minimal width and total elapsed time.
+        */
+       /* must apply all SSVs from start, to get the right time sig */
+       initstructs();                  /* clean out old SSV info */
+       for (mainll_p = Mainllhc_p; mainll_p != start_p;
+                       mainll_p = mainll_p->next) {
+               if (mainll_p->str == S_SSV)
+                       asgnssv(mainll_p->u.ssv_p);
+       }
+
+       packfact = Score.packfact;      /* use current value (start of score) */
+       totwidth = 0;           /* start totals at 0 */
+       totpseudodur = 0;
+       numbars = 0;
+
+       /* loop through chunk, adding up width and time */
+       for ( ; mainll_p != end_p; mainll_p = mainll_p->next) {
+
+               switch (mainll_p->str) {
+               case S_SSV:
+                       /* assign to keep time sig accurate */
+                       asgnssv(mainll_p->u.ssv_p);
+                       break;
+
+               case S_CHHEAD:
+                       /*
+                        * Add in the minimum widths of all chords in the
+                        * measure, and add up the pseudoduration.  There are
+                        * special things done for mrpt: 1) if all staff(s)
+                        * have mrpt, we don't want them to "deserve" as much
+                        * space as an mr, so reduce their pseudodur; 2) the
+                        * minimum width of the measure must be made wide
+                        * enough to contain the symbol.  Also, allow room
+                        * for any midmeasure clefs.  (This will happen
+                        * automatically, because the group boundaries still
+                        * include their preceding midmeasure clefs at this
+                        * point.)
+                        */
+                       measwidth = 0;
+                       ch_p = mainll_p->u.chhead_p->ch_p;
+                       if (ABSDIFF(ch_p->width, TEMPMRPTWIDTH) < 0.001) {
+                               /* the 0.001 is to allow for roundoff error */
+                               ch_p->pseudodur *= 0.5;
+                       }
+                       for ( ; ch_p != 0; ch_p = ch_p->ch_p) {
+                               measwidth += ch_p->width;
+                               /* only count time if there is a real width */
+                               /* (nonspace, or "us", or padded "s") */
+                               if (ch_p->width != 0)
+                                       totpseudodur += ch_p->pseudodur;
+                       }
+                       /* add this measure into the total */
+                       totwidth += adjust_measwid4mrpt(measwidth,
+                                       mainll_p->u.chhead_p->ch_p);
+                       break;
+
+               case S_CLEFSIG:
+                       /* width of clef/key/time to print when changing */
+                       totwidth += EFF_WIDCLEFSIG(mainll_p->u.clefsig_p) +
+                                       CSP(mainll_p->u.clefsig_p);
+                       break;
+
+               case S_BAR:
+                       /* add width of bar line, and incr no. of bars */
+                       totwidth += width_barline(mainll_p->u.bar_p);
+                       numbars++;
+                       break;
+               }
+       }
+
+       /*
+        * Allocate the measinscore array.  We need an element for each score
+        * that this chunk will be broken up into.  We don't yet know how
+        * many that will be.  So allocate enough for the worst case, where
+        * each measure is so wide that it has to go on a separate score.
+        */
+       MALLOCA(short, measinscore, numbars + 1);
+
+       /*
+        * Our first trial is to allow "packfact" times the minimal
+        * width we have just added up, partly to allow for the stuff at the
+        * start of each score (more CLEFSIGs to be inserted after we know
+        * where the FEEDs are going to be), and partly so things can spread
+        * out nicely.
+        */
+       lowscale = packfact * totwidth / totpseudodur;
+       tryabs(start_p, lowscale, &scores, measinscore);
+
+       /*
+        * If the whole chunk fits on one score, just set the absolute coords
+        * for this score and get out.
+        */
+       if (scores == 1) {
+               setabs(start_p, scores, measinscore);
+               FREE(measinscore);
+               return;
+       }
+
+       /*
+        * However many scores tryabs() says were needed, that is what we will
+        * require.  But it's likely that the last score is far from filled up.
+        * It would look bad to just spread out the stuff in the last score.
+        * We want to "balance the load".
+        *
+        * So make up a new scale (highscale) which would probably force us to
+        * use an additional score.  Then loop, binary searching, to find a
+        * value for scale almost as big as possible without forcing a new
+        * score.
+        */
+       reqscores = scores;
+       highscale = 3.0 * lowscale;
+       for (trial = 0; trial < 12; trial++) {
+               midscale = (lowscale + highscale) / 2;
+               tryabs(start_p, midscale, &scores, measinscore);
+               if (scores > reqscores) {
+                       highscale = midscale;
+               } else { /* must be equal, can never be less */
+                       lowscale = midscale;
+               }
+       }
+       /*
+        * If the last one we tried is not a good one, we have to run tryabs
+        * again to reset the scores array properly.
+        */
+       if (midscale != lowscale) {
+               tryabs(start_p, lowscale, &scores, measinscore);
+       }
+
+       setabs(start_p, scores, measinscore);
+
+       FREE(measinscore);
+}
+\f
+/*
+ * Name:        tryabs()
+ *
+ * Abstract:    Given trial scale, find how many scores are needed, etc.
+ *
+ * Returns:     void
+ *
+ * Description: This function, given a proposed scale factor for a chunk
+ *             delimited by FEEDs, figures out how many measures would
+ *             fit on each score.
+ */
+
+static void
+tryabs(start_p, scale, scores_p, measinscore)
+
+struct MAINLL *start_p;                /* FEED at start of chunk of MAINLL */
+double scale;                  /* inches per "whole" unit of time */
+int *scores_p;                 /* return number of scores needed */
+short measinscore[];           /* return number of measures in each score */
+
+{
+       struct MAINLL *mainll_p;/* points along main linked list */
+       struct MAINLL *new_p;   /* points at first struct in new measure */
+       struct MAINLL *ml2_p;   /* another general pointer into MAINLL */
+       struct MAINLL *prevfeed_p;/* where previous FEED is or would go */
+       float remwidth;         /* width remaining on this score */
+       float userdelta;        /* (user right margin) - (normal right margin)*/
+       int atend;              /* are we at the end of the chunk? */
+       struct CLEFSIG clefsig; /* temporary CLEFSIG for start of each score */
+       struct BAR bar;         /* temp BAR; may be need by the above CLEFSIG*/
+       float measwidth;        /* width needed by one measure */
+       float adjust;           /* bar line adjust if last measure in score */
+       int ressv;              /* do we have to re-initstructs() and re-SSV?*/
+
+
+       debug(32, "tryabs file=%s line=%d scale=%f", start_p->inputfile,
+                       start_p->inputlineno, (float)scale);
+       /* must apply all SSVs from start, to get the right clef/key/time; */
+       setssvstate(start_p);
+
+       mainll_p = start_p;
+
+       /*
+        * Set up for beginning of first score in this chunk.
+        * Find out how much width is available, allowing for
+        * margins and stuff to the left (labels, etc.).
+        */
+       *scores_p = 0;                  /* no scores yet */
+       /* left margin may have user override; for now, assume right doesn't */
+       remwidth = PGWIDTH - eff_rightmargin((struct MAINLL *)0)
+                               - eff_leftmargin(start_p);
+       remwidth -= width_left_of_score(start_p);
+       measinscore[0] = 0;
+
+       /*
+        * If the user overrode the right margin at the end of this chunk, we
+        * need to know the difference between what they requested and what the
+        * normal value is.  If they didn't, userdelta will be zero.
+        */
+       userdelta = eff_rightmargin(start_p->next) -
+                   eff_rightmargin((struct MAINLL *)0);
+
+       prevfeed_p = start_p;   /* init previous FEED to the start of chunk */
+
+       /*
+        * We need to set up a provisional CLEFSIG containing what would need
+        * to be printed at the start of this new score.  We can't put it in
+        * the real MAINLL yet, since this function is just trying a
+        * possibility, and cannot alter MAINLL for real.  Set a pointer to
+        * a bar, which in real life would be allocated by fillclefsig.
+        * Subtract the clefsig's width from what we have left to work with.
+        */
+       (void)memset((char *)&clefsig, 0, sizeof(clefsig));
+       (void)memset((char *)&bar, 0, sizeof(bar));
+       clefsig.bar_p = &bar;
+       fillclefsig(&clefsig, mainll_p);
+       remwidth -= width_clefsig(&clefsig) + CSP(&clefsig);
+
+       /* loop through chunk, once per measure, finding where FEEDs would go*/
+       for (;;) {
+               /* get width of this measure, and where next one starts */
+               new_p = trymeasure(mainll_p, scale, &measwidth, &adjust,&ressv);
+
+               atend = endchunk(new_p);        /* last measure of chunk? */
+
+               if (!atend && (measwidth - adjust) <= remwidth ||
+                    atend && (measwidth - adjust) <= remwidth - userdelta ||
+                    measinscore[*scores_p] == 0) {
+                       /*
+                        * There is room for this measure on this score, or
+                        * there are no measures here yet so we better force
+                        * this one onto there.  We'll fail later if this
+                        * in fact cannot fit.
+                        * Subtract its width from what's left on this score,
+                        * and increment the number of measures on it.  Point
+                        * at the next measure.
+                        */
+                       remwidth -= measwidth;
+                       measinscore[*scores_p]++;
+                       mainll_p = new_p;
+
+                       /* if we are at the end, inc no. of scores & return */
+                       if (atend) {
+                               (*scores_p)++;
+                               return;
+                       }
+               } else {
+                       /*
+                        * There is not room for this measure on this score.
+                        * Increment the number of scores needed.
+                        */
+                       (*scores_p)++;
+
+                       /*
+                        * If this last measure ended with SSV(s) after the
+                        * bar line that would cause a CLEFSIG, we need to
+                        * undo the change so that the new score will start
+                        * with the old info.  Sadly, we'll have to init
+                        * the SSVs and apply them over from the beginning.
+                        */
+                       if (ressv) {
+                               setssvstate(mainll_p);
+                       }
+
+                       /*
+                        * Find out how much width is available, allowing for
+                        * margins and stuff to the left (labels, etc.).
+                        * For now, assume this is not the last score, so no
+                        * user margin override.
+                        */
+                       remwidth = PGWIDTH - eff_rightmargin((struct MAINLL *)0)
+                                        - eff_leftmargin((struct MAINLL *)0);
+                       remwidth -= pwidth_left_of_score(mainll_p, prevfeed_p);
+
+                       prevfeed_p = mainll_p;  /* where feed would go */
+
+                       /*
+                        * We need to set up a provisional CLEFSIG containing
+                        * what would need to be printed at the start of this
+                        * new score.  We can't put it in the real MAINLL yet,
+                        * since this function is just trying a possibility,
+                        * and cannot alter MAINLL for real.  In case a repeat
+                        * start is going to be needed, have a bar pointer
+                        * ready.  Subtract the clefsig's width from what we
+                        * have left to work with.
+                        */
+                       (void)memset((char *)&clefsig, 0, sizeof(clefsig));
+                       (void)memset((char *)&bar, 0, sizeof(bar));
+                       clefsig.bar_p = &bar;
+                       fillclefsig(&clefsig, mainll_p);
+                       remwidth -= width_clefsig(&clefsig) + CSP(&clefsig);
+                       measinscore[*scores_p] = 0; /* no bars here yet */
+
+                       /*
+                        * If the measure we just figured is too wide for the
+                        * new score we are about to begin, it must be that
+                        * we are just padding things too much.  (If there
+                        * really is too much stuff in the measure, we'll fail
+                        * later.)  So assume we'll cram in it anyway, set up
+                        * 0 width remaining, and prepare for next measure.
+                        * We have to reapply the SSVs we removed above, since
+                        * we won't be calling trymeasure() again for that
+                        * measure.
+                        *
+                        * If the measure fits, don't do any of this.  Just let
+                        * trymeasure figure the same one over again, next
+                        * time around.
+                        */
+                       if (!atend && measwidth > remwidth ||
+                            atend && measwidth > remwidth - userdelta) {
+                               if (ressv) {
+                                       for (ml2_p = mainll_p; ml2_p != new_p;
+                                                       ml2_p = ml2_p->next) {
+                                               if (ml2_p->str == S_SSV)
+                                                       asgnssv(ml2_p->u.ssv_p);
+                                       }
+                               }
+                               remwidth = 0;
+                               measinscore[*scores_p] = 1;
+                               mainll_p = new_p;
+
+                               /* if at the end, fix no. of scores & ret */
+                               if (atend) {
+                                       (*scores_p)++;
+                                       return;
+                               }
+                       }
+               }
+       }
+}
+\f
+/*
+ * Name:        endchunk()
+ *
+ * Abstract:    Is this MLL item near the end of a chunk?
+ *
+ * Returns:     YES or NO
+ *
+ * Description: This function, given a main linked list structure, finds out
+ *             whether there is nothing left in this chunk of the MLL other
+ *             than possibly SSVs/PRHEADs/LINEs/CURVEs.  The very end of a
+ *             chunk is determined by the end of the MLL, or the FEED that
+ *             begins the next chunk.
+ */
+
+static int
+endchunk(mainll_p)
+
+struct MAINLL *mainll_p;       /* points into the MAINLL */
+
+{
+       /* loop past any SSVs or PRHEADs */
+       while (mainll_p != 0 && (mainll_p->str == S_SSV ||
+                                mainll_p->str == S_PRHEAD ||
+                                mainll_p->str == S_LINE ||
+                                mainll_p->str == S_CURVE))
+               mainll_p = mainll_p->next;
+
+       /* if we hit the end or a FEED, we found the end of this chunk */
+       if (mainll_p == 0 || mainll_p->str == S_FEED)
+               return (YES);
+       return (NO);
+}
+\f
+/*
+ * Name:        adjust_measwid4mrpt()
+ *
+ * Abstract:    If the measure contains a mrpt, adjust the measure's width.
+ *
+ * Returns:     the new (possibly increased) measure width
+ *
+ * Description: This function, given the supposed width of a measure and the
+ *             first chord of the measure, looks to see if there is an mrpt
+ *             in the measure.  If so, it enlarges the chord(s) in the measure
+ *             if necessary, to make sure there's enough room for the symbol.
+ */
+
+static double
+adjust_measwid4mrpt(oldmeaswid, ch_p)
+
+double oldmeaswid;     /* old measure width */
+struct CHORD *ch_p;    /* points at a chord, should be first chord in meas */
+
+{
+       int gotmrpt;            /* is there an mrpt? */
+       struct GRPSYL *gs_p;    /* point down list of GRPSYLs in chord */
+       float newmeaswid;       /* possible new measure width */
+       float thismrpt;         /* space needed by one mrpt and its padding */
+       float increase;         /* how much bigger must we make the measure? */
+
+
+       /*
+        * Scan down the first chord and see if any groups have an mrpt.
+        */
+       gotmrpt = NO;
+       newmeaswid = 0.0;
+       for (gs_p = ch_p->gs_p; gs_p != 0; gs_p = gs_p->gs_p) {
+               if (is_mrpt(gs_p)) {
+                       gotmrpt = YES;
+                       /* get width of this mrpt + its padding, if any */
+                       thismrpt = width(FONT_MUSIC, DFLT_SIZE, C_MEASRPT) *
+                               svpath(gs_p->staffno, STAFFSCALE)->staffscale +
+                               gs_p->padding;
+                       if (thismrpt > newmeaswid) {
+                               newmeaswid = thismrpt;
+                       }
+               }
+       }
+
+       if (gotmrpt == NO)
+               return (oldmeaswid);    /* no mrpt, return original width */
+
+       /* if measure is already wider than all mrpts, return unchanged */
+       if (oldmeaswid >= newmeaswid)
+               return (oldmeaswid);
+
+       /*
+        * Some staff(s) have mrpts, and the existing chord(s) add up to
+        * narrower than the width of an mrpt.  It's really rare that this
+        * could happen if there is only one chord.  So we will handle it by
+        * forcing the first chord to be wider, rather than allocating the
+        * extra amongst all the chords.
+        */
+       if (ABSDIFF(ch_p->width, TEMPMRPTWIDTH) < 0.001) {
+               /* first chord is all mrpt, so no other chords exist; */
+               /* the 0.001 is to allow for roundoff error */
+               ch_p->c[RE] = newmeaswid / 2.0;
+               ch_p->c[RW] = -newmeaswid / 2.0;
+               ch_p->width = newmeaswid;
+       } else {
+               /* add extra to the right side of the first chord */
+               increase = newmeaswid - oldmeaswid;
+               ch_p->c[RE] += increase;
+               ch_p->width += increase;
+       }
+
+       return (newmeaswid);
+}
+\f
+/*
+ * Name:        fillclefsig()
+ *
+ * Abstract:    Fill the CLEFSIG for after a FEED.
+ *
+ * Returns:     void
+ *
+ * Description: This function, given an empty CLEFSIG structure and a pointer
+ *             to a FEED structure in the MAINLL, fills the CLEFSIG according
+ *             to what should be printed.  If called from tryabs() (bar_p !=
+ *             0), the bar will be set to REPEATSTART if need be, based on the
+ *             type of the preceding bar.  If called from setabs() (bar_p ==
+ *             0), the same will be done, if need be, after allocating a BAR
+ *             and setting the pointer to it.  In one bizarre case, abschunk()
+ *             calls here directly, and this is treated the same as tryabs();
+ *             bar_p != 0.
+ */
+
+static void
+fillclefsig(clefsig_p, feed_p)
+
+struct CLEFSIG *clefsig_p;     /* points at empty clefsig to be filled in */
+struct MAINLL *feed_p;         /* points at a FEED in the MAINLL */
+
+{
+       struct MAINLL *mainll_p;/* points along the MAINLL */
+       struct BAR *realbar_p;  /* point at bar before this feed */
+       float barpad;           /* padding on that bar */
+       int s;                  /* staff number */
+
+
+       /*
+        * On every visible staff, the clef and key signature are to
+        * be printed.
+        */
+       for (s = 1; s <= Score.staffs; s++) {
+               if (svpath(s, VISIBLE)->visible == NO)
+                       continue;       /* invisible */
+               clefsig_p->prclef[s] = YES;
+               clefsig_p->sharps[s] = eff_key(s);
+       }
+
+       /* clefs to be printed should be regular size */
+       clefsig_p->clefsize = DFLT_SIZE;
+
+       /*
+        * The time signature is to be printed on the first score, or if
+        * it just changed.  Search back to see if there was a CLEFSIG just
+        * before this FEED where the time changed, or if this is the first
+        * measure.
+        */
+       for (mainll_p = feed_p->prev; mainll_p != 0 &&
+                       mainll_p->str != S_BAR && mainll_p->str != S_CLEFSIG;
+                       mainll_p = mainll_p->prev)
+               ;
+
+       /* see chkrestart() for explanation of the bar_p->c[AY] part of this */
+       if (mainll_p == 0 ||
+                       mainll_p->str == S_CLEFSIG &&
+                               mainll_p->u.clefsig_p->prtimesig == YES ||
+                       mainll_p->str == S_BAR &&
+                               mainll_p->u.bar_p->c[AY] > 0.0)
+               clefsig_p->prtimesig = YES;
+
+       /*
+        * If the preceding BAR (if any) was a REPEATSTART or REPEATBOTH, it
+        * has to be "split up".  Search back to find this bar.
+        */
+       for (mainll_p = feed_p->prev; mainll_p != 0 && mainll_p->str != S_BAR;
+                       mainll_p = mainll_p->prev)
+               ;
+
+       if (clefsig_p->bar_p != 0) {
+               /*
+                * tryabs() called us.  If there is a preceding bar, and it is
+                * REPEATSTART or REPEATBOTH, it would have to be "split", and
+                * our pseudo bar must be made a REPEATSTART.  Otherwise, the
+                * the pseudo bar should be INVISBAR since nothing really
+                * should be printed there.  Since tryabs() called us, we can't
+                * tamper with the preceding (real) bar.  That's okay; the
+                * change would just make that bar slightly narrower, so
+                * things will still fit on that score.
+                */
+               if (mainll_p != 0 &&
+                               (mainll_p->u.bar_p->bartype == REPEATSTART ||
+                                mainll_p->u.bar_p->bartype == REPEATBOTH)) {
+                       clefsig_p->bar_p->bartype = REPEATSTART;
+               } else {
+                       clefsig_p->bar_p->bartype = INVISBAR;
+               }
+       } else {
+               /*
+                * setabs() called us, so we must allocate a pseudo bar and
+                * point the clefsig at it.  The same splitting rules apply as
+                * for tryabs(), except since we're now doing it for real, we
+                * have to really alter the preceding bar's bar type in those
+                * cases.  This preceding bar's AW and AX must also be
+                * adjusted, since the bar is now going to be narrower than it
+                * was before.
+                */
+               CALLOC(BAR, clefsig_p->bar_p, 1);
+
+               if (mainll_p != 0 &&
+                               (mainll_p->u.bar_p->bartype == REPEATSTART ||
+                                mainll_p->u.bar_p->bartype == REPEATBOTH)) {
+
+                       realbar_p = mainll_p->u.bar_p;
+                       realbar_p->bartype = realbar_p->bartype == REPEATSTART ?
+                                       realbar_p->precbartype : REPEATEND;
+                       realbar_p->c[AW] = realbar_p->c[AE] - 
+                                       width_barline(realbar_p);
+                       /* to get AX, temporarily set padding to 0; find the */
+                       /* width of the resulting nonpadded bar, and subtract*/
+                       /* half of that from AE; then restore padding */
+                       barpad = realbar_p->padding;
+                       realbar_p->padding = 0;
+                       realbar_p->c[AX] = realbar_p->c[AE] -
+                                       width_barline(realbar_p) / 2;
+                       realbar_p->padding = barpad;
+                       clefsig_p->bar_p->bartype = REPEATSTART;
+               } else {
+                       clefsig_p->bar_p->bartype = INVISBAR;
+               }
+       }
+}
+\f
+/*
+ * Name:        trymeasure()
+ *
+ * Abstract:    Find trial width of a measure.
+ *
+ * Returns:     Pointer to the first MAINLL structure of the next measure,
+ *             or 0 if we hit the end of MAINLL.
+ *
+ * Description: This function, given a pointer to the first MAINLL structure
+ *             in a measure (or the FEED preceding), finds and fills in the
+ *             width of the measure.
+ */
+
+static struct MAINLL *
+trymeasure(mainll_p, scale, measwidth_p, adjust_p, ressv_p)
+
+struct MAINLL *mainll_p;       /* points first thing in meas, or FEED */
+double scale;                  /* inches per "whole" unit of time */
+float *measwidth_p;            /* width of measure to be filled in */
+float *adjust_p;               /* bar line adjust, to be filled in; if last
+                                * meas in score, bar shouldn't be padded on
+                                * right, so subtract this for measwidth */
+int *ressv_p;                  /* did we apply a CLEFSIG-causing SSV? */
+
+{
+       struct CHORD *ch_p;     /* point at a chord */
+       struct TIMEDSSV *tssv_p;/* point along timed SSV list */
+       float idealwidth;       /* the width a chord should be, based on time*/
+
+
+       if (mainll_p == 0)
+               pfatal("invalid mainll_p argument (0) to trymeasure");
+
+       *ressv_p = NO;          /* assume no SSVs for now */
+
+       /* every measure has one CHHEAD; find it */
+       while (mainll_p != 0 && mainll_p->str != S_CHHEAD)
+               mainll_p = mainll_p->next;
+       if (mainll_p == 0)
+               pfatal("missing CHHEAD near end of main linked list");
+
+       *measwidth_p = 0;
+
+       /*
+        * For each chord, find out how much width it "deserves",
+        * based on its pseudodur.  But if it requires more space
+        * than that, give it what it needs.  Accumulate in *measwidth_p.
+        */
+       for (ch_p = mainll_p->u.chhead_p->ch_p; ch_p != 0;
+                               ch_p = ch_p->ch_p) {
+
+               idealwidth = scale * ch_p->pseudodur;
+               /* but a chord of all compressible, nonpadded spaces */
+               /*  deserves no width */
+               if (ch_p->width == 0)
+                       idealwidth = 0;
+               *measwidth_p += MAX(idealwidth, ch_p->width);
+       }
+
+       /*
+        * Find the bar line and add in its width.
+        */
+       while (mainll_p->str != S_BAR)
+               mainll_p = mainll_p->next;
+       *measwidth_p += width_barline(mainll_p->u.bar_p);
+
+       /* apply any timed SSVs */
+       for (tssv_p = mainll_p->u.bar_p->timedssv_p; tssv_p != 0;
+                       tssv_p = tssv_p->next) {
+               asgnssv(&tssv_p->ssv);
+               *ressv_p = YES;         /* remember we've assigned SSVs */
+       }
+
+       /* need this in case this will be the last measure in the score */
+       *adjust_p = eos_bar_adjust(mainll_p->u.bar_p);
+
+       mainll_p = mainll_p->next;      /* point after bar line */
+       /* if end of MAINLL or next measure is starting up, return now */
+       if (mainll_p == 0 || (mainll_p->str != S_SSV &&
+                             mainll_p->str != S_CLEFSIG))
+               return (mainll_p);
+
+       /* apply any SSVs at this point */
+       while (mainll_p != 0 && mainll_p->str == S_SSV) {
+               asgnssv(mainll_p->u.ssv_p);
+               mainll_p = mainll_p->next;
+               *ressv_p = YES;         /* remember we've assigned SSVs */
+       }
+
+       if (mainll_p != 0 && mainll_p->str == S_CLEFSIG) {
+               *measwidth_p += EFF_WIDCLEFSIG(mainll_p->u.clefsig_p) +
+                               CSP(mainll_p->u.clefsig_p);
+               mainll_p = mainll_p->next;
+       }
+
+       return (mainll_p);
+}
+\f
+/*
+ * Name:        setabs()
+ *
+ * Abstract:    Sets horizontal absolute coordinates for one chunk.
+ *
+ * Returns:     void
+ *
+ * Description: This function, given a chunk of the piece delimited by FEEDs,
+ *             and the number of measures to be put on each score, loops
+ *             through the scores, inserting FEEDs between them, and calling
+ *             setabsscore() to set the horizontal absolute coordinates.
+ */
+
+static void
+setabs(start_p, scores, measinscore)
+
+struct MAINLL *start_p;                /* FEED at start of chunk of MAINLL */
+int scores;                    /* number of scores this chunk needs */
+short measinscore[];           /* number of measures in each score */
+
+{
+       struct MAINLL *mainll_p;/* points along main linked list */
+       struct MAINLL *ml2_p;   /* another general pointer into MAINLL */
+       int score;              /* score number, 0 to scores-1 */
+       int n;                  /* loop counter */
+
+
+       debug(16, "setabs file=%s line=%d scores=%d", start_p->inputfile,
+                       start_p->inputlineno, scores);
+       /* must apply all SSVs from start, to get the right clef/key/time; */
+       setssvstate(start_p);
+
+       /* point at first item in first measure of chunk (skip initial FEED) */
+       mainll_p = start_p->next;
+
+       for (score = 0; score < scores; score++) {
+               /* the first score already has a FEED; insert if later score */
+               if (score != 0) {
+                       ml2_p = newMAINLLstruct(S_FEED, 0);
+                       insertMAINLL(ml2_p, mainll_p->prev);
+               }
+               mainll_p = mainll_p->prev;      /* point at the FEED */
+
+               /*
+                * Insert CLEFSIG following the FEED, and fill as needed.
+                * fillclefsig() will also allocate a BAR, and point the
+                * clefsig at it.  If the previous bar line was a REPEATSTART
+                * or REPEATBOTH, it will set REPEATSTART in the new pseudo
+                * BAR, and alter the preceding bar as necessary.
+                */
+               ml2_p = newMAINLLstruct(S_CLEFSIG, 0);
+               insertMAINLL(ml2_p, mainll_p);
+               fillclefsig(ml2_p->u.clefsig_p, mainll_p);
+               ml2_p->u.clefsig_p->multinum = getmultinum(ml2_p);
+
+               /*
+                * Find end of score by searching forward the correct number
+                * of measures.  Each measure begins with a CHHEAD, and a block
+                * begins with a BLOCKHEAD, so stop at either of these.  Call
+                * a subroutine to process this score.
+                */
+               ml2_p = ml2_p->next;            /* point at CHHEAD */
+               for (n = 0; n < measinscore[score]; n++) {
+                       do {
+                               ml2_p = ml2_p->next;
+                       } while (ml2_p != 0 && ml2_p->str != S_CHHEAD &&
+                                              ml2_p->str != S_BLOCKHEAD);
+               }
+               chkrestart(mainll_p, ml2_p);
+               if (hidestaffs(mainll_p, ml2_p) == YES) {
+                       /* if we had to force any staffs invisible, we have to
+                        * reapply SSVs so that the new ones we inserted take
+                        * effect */
+                       setssvstate(start_p);
+               }
+               setabsscore(mainll_p, ml2_p);
+               mainll_p = ml2_p;
+       }
+}
+\f
+/*
+ * Name:        chkrestart()
+ *
+ * Abstract:    Check for restart bars and remove if necessary.
+ *
+ * Returns:     void
+ *
+ * Description: This function, given one score's worth of input, checks for
+ *             restart bars.  These are used for making a gap in the score.
+ *             So when they are the first or last bar in a score, they don't
+ *             make sense, and should be removed.  Well, not simply removed;
+ *             various things need to be done to the main linked list.
+ */
+
+static void
+chkrestart(start_p, end_p)
+
+struct MAINLL *start_p;                /* point at the initial FEED of this score */
+struct MAINLL *end_p;          /* point after the last thing on this score */
+
+{
+       struct MAINLL *mainll_p;/* points along main linked list */
+       struct MAINLL *m2_p;    /* another pointer along main linked list */
+       int s;                  /* staff number */
+
+
+       /* find first bar on this score; there has to be at least one */
+       for (mainll_p = start_p; mainll_p->str != S_BAR;
+                       mainll_p = mainll_p->next)
+               ;
+
+       if (mainll_p->u.bar_p->bartype == RESTART) {
+               /*
+                * The first bar on the score is a restart.  So the score would
+                * start with whitespace followed by the restart bar, which
+                * would look bad.  So make the restart into an invisbar (which
+                * eliminates the whitespace).  A little negative padding is
+                * needed to make things line up.  Clean out the beginning of
+                * score clefsig so that nothing prints there.  The former
+                * restart's clefsig will print at the start of the line as if
+                * it were the beginning of line clefsig.
+                */
+               mainll_p->u.bar_p->bartype = INVISBAR;
+               mainll_p->u.bar_p->padding = -0.12;
+
+               start_p->next->u.clefsig_p->prtimesig = NO;
+               for (s = 1; s <= MAXSTAFFS; s++) {
+                       start_p->next->u.clefsig_p->prclef[s] = NO;
+                       start_p->next->u.clefsig_p->sharps[s] = 0;
+                       /* no need to zap the naturals, already 0 */
+               }
+       }
+
+       /* find the last bar on this score */
+       m2_p = 0;       /* keep lint happy */
+       for ( ; mainll_p != end_p; mainll_p = mainll_p->next) {
+               if (mainll_p->str == S_BAR)
+                       m2_p = mainll_p;
+       }
+
+       if (m2_p->u.bar_p->bartype == RESTART) {
+               /*
+                * The last bar on the score is a restart.  So the score would
+                * end with whitespace followed by a clefsig, which would look
+                * bad.  So make the restart into an invisbar (which eliminates
+                * the whitespace) and remove the clefsig from the MLL, since
+                * we don't want to show those things at a restart.  The next
+                * score will now be like a restart.
+                */
+               m2_p->u.bar_p->bartype = INVISBAR;
+               m2_p->u.bar_p->padding = 0;
+
+               mainll_p = m2_p;        /* remember bar */
+
+               /* find the clefsig; defensive check for end of MLL */
+               for ( ; m2_p != 0 && m2_p->str != S_CLEFSIG; m2_p = m2_p->next)
+                       ;
+               if (m2_p == 0)
+                       pfatal("the last bar in the piece is a restart");
+
+               /*
+                * When it comes time to create the coming feed and clefsig,
+                * that clefsig's value for prtimesig depends on the one in the
+                * clefsig we are about to remove.  So as a special kluge, if
+                * that value is YES, set the bar's AY to a positive number.
+                * It will get overwritten in absvert.c.
+                */
+               if (m2_p->u.clefsig_p->prtimesig == YES) {
+                       mainll_p->u.bar_p->c[AY] = 1.0;
+               }
+
+               m2_p->prev->next = m2_p->next;
+               m2_p->next->prev = m2_p->prev;
+               FREE(m2_p->u.clefsig_p);
+               FREE(m2_p);
+       }
+}
+\f
+/*
+ * Name:        setabsscore()
+ *
+ * Abstract:    Sets horizontal absolute coordinates for one score.
+ *
+ * Returns:     void
+ *
+ * Description: This function, given one score's worth of input, decides how
+ *             to space everything horizontally to look pleasing, and then
+ *             sets the horizontal absolute coordinates.
+ */
+
+static void
+setabsscore(start_p, end_p)
+
+struct MAINLL *start_p;                /* point at the initial FEED of this score */
+struct MAINLL *end_p;          /* point after the last thing on this score */
+
+{
+       struct MAINLL *mainll_p;/* points along main linked list */
+       struct MAINLL *m2_p;    /* another pointer along main linked list */
+       struct CHORD *ch_p;     /* point at a chord */
+       struct CHORD *firstch_p;/* point at first chord in a measure */
+       struct BAR *bar_p;      /* convenient pointer at a clefsig's bar */
+       struct MAINLL *mm_p;    /* another pointer along MLL */
+       struct MAINLL *lastbarmll_p; /* remember the last bar in the score */
+       struct CLEFSIG *clefsig_p; /* point at a clefsig */
+       struct TIMEDSSV *tssv_p;/* point along a timed SSV list */
+       float prevbarae;        /* remember previous bar's AE */
+       float wid;              /* temp variable, width of something */
+       float eff_right;        /* effective right margin */
+       float scorewidth;       /* total width allowed for the score */
+       float totwidth;         /* total minimum width */
+       float totwhole;         /* total equivalent whole notes of time */
+       float chw;              /* total minimum width of chords */
+       float notespace;        /* space for chords */
+       float nnotespace;       /* space for expandable chords */
+       float ntotwhole;        /* total equiv wholes for expandables */
+       float inchpwhole;       /* inches each whole note should have */
+       float expanded;         /* width of something after expansion */
+       float absx;             /* absolute x coordinate */
+       float leftx, rightx;    /* start and end positions of a measure */
+       float eff;              /* effective width */
+       int toowide;            /* number of chords wider than they deserve */
+       int ntoowide;           /* new no. of chords wider than deserved */
+
+
+       debug(32, "setabsscore file=%s line=%d",
+               start_p->inputfile, start_p->inputlineno);
+       firstch_p = 0;          /* keep lint happy; will be set before used */
+       prevbarae = 0.0;        /* keep lint happy; will be set before used */
+       lastbarmll_p = 0;       /* keep lint happy; will be set before used */
+
+       /*
+        * Get total available width on this score.
+        */
+       if (end_p == 0) {
+               /* find last feed or last bar, whichever comes last */
+               for (m2_p = Mainlltc_p; m2_p->str != S_FEED && 
+                               m2_p->str != S_BAR; m2_p = m2_p->prev)
+                       ;
+               if (m2_p->str == S_FEED) {
+                       /* a feed after the last bar; use it */
+                       eff_right = eff_rightmargin(m2_p);
+               } else {
+                       /* no feed after the last bar */
+                       eff_right = eff_rightmargin((struct MAINLL *)0);
+               }
+       } else {
+               /*
+                * end_p must be the chhead of the following measure.  Its prev
+                * may be a user FEED.  (The CLEFSIG which should be between
+                * them has not been inserted yet.)
+                */
+               if (end_p->prev->str == S_FEED) {
+                       /* it is a feed, so use it */
+                       eff_right = eff_rightmargin(end_p->prev);
+               } else {
+                       /* no feed */
+                       eff_right = eff_rightmargin((struct MAINLL *)0);
+               }
+       }
+
+       scorewidth = PGWIDTH - eff_right - eff_leftmargin(start_p);
+       scorewidth -= width_left_of_score(start_p);
+
+       /*
+        * Accumulate the total minimum width, total pseudodur in equivalent
+        * wholes, and the total minimum width needed by chords.
+        */
+       totwidth = 0;
+       totwhole = 0;
+       chw = 0;
+       for (mainll_p = start_p; mainll_p != end_p; mainll_p = mainll_p->next){
+               switch (mainll_p->str) {
+
+               case S_SSV:
+                       /* assign to keep time sig accurate */
+                       asgnssv(mainll_p->u.ssv_p);
+                       break;
+
+               case S_CHHEAD:
+                       /*
+                        * Add in min widths & time of all chords in measure.
+                        * Skip any leading space chords in determining first.
+                        * (Actually, only compressible, nonpadded spaces.)
+                        * The west part of the first chord is considered part
+                        * a fixed width.  The "effwidth" of a chord is its
+                        * east part plus the west part of the next chord, if
+                        * any.
+                        */
+                       for (ch_p = mainll_p->u.chhead_p->ch_p; ch_p != 0 &&
+                                    ch_p->width == 0; ch_p = ch_p->ch_p)
+                               ;
+                       if (ch_p == 0)
+                               break;
+                       totwidth -= ch_p->c[RW];        /* first nonspace */
+                       for ( ; ch_p != 0; ch_p = ch_p->ch_p) {
+                               totwidth += effwidth(ch_p);
+                               chw += effwidth(ch_p);
+
+                               /* count time only if not a chord of */
+                               /*  compressible, nonpadded spaces */
+                               if (ch_p->width != 0)
+                                       totwhole += ch_p->pseudodur;
+                       }
+                       break;
+
+               case S_CLEFSIG:
+                       /*
+                        * If this clefsig is the last thing on this score
+                        * (except possibly the FEED that starts the next chunk)
+                        * find the preceding bar line.  If that bar has
+                        * hidechanges set, it means that we are not to print
+                        * this clefsig.
+                        */
+                       if (mainll_p->next == end_p ||
+                           mainll_p->next->str == S_FEED) {
+                               for (m2_p = mainll_p; m2_p->str != S_BAR;
+                                               m2_p = m2_p->prev)
+                                       ;
+                               if (m2_p->u.bar_p->hidechanges) {
+                                       mainll_p->u.clefsig_p->hide = YES;
+                                       mainll_p->u.clefsig_p->effwidth = 0.0;
+                               }
+                       }
+
+                       /* width of clef/key/time/repeatstart when needed */
+                       totwidth += EFF_WIDCLEFSIG(mainll_p->u.clefsig_p);
+                       /* pad clefsig, unless it's the last thing on score */
+                       if (mainll_p->next != end_p &&
+                           mainll_p->next->str != S_FEED)
+                               totwidth += CSP(mainll_p->u.clefsig_p);
+                       break;
+
+               case S_BAR:
+                       /* bar's width */
+                       totwidth += width_barline(mainll_p->u.bar_p) +
+                                       mainll_p->u.bar_p->padding;
+                       /* apply any timed SSVs */
+                       for (tssv_p = mainll_p->u.bar_p->timedssv_p;
+                                       tssv_p != 0; tssv_p = tssv_p->next) {
+                               asgnssv(&tssv_p->ssv);
+                       }
+                       lastbarmll_p = mainll_p;
+                       break;
+               }
+       }
+
+       /*
+        * If the last bar is truly at the end of the line, it doesn't need its
+        * full width, because there is no padding after it.  But when there is
+        * a visible clefsig with time or sig there, the bar is not at the end.
+        */
+       for (mm_p = lastbarmll_p; mm_p != 0; mm_p = mm_p->next) {
+               if (mm_p->str == S_STAFF || mm_p->str == S_CLEFSIG) {
+                       break;
+               }
+       }
+       if (mm_p == 0 || mm_p->str != S_CLEFSIG ||
+                               mm_p->u.clefsig_p->hide == YES) {
+               /* no visible clefsig; get rid of padding */
+               totwidth -= eos_bar_adjust(lastbarmll_p->u.bar_p);
+       } else {
+               /* If there is a clefsig, but it has hidechanges,
+                * or it has no time sigature or any key signatures,
+                * it needs to be moved to the edge of the score. */
+               if (mm_p->u.clefsig_p->prtimesig == NO) {
+                       int s;
+                       for (s = 1; s <= MAXSTAFFS; s++) {
+                               if ((mm_p->u.clefsig_p->sharps[s] != 0 ||
+                                    mm_p->u.clefsig_p->naturals[s] != 0) &&
+                                    svpath(s, VISIBLE)->visible == YES) {
+                                       break;
+                               }
+                       }
+                       if (s > MAXSTAFFS) {
+                               totwidth -= eos_bar_adjust(
+                                               lastbarmll_p->u.bar_p);
+                       }
+               }
+       }
+
+       /* fail if even the minimum size for everything doesn't fit */
+       if (totwidth > scorewidth) {
+               if (Score.units == INCHES) {
+                       l_ufatal(start_p->inputfile, start_p->inputlineno,
+                                       "too much (%f inches) to put in score",
+                                       totwidth * Score.scale_factor);
+               } else {
+                       l_ufatal(start_p->inputfile, start_p->inputlineno,
+                                       "too much (%f cm) to put in score",
+                                       totwidth * Score.scale_factor *
+                                       CMPERINCH);
+               }
+       }
+
+       /*
+        * Only chords are allowed to expand when there is extra space;
+        * other items have a fixed width.  To find how much space is
+        * available for chords, take the total screen width minus the
+        * space needed by the fixed-size things.
+        */
+       notespace = scorewidth - (totwidth - chw);
+
+       /*
+        * Some chords' "effwidths" are already wider than what they deserve
+        * based on their pseudodur.  Let them keep that minimum size.  We
+        * will consider their size as fixed and allocate the remaining
+        * space among chords that deserve more.  Remove the too-wide (and
+        * just right) chords from the totals.  This has to be done
+        * repeatedly, since after each iteration the number of inches
+        * deserved by each remaining chord shrinks.  Leave the loop when
+        * it is found that all remaining chords deserve to expand.
+        */
+       ntotwhole = totwhole;   /* initially assume all may be expandable */
+       nnotespace = notespace;
+       ntoowide = 0;
+       do {
+               /*
+                * If there are no notes in this score, totwhole will already
+                * be 0 on the first loop iteration, and there is nothing that
+                * can expand.  Each measure will be very small, just the width
+                * of the bar line and its padding, and the rightmost bar line
+                * won't be at the right edge of the score.  This is usually a
+                * useless situation; but if invisbars are used, and "newscore"
+                * every measure, it provides a way to print blank music paper.
+                *
+                * inchpwhole won't ever get used, but we set it to something
+                * arbitrary in case lint cares.  Then break out of this loop.
+                */
+               if (totwhole == 0.0) {
+                       inchpwhole = 1.0;
+                       break;
+               }
+               /*
+                * Find how much space each whole note worth of chords
+                * deserves, allocating proportionally.  Consider just the
+                * ones not known to be too big already.
+                */
+               inchpwhole = nnotespace / ntotwhole;
+
+               /* start with all chords' time and space */
+               ntotwhole = totwhole;
+               nnotespace = notespace;
+
+               toowide = ntoowide;     /* remember how many last time */
+               ntoowide = 0;
+
+               /* remove from consideration ones that are too big already */
+               for (mainll_p = start_p; mainll_p != end_p;
+                                       mainll_p = mainll_p->next) {
+
+                       if (mainll_p->str == S_CHHEAD) {
+                               /* loop through all chords doing this */
+                               for (ch_p = mainll_p->u.chhead_p->ch_p;
+                                               ch_p != 0; ch_p = ch_p->ch_p) {
+                                       if (effwidth(ch_p) >=
+                                       ch_p->pseudodur * inchpwhole) {
+                                               ntotwhole -= ch_p->pseudodur;
+                                               nnotespace -= effwidth(ch_p);
+                                               ntoowide++;
+                                       }
+                               }
+                       }
+               }
+
+               /*
+                * In the (rare) case where nothing is now expandable (every-
+                * thing is packed perfectly tightly), we should break out now.
+                * The "<" is defensive.
+                */
+               if (ntotwhole <= 0)
+                       break;
+
+       } while (ntoowide > toowide);
+
+       /*
+        * Now inchpwhole is the number of inches that should be given to each
+        * whole note worth of chords that deserve to be wider than their
+        * minimum.  Allocate width proportionally to these chords.
+        */
+       for (mainll_p = start_p; mainll_p != end_p; mainll_p = mainll_p->next){
+               if (mainll_p->str == S_CHHEAD) {
+                       for (ch_p = mainll_p->u.chhead_p->ch_p;
+                                       ch_p != 0; ch_p = ch_p->ch_p) {
+
+                               /* normal case (proportional) */
+                               expanded = ch_p->pseudodur * inchpwhole;
+
+                               /* special case to fix space interaction */
+
+                               /* but a chord of all compressible, */
+                               /*  nonpadded spaces deserves no width */
+                               if (ch_p->width == 0)
+                                       expanded = 0;
+
+                               /* get min dist needed from our X to next's X */
+                               eff = effwidth(ch_p);
+
+                               /* the dist we'll really have from X to X */
+                               ch_p->fullwidth = MAX(eff, expanded);
+                       }
+               }
+       }
+
+       /*
+        * Now that we know everything's width, set all absolute horizontal
+        * coordinates for this score.  The absx variable keeps track of
+        * where we are, working from left to right.  At all times, keep
+        * track of the start and end of each measure (leftx and rightx)
+        * and the first chord in it, so we can reposition measure rests.
+        */
+       /* first reset SSVs to how they were at start of this score */
+       setssvstate(start_p);
+
+       start_p->u.feed_p->c[AW] = eff_leftmargin(start_p);
+       start_p->u.feed_p->c[AE] = PGWIDTH - eff_right;
+       absx = eff_leftmargin(start_p) + width_left_of_score(start_p);
+       start_p->u.feed_p->c[AX] = absx;
+       leftx = 0.0;            /* prevent useless 'used before set' warning */
+
+       for (mainll_p = start_p; mainll_p != end_p; mainll_p = mainll_p->next) {
+               switch (mainll_p->str) {
+               case S_SSV:
+                       /* assign to keep time sig accurate */
+                       asgnssv(mainll_p->u.ssv_p);
+                       break;
+
+               case S_CLEFSIG:
+                       clefsig_p = mainll_p->u.clefsig_p;
+
+                       /* this kind partly already handled by preceding bar */
+                       if (clefsig_p->clefsize == SMALLSIZE &&
+                                       clefsig_p->hide == NO) {
+                               /* absx points at AE of barline, so add width*/
+                               /* of clef excluding any clef space */
+                               absx += width_clefsig(clefsig_p) -
+                                               clefsig_p->widestclef +
+                                               CSP(clefsig_p);
+                               leftx = absx;
+                               break;
+                       }
+
+                       /* "beginning of line" or "restart" clefsig */
+                       clefsig_p->wclefsiga = absx;
+                       if (clefsig_p->hide == NO) {
+                               absx += width_clefsig(clefsig_p) +
+                                               CSP(clefsig_p);
+                       }
+                       bar_p = clefsig_p->bar_p;
+                       if (bar_p != 0) {
+                               /* clefsig has a pseudo bar in it; set coords*/
+                               bar_p->c[AE] = absx;
+                               bar_p->c[AW] = absx - width_barline(bar_p);
+                               bar_p->c[AX] = (bar_p->c[AW] + absx) / 2;
+
+                               /* remember the AE of this pseudobar */
+                               prevbarae = absx;
+                       }
+                       leftx = absx;
+                       break;
+
+               case S_BAR:
+                       bar_p = mainll_p->u.bar_p;
+                       absx += bar_p->padding;
+
+                       /* apply any timed SSVs */
+                       for (tssv_p = bar_p->timedssv_p; tssv_p != 0;
+                                       tssv_p = tssv_p->next) {
+                               asgnssv(&tssv_p->ssv);
+                       }
+
+                       /*
+                        * If this bar is followed by a clefsig, any clefs in
+                        * it must be printed before this bar.  Note that any
+                        * padding will go before the clef (see above).  But
+                        * the previous measure "ends" after the clefs.
+                        */
+                       for (m2_p = mainll_p; m2_p != 0 &&
+                                       m2_p->str != S_CLEFSIG &&
+                                       m2_p->str != S_CHHEAD;
+                                       m2_p = m2_p->next)
+                               ;
+                       /* if clefsig that belongs to this bar line . . . */
+                       if (m2_p != 0 && m2_p->str == S_CLEFSIG && m2_p->u.
+                                       clefsig_p->clefsize == SMALLSIZE &&
+                                       m2_p->u.clefsig_p->hide == NO) {
+                               clefsig_p = m2_p->u.clefsig_p;
+
+                               /*
+                                * Apply SSVs to get the time & clef changes
+                                * that occur at this bar, if any, since we
+                                * are going to print the new values of them.
+                                * After the width_clefsig, restore the SSVs to
+                                * the proper state at this bar line.
+                                */
+                               for (m2_p = mainll_p; m2_p->str != S_CLEFSIG;
+                                               m2_p = m2_p->next) {
+                                       if (m2_p->str == S_SSV) {
+                                               asgnssv(m2_p->u.ssv_p);
+                                       }
+                               }
+                               wid = width_clefsig(clefsig_p);
+                               setssvstate(mainll_p);
+
+                               /* if wid > effwid, this will overlap the */
+                               /* widest clef by that difference */
+                               clefsig_p->wclefsiga = absx -
+                                               (wid - clefsig_p->effwidth) +
+                                               bardiff(mainll_p, end_p);
+
+                               /* point absx after any clefs in clefsig */
+                               absx += clefsig_p->effwidth -
+                                               (wid - clefsig_p->widestclef);
+                               rightx = clefsig_p->wclefsiga;
+                       } else {        /* no relevant clefsig */
+                               rightx = absx;  /* prev measure "ends" here */
+                       }
+                       bar_p->c[AW] = absx;
+                       absx += width_barline(bar_p);
+                       bar_p->c[AE] = absx;
+                       bar_p->c[AX] = (bar_p->c[AW] + bar_p->c[AE]) / 2.0;
+                       fixfullmeas(firstch_p, (leftx + rightx) / 2.0);
+                       leftx = absx;   /* next measure starts here */
+
+                       /*
+                        * for each staff in the measure just ended, set its AE
+                        * to this bar's AW, and set AX to the midpoint now
+                        * that we know both AW and AE.
+                        */
+                       for (m2_p = mainll_p; m2_p->str != S_CHHEAD;
+                                       m2_p = m2_p->prev) {
+                               if (m2_p->str == S_STAFF) {
+                                       m2_p->u.staff_p->c[AE] = bar_p->c[AW];
+                                       m2_p->u.staff_p->c[AX] =
+                                               (m2_p->u.staff_p->c[AW] +
+                                                m2_p->u.staff_p->c[AE]) / 2.0;
+                               }
+                       }
+
+                       /* remember the AE of this bar */
+                       prevbarae = absx;
+                       break;
+
+               case S_STAFF:
+                       /* as we come to each staff, set AW to prev bar's AE */
+                       mainll_p->u.staff_p->c[AW] = prevbarae;
+                       break;
+
+               case S_CHHEAD:
+                       for (ch_p = mainll_p->u.chhead_p->ch_p; ch_p != 0 &&
+                                    ch_p->width == 0; ch_p = ch_p->ch_p)
+                               setabschord(ch_p, absx);
+                       if ((firstch_p = ch_p) == 0)
+                               break;
+                       absx -= firstch_p->c[RW];
+                       for (ch_p = firstch_p; ch_p != 0; ch_p = ch_p->ch_p) {
+                               setabschord(ch_p, absx);
+                               absx += ch_p->fullwidth;
+                       }
+                       break;
+               }
+       }
+}
+\f
+/*
+ * Name:        setabschord()
+ *
+ * Abstract:    Sets horizontal absolute coordinates for everything in a chord.
+ *
+ * Returns:     void
+ *
+ * Description: This function, given a chord, and its absolute offset, sets
+ *             the horizontal absolute coordinates of everything in it.
+ */
+
+static void
+setabschord(ch_p, nomx)
+
+struct CHORD *ch_p;            /* point at the chord */
+double nomx;                   /* nominal X coord; may shift it right a bit */
+
+{
+       struct GRPSYL *gs_p;    /* point at a group or syllable in chord */
+       struct GRPSYL *g_p;     /* point at a group with notes */
+       float extra;            /* width available beyond what chord needs */
+       int n;                  /* loop counter */
+
+
+       /*
+        * Set the CHORD's horizonal absolute coordinates.  If the chord had
+        * no room to expand (effwidth == fullwidth), there's no question
+        * where its AX has to be.  But otherwise, we want to place it close
+        * to as far left as it can go, but not jammed up against there.
+        */
+       if ((extra = ch_p->fullwidth - effwidth(ch_p)) > 0) {
+               nomx += (extra > 1.20 ? 0.20 : extra / 6);
+       }
+
+       ch_p->c[AX] = nomx;
+       ch_p->c[AW] = nomx + ch_p->c[RW];
+       ch_p->c[AE] = nomx + ch_p->c[RE];
+
+       /*
+        * Loop through all GRPSYLs in this chord, setting absolute horizontal
+        * coordinates.  To avoid the aggravation of dealing with SSVs again,
+        * don't bother checking if the staffs in question are visible, just
+        * do it.  It doesn't hurt anything to increment garbage.
+        */
+       for (gs_p = ch_p->gs_p; gs_p != 0; gs_p = gs_p->gs_p) {
+               /*
+                * For groups, do the group itself and all the notes in it (if
+                * any), and do the same for all preceding grace groups.
+                */
+               if (gs_p->grpsyl == GS_GROUP) {
+                       g_p = gs_p;     /* init to the normal group */
+                       do {
+                               /* do the group itself, based off the chord */
+                               g_p->c[AX] = ch_p->c[AX] + g_p->c[RX];
+                               g_p->c[AW] = ch_p->c[AX] + g_p->c[RW];
+                               g_p->c[AE] = ch_p->c[AX] + g_p->c[RE];
+
+                               /* do each note, based off the group */
+                               for (n = 0; n < g_p->nnotes; n++) {
+                                       g_p->notelist[n].c[AX] = g_p->c[AX] +
+                                                       g_p->notelist[n].c[RX];
+                                       g_p->notelist[n].c[AW] = g_p->c[AX] +
+                                                       g_p->notelist[n].c[RW];
+                                       g_p->notelist[n].c[AE] = g_p->c[AX] +
+                                                       g_p->notelist[n].c[RE];
+                               }
+                               g_p = g_p->prev;
+                       } while (g_p != 0 && g_p->grpvalue == GV_ZERO);
+               } else {
+                       /* this is a syllable; just do the syllable */
+                       gs_p->c[AX] = ch_p->c[AX] + gs_p->c[RX];
+                       gs_p->c[AW] = ch_p->c[AX] + gs_p->c[RW];
+                       gs_p->c[AE] = ch_p->c[AX] + gs_p->c[RE];
+               }
+       }
+}
+\f
+/*
+ * Name:        effwidth()
+ *
+ * Abstract:    Find "effective" width of a chord.
+ *
+ * Returns:     the width
+ *
+ * Description: This function returns the "effective width" of a chord.  This
+ *             is the (minimum) width of its east part, plus the width of the
+ *             west part of the following chord, if there is one.
+ */
+
+static double
+effwidth(ch_p)
+
+struct CHORD *ch_p;            /* point at the chord */
+
+{
+       struct CHORD *next_p;
+
+
+       /* compressible, nonpadded spaces count for nothing */
+       if (ch_p->width == 0)
+               return (0.0);
+
+       /* find the next chord, if any, that is not all compressible, */
+       /*  nonpadded spaces */
+       for (next_p = ch_p->ch_p; next_p != 0; next_p = next_p->ch_p) {
+               if (next_p->width != 0)
+                       break;
+       }
+
+       /*
+        * If it's the last one in the measure, return the east side of the
+        * current chord.  Otherwise, return that plus the west side of the
+        * next nonspace chord.
+        */
+       if (next_p == 0)
+               return (ch_p->c[RE]);
+       else
+               return (ch_p->c[RE] - next_p->c[RW]);
+}
+\f
+/*
+ * Name:        bardiff()
+ *
+ * Abstract:    Find size difference of end of score bar vs. what it will be.
+ *
+ * Returns:     void
+ *
+ * Description: When a REPEATSTART occurs at the end of a score, it gets
+ *             changed to a SINGLEBAR, and a REPEATBOTH becomes a REPEATEND
+ *             (the following pseudobar getting set to REPEATSTART).  Other
+ *             bartypes are left alone.  This function, given the MLL of a bar,
+ *             just returns zero if the bar is not at the end of a score; but
+ *             otherwise it returns the size of that bartype minus the size of
+ *             what it will be replaced by.
+ */
+
+static double
+bardiff(mainll_p, end_p)
+
+struct MAINLL *mainll_p;       /* MLL for the bar line */
+struct MAINLL *end_p;          /* MLL after end of the score */
+
+{
+       struct MAINLL *mll_p;   /* for searching the MLL */
+       struct BAR bar;         /* phony BAR structure */
+       double temp;            /* hold the width of the orginal bar */
+
+
+       /*
+        * Search forward from the bar.  If we hit a CHHEAD before hitting the
+        * end of the score, then this is not the last barline in the score, so
+        * return zero.
+        */
+       for (mll_p = mainll_p; mll_p != end_p; mll_p = mll_p->next) {
+               if (mll_p->str == S_CHHEAD)
+                       return (0.0);
+       }
+
+       /* last bar in the score, so do the arithmetic */
+       switch (mainll_p->u.bar_p->bartype) {
+       case REPEATSTART:
+               bar.bartype = REPEATSTART;
+               temp = width_barline(&bar);
+               bar.bartype = SINGLEBAR;
+               return (temp - width_barline(&bar));
+
+       case REPEATBOTH:
+               bar.bartype = REPEATBOTH;
+               temp = width_barline(&bar);
+               bar.bartype = REPEATEND;
+               return (temp - width_barline(&bar));
+       }
+
+       return (0.0);   /* all other types remain the same; difference = 0 */
+}
+\f
+/*
+ * Name:        fixfullmeas()
+ *
+ * Abstract:    Adjust the AE of full measure symbols (mr, multirest, mrpt).
+ *
+ * Returns:     void
+ *
+ * Description: This function, given the first chord in a measure (the only
+ *             one that can contain a one of these symbols), adjusts the AE
+ *             coord of each GRPSYL in the chords that is one of these.  AW
+ *             stays where it is, near the left bar line, except that for
+ *             multirests it moves it to the right, especially for ones
+ *             that are drawn with rest symbols.  For multirests and
+ *             measure repeats, AX gets moved leftwards a little, to be
+ *             where it would have been for a measure rest, but for measure
+ *             rests, it stays where it is, not far to the right of that.
+ *             For all three things, AE is put near the right bar line, the
+ *             same distance from it that AW is from the left.
+ */
+
+static void
+fixfullmeas(ch_p, x)
+
+struct CHORD *ch_p;            /* point at the chord */
+double x;                      /* absolute X coord of center of measure */
+
+{
+       struct GRPSYL *gs_p;    /* point at a group or syllable in chord */
+
+
+       /* in case we have all spaces */
+       if (ch_p == 0)
+               return;
+
+       debug(32, "fixfullmeas file=%s line=%d x=%f", ch_p->gs_p->inputfile,
+                       ch_p->gs_p->inputlineno, (float)x);
+
+       /* loop through all GRPSYLs, resetting AE/AW for full measure symbols */
+       for (gs_p = ch_p->gs_p; gs_p != 0; gs_p = gs_p->gs_p) {
+               /* skip syllables */
+               if (gs_p->grpsyl != GS_GROUP) {
+                       continue;
+               }
+
+               if (gs_p->is_meas == YES) {
+                       gs_p->c[AE] = x + (x - gs_p->c[AW]);
+               } else if (gs_p->basictime < -1) {
+                       /* multirest; move the left end to the right a little */
+                       set_staffscale(gs_p->staffno);
+                       gs_p->c[AW] += 2.0 * Stepsize;
+                       /*
+                        * For multirests that are drawn with rest symbols,
+                        * the width may need to be reduced.  If half the
+                        * multirest's width exceeds 10 stepsizes, reduce it
+                        * by 0.8 of the excess.
+                        */
+                       if (gs_p->basictime >= -8 && svpath(gs_p->staffno,
+                                               RESTSYMMULT)->restsymmult) {
+                               if (x - gs_p->c[AW] > 10.0 * Stepsize) {
+                                       gs_p->c[AW] += ((x - gs_p->c[AW]) -
+                                                     (10.0 * Stepsize)) * 0.8;
+                               }
+                       }
+                       gs_p->c[AE] = x + (x - gs_p->c[AW]);
+               }
+       }
+
+       /* for multirest/mrpt, put AX where it would have been for a mr */
+       if (ch_p->gs_p->basictime < -1 || is_mrpt(gs_p)) {
+               ch_p->c[AX] = ch_p->c[AW] +
+                               width(FONT_MUSIC, DFLT_SIZE, C_1REST) / 2;
+       }
+}
+\f
+/*
+ * Name:        restore_grpsyl_west()
+ *
+ * Abstract:    Restore all GRPSYLs' west coords when there was a clef there.
+ *
+ * Returns:     void
+ *
+ * Description: In fixclef() in restsyl.c, we altered the west of any GRPSYL
+ *             that was associated with a midmeasure clef.  This was needed so
+ *             that room would be made for the clefs.  Now that the packing
+ *             part of abshorz.c is done, we can restore these coords, for the
+ *             benefit of the print phrase.
+ */
+
+static void
+restore_grpsyl_west()
+
+{
+       struct MAINLL *mainll_p;        /* point along main linked list */
+       struct GRPSYL *gs_p;            /* point along a GRPSYL list */
+       struct GRPSYL *gs2_p;           /* look for a grace group's main grp */
+       int vidx;                       /* voice index */
+       float size;                     /* to be used for clef */
+       float staffscale;               /* scale for a staff */
+       float clefwid;                  /* width of a clef */
+
+
+       size = 3.0/4.0 * DFLT_SIZE;     /* small size clefs */
+       initstructs();
+
+       for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
+               switch (mainll_p->str) {
+               case S_SSV:
+                       /* keep staffscale up to date */
+                       asgnssv(mainll_p->u.ssv_p);
+                       continue;
+               case S_STAFF:
+                       /* break out to handle staff */
+                       break;
+               default:
+                       continue;
+               }
+
+               for (vidx = 0; vidx < MAXVOICES; vidx++) {
+                       for (gs_p = mainll_p->u.staff_p->groups_p[vidx];
+                                       gs_p != 0; gs_p = gs_p->next) {
+                               if (gs_p->clef == NOCLEF) {
+                                       continue; /* no clef, nothing to do */
+                               }
+
+                               staffscale = svpath(gs_p->staffno,
+                                               STAFFSCALE)->staffscale;
+                               clefwid = (clefwidth(gs_p->clef, YES) +
+                                               CLEFPAD) * staffscale;
+                               gs_p->c[RW] += clefwid;
+                               gs_p->c[AW] += clefwid;
+
+                               /*
+                                * If we are a grace group, look ahead to the
+                                * main group and restore it too.
+                                */
+                               if (gs_p->grpvalue == GV_ZERO) {
+                                       for (gs2_p = gs_p; gs2_p->grpvalue ==
+                                       GV_ZERO; gs2_p = gs2_p->next) {
+                                               ;
+                                       }
+                                       gs2_p->c[RW] += clefwid;
+                                       gs2_p->c[AW] += clefwid;
+                               }
+                       }
+               }
+       }
+}
+\f
+/*
+ * Name:        setipw()
+ *
+ * Abstract:    Set INCHPERWHOLE "coordinate" for all structures having it.
+ *
+ * Returns:     void
+ *
+ * Description: This function sets the special pseudocoord "c[INCHPERWHOLE]"
+ *             for all nongrace GRPSYLs, notes, chords, and BARs.  BARs is
+ *             done right here; for the others, it calls subroutines.
+ */
+
+static void
+setipw()
+
+{
+       struct MAINLL *mainll_p;        /* point along main linked list */
+       struct MAINLL *m2_p;            /* look forward for bar line */
+       struct GRPSYL *gs_p;            /* point along a GRPSYL list */
+       int timeden;                    /* denominator of a time signature */
+       int v;                          /* index into voices or verses */
+
+
+       debug(16, "setipw");
+       initstructs();                  /* clean out old SSV info */
+
+       /*
+        * Loop through MLL, applying SSVs and processing each visible linked
+        * list of GRPSYLs.
+        */
+       for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
+
+               switch (mainll_p->str) {
+               case S_SSV:
+                       /* this is needed to keep time sig up to date */
+                       asgnssv(mainll_p->u.ssv_p);
+                       break;
+
+               case S_CHHEAD:
+                       /* set the thing for all chords in this measure */
+                       setipwchord(mainll_p);
+                       break;
+
+               case S_STAFF:
+                       /* skip this staff if it's invisible */
+                       if (mainll_p->u.staff_p->visible == NO)
+                               break;
+
+                       /* do all the voices on this staff */
+                       for (v = 0; v < MAXVOICES && (gs_p = mainll_p->u.
+                                       staff_p->groups_p[v]) != 0; v++) {
+                               setipwgrpsyl(mainll_p, gs_p);
+                       }
+
+                       /* do all the verses on this staff */
+                       for (v = 0; v < mainll_p->u.staff_p->nsyllists; v++) {
+                               gs_p = mainll_p->u.staff_p->syls_p[v];
+                               setipwgrpsyl(mainll_p, gs_p);
+                       }
+
+                       break;
+
+               case S_BAR:
+                       /*
+                        * If this is the ending bar line of a score, ignore
+                        * it.  The following measure would refer to its
+                        * preceding CLEFSIG's pseudo bar instead.  So see if
+                        * we hit a FEED while trying to find the next CHHEAD.
+                        * While doing this, keep track of the denominator of
+                        * the time signature, in case it changes at this bar.
+                        */
+                       timeden = Score.timeden;
+                       for (m2_p = mainll_p; m2_p != 0 && m2_p->str != S_FEED
+                                       && m2_p->str != S_CHHEAD;
+                                       m2_p = m2_p->next) {
+                               if (m2_p->str == S_SSV && m2_p->u.ssv_p->used[
+                                               TIME] == YES) {
+                                       timeden = m2_p->u.ssv_p->timeden;
+                               }
+                       }
+                       if (m2_p == 0 || m2_p->str == S_FEED)
+                               break;
+
+                       /*
+                        * This is not the last bar of a score, and m2_p points
+                        * at the CHHEAD of the following measure, with timeden
+                        * being the denominator of the time sig.  The space
+                        * between the bar ("count 0") and the first chord
+                        * ("count 1") must be multiplied by the number of
+                        * counts in a whole note (timeden).
+                        */
+                       mainll_p->u.bar_p->c[INCHPERWHOLE] = timeden *
+                                       (m2_p->u.chhead_p->ch_p->c[AX] -
+                                       mainll_p->u.bar_p->c[AX]);
+                       break;
+
+               case S_CLEFSIG:
+                       /*
+                        * If this clefsig is not at the start of a score,
+                        * ignore it.  If it is, it will contain a pseudo bar
+                        * line, and we need to set that bar's coord just like
+                        * for a normal bar line.
+                        */
+                       if (mainll_p->u.clefsig_p->bar_p == 0)
+                               break;
+
+                       if (mainll_p->next->str != S_CHHEAD)
+                               pfatal("CLEFSIG with pseudo bar not followed by CHHEAD");
+
+                       mainll_p->u.clefsig_p->bar_p->c[INCHPERWHOLE] =
+                               Score.timeden *
+                               (mainll_p->next->u.chhead_p->ch_p->c[AX] -
+                               mainll_p->u.clefsig_p->bar_p->c[AX]);
+                       break;
+               }               
+       }
+}
+\f
+/*
+ * Name:        setipwgrpsyl()
+ *
+ * Abstract:    Set INCHPERWHOLE "coordinate" for the GRPSYLs in one list.
+ *
+ * Returns:     void
+ *
+ * Description: This function sets the special pseudocoord "c[INCHPERWHOLE]"
+ *             for all the nongrace GRPSYLs and notes in one voice or verse
+ *             list hanging off a STAFF.
+ */
+
+static void
+setipwgrpsyl(mainll_p, gs_p)
+
+struct MAINLL *mainll_p;               /* point along main linked list */
+struct GRPSYL *gs_p;                   /* point along this GRPSYL list */
+
+{
+       struct MAINLL *m2_p;            /* look forward for bar line */
+       struct GRPSYL *ngs_p;           /* the next nongrace GRPSYL in list */
+       float inchperwhole;             /* inches per whole note */
+       int n;                          /* loop variable */
+
+
+       debug(32, "setipwgrpsyl file=%s line=%d", gs_p->inputfile,
+                       gs_p->inputlineno);
+       /* get first nongrace GRPSYL */
+       for ( ; gs_p != 0 && gs_p->grpsyl == GS_GROUP &&
+                            gs_p->grpvalue == GV_ZERO; gs_p = gs_p->next)
+               ;
+       if (gs_p == 0)
+               pfatal("nothing but grace notes in measure");
+
+       /*
+        * Loop down the list of GRPSYLs.  gs_p always points the current
+        * (nongrace) GRPSYL, whose inches per whole we want to set.  ngs_p
+        * points at the next nongrace GRPSYL.
+        */
+       for (;;) {
+               /* find next nongrace GRPSYL; break if none */
+               for (ngs_p = gs_p->next;
+                    ngs_p != 0 && ngs_p->grpsyl == GS_GROUP &&
+                                  ngs_p->grpvalue == GV_ZERO;
+                    ngs_p = ngs_p->next)
+                       ;
+               if (ngs_p == 0)
+                       break;
+
+               /*
+                * Distance between them divided by time gives the space a
+                * a whole note theoretically would have been given.
+                */
+               inchperwhole = (ngs_p->c[AX] - gs_p->c[AX]) /
+                               RAT2FLOAT(gs_p->fulltime);
+
+               /* store in GRPSYL & each note (if notes) */
+               gs_p->c[INCHPERWHOLE] = inchperwhole;
+               if (gs_p->grpsyl == GS_GROUP && gs_p->grpcont == GC_NOTES) {
+                       for (n = 0; n < gs_p->nnotes; n++)
+                               gs_p->notelist[n].c[INCHPERWHOLE]
+                                               = inchperwhole;
+               }
+
+               /* point current at next, for next iteration */
+               gs_p = ngs_p;
+       }
+
+       /*
+        * We've hit the end of the measure.  Loop forward through the MLL
+        * until we find the bar line.
+        */
+       for (m2_p = mainll_p;
+            m2_p != 0 && m2_p->str != S_BAR;
+            m2_p = m2_p->next)
+               ;
+       if (m2_p == 0)
+               pfatal("no bar at end of last measure");
+
+       /* this time use bar line as terminating point */
+       inchperwhole = (m2_p->u.bar_p->c[AX] - gs_p->c[AX]) /
+                       RAT2FLOAT(gs_p->fulltime);
+
+       gs_p->c[INCHPERWHOLE] = inchperwhole;
+       if (gs_p->grpsyl == GS_GROUP && gs_p->grpcont == GC_NOTES) {
+               for (n = 0; n < gs_p->nnotes; n++)
+                       gs_p->notelist[n].c[INCHPERWHOLE] = inchperwhole;
+       }
+}
+\f
+/*
+ * Name:        setipwchord()
+ *
+ * Abstract:    Set INCHPERWHOLE "coordinate" for the CHORDs in one list.
+ *
+ * Returns:     void
+ *
+ * Description: This function sets the special pseudocoord "c[INCHPERWHOLE]"
+ *             for all the CHORDs in the list hanging off of one CHHEAD.
+ */
+
+static void
+setipwchord(mainll_p)
+
+struct MAINLL *mainll_p;               /* point at the CHHEAD */
+
+{
+       struct MAINLL *m2_p;            /* look forward for bar line */
+       struct CHORD *ch_p, *nch_p;     /* point at chords */
+
+
+       debug(32, "setipwchord file=%s line=%d", mainll_p->inputfile,
+                               mainll_p->inputlineno);
+       /*
+        * Loop down the list of CHORDs.  ch_p always points the current
+        * CHORD, whose inches per whole we want to set.  nch_p points at
+        * the next CHORD.  When nch_p is 0, ch_p is the last chord, and we
+        * get out of the loop.
+        */
+       for (ch_p = mainll_p->u.chhead_p->ch_p, nch_p = ch_p->ch_p;
+                       nch_p != 0; ch_p = nch_p, nch_p = nch_p->ch_p) {
+               /*
+                * Distance between them divided by time gives the space a
+                * a whole note theoretically would have been given.
+                */
+               ch_p->c[INCHPERWHOLE] = (nch_p->c[AX] - ch_p->c[AX]) /
+                                       RAT2FLOAT(ch_p->duration);
+       }
+
+       /*
+        * We've hit the end of the measure.  Loop forward through the MLL
+        * until we find the bar line.
+        */
+       for (m2_p = mainll_p;
+            m2_p != 0 && m2_p->str != S_BAR;
+            m2_p = m2_p->next)
+               ;
+       if (m2_p == 0)
+               pfatal("no bar at end of last measure");
+
+       /* this time use bar line as terminating point */
+       ch_p->c[INCHPERWHOLE] = (m2_p->u.bar_p->c[AX] - ch_p->c[AX]) /
+                               RAT2FLOAT(ch_p->duration);
+}
+\f
+/*
+ * Name:        fixendings()
+ *
+ * Abstract:    Fix endings at end of score and in pseudobars.
+ *
+ * Returns:     void
+ *
+ * Description: This function finds endings that start at the final bar of a
+ *             score.  It moves them so that they will start at the pseudobar
+ *             at the start of the next score.  Then, wherever an ending is
+ *             continuing through a scorefeed, set the pseudobar's endingloc.
+ */
+
+static void
+fixendings()
+
+{
+       struct MAINLL *mainll_p;        /* point along main linked list */
+       struct MAINLL *m2_p;            /* look forward for bar line */
+       struct BAR *bar_p;              /* point at preceding bar */
+       char *str_p;                    /* point at an ending string */
+
+       
+       debug(16, "fixendings");
+       /*
+        * Loop through the main linked list, looking for endings that start at
+        * the end of a score, and moving them.  We do it in reverse, to make
+        * it slightly easier to deal with the case of scores that have only
+        * one measure on them.  (Previous endings won't have been moved yet.)
+        */
+       for (mainll_p = Mainlltc_p; mainll_p != 0; mainll_p = mainll_p->prev) {
+               if (mainll_p->str != S_BAR)
+                       continue;
+               if (mainll_p->u.bar_p->endingloc != STARTITEM)
+                       continue;
+
+               /*
+                * We are at a bar where an ending starts.  Find out if this is
+                * at the end of a score, by seeing if we find a FEED before
+                * the next bar.
+                */
+               for (m2_p = mainll_p->next; m2_p != 0 && m2_p->str != S_BAR &&
+                               m2_p->str != S_FEED; m2_p = m2_p->next)
+                       ;
+               if (m2_p == 0)
+                       pfatal("unterminated ending");
+               if (m2_p->str == S_BAR)
+                       continue;
+
+               /*
+                * The ending starts at the last bar of a score.  We need to
+                * know whether a previous ending also ends there, or not.  So
+                * search back to the previous bar.  Since we're doing the main
+                * loop in reverse, we don't have to look at pseudobars, only
+                * real ones.
+                */
+               for (m2_p = mainll_p->prev; m2_p != 0 && m2_p->str != S_BAR;
+                               m2_p = m2_p->prev)
+                       ;
+
+               /*
+                * If the previous bar was the end of an ending or not involved
+                * in one at all, the bar at the end of the score should not be
+                * involved.  Otherwise, there was a preceding ending which
+                * ends here (where the new one starts), so mark that it ends.
+                */
+               if (m2_p == 0 || m2_p->u.bar_p->endingloc == ENDITEM ||
+                                m2_p->u.bar_p->endingloc == NOITEM)
+
+                       mainll_p->u.bar_p->endingloc = NOITEM;
+               else
+                       mainll_p->u.bar_p->endingloc = ENDITEM;
+
+               str_p = mainll_p->u.bar_p->endinglabel;
+               mainll_p->u.bar_p->endinglabel = 0;
+
+               /*
+                * Find the first feed after this bar that is not at the start
+                * of a "block", and mark in the following pseudobar that an
+                * ending starts there.
+                */
+               for (m2_p = mainll_p->next; m2_p != 0 && (m2_p->str != S_FEED ||
+                            m2_p->next != 0 && m2_p->next->str == S_BLOCKHEAD);
+                            m2_p = m2_p->next)
+                       ;
+               if (m2_p == 0) {
+                       pfatal("can't find any music after ending begins");
+               }
+               m2_p->next->u.clefsig_p->bar_p->endingloc = STARTITEM;
+               m2_p->next->u.clefsig_p->bar_p->endinglabel = str_p;
+       }
+
+       /*
+        * Loop again through the main linked list, this time forwards.
+        * Remember each bar as we find one.  Then, adjust the following
+        * pseudobar if need be.
+        */
+       bar_p = 0;              /* no previous bar yet */
+       for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
+               if (mainll_p->str == S_BAR)
+                       bar_p = mainll_p->u.bar_p;
+               if (mainll_p->str == S_CLEFSIG &&
+                                       mainll_p->u.clefsig_p->bar_p != 0) {
+                       /*
+                        * We're at a pseudobar.  If the preceding bar was
+                        * inside an ending, mark the pseudobar that way too.
+                        * (If this is the first pseudobar, there won't have
+                        * been any preceding bar.)
+                        */
+                       if (bar_p != 0 && bar_p->endingloc == INITEM)
+                               mainll_p->u.clefsig_p->bar_p->endingloc
+                                               = INITEM;
+               }
+       }
+}
+\f
+/*
+ * Name:        fixreh()
+ *
+ * Abstract:    Move rehearsal marks at end of a score to the next score.
+ *
+ * Returns:     void
+ *
+ * Description: This function finds rehearsal marks at the final bar of a
+ *             score.  It moves them so that they will be at the pseudobar
+ *             at the start of the next score.
+ */
+
+static void
+fixreh()
+
+{
+       struct MAINLL *mainll_p;        /* point along main linked list */
+       struct MAINLL *m2_p;            /* look forward for bar line */
+
+       
+       debug(16, "fixreh");
+       /*
+        * Loop through the main linked list, looking for rehearsal marks at
+        * the end of a score, and moving them.
+        */
+       for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
+               if (mainll_p->str != S_BAR)
+                       continue;
+               if (mainll_p->u.bar_p->reh_type == REH_NONE)
+                       continue;
+
+               /*
+                * We are at a bar with a rehearsal mark.  Find out if this is
+                * at the end of a score, by seeing if we find a FEED before
+                * the next bar.
+                */
+               for (m2_p = mainll_p->next; m2_p != 0 && m2_p->str != S_BAR &&
+                               m2_p->str != S_FEED; m2_p = m2_p->next)
+                       ;
+               if (m2_p == 0)
+                       return; /* nothing more we can do in this case */
+               if (m2_p->str == S_BAR)
+                       continue;
+
+               /*
+                * The ending starts at the last bar of a score.  m2_p is at
+                * the FEED there, but what follows could be either music or a
+                * "block".  If it is a block, we need to keep moving forward
+                * until we find a FEED followed by music.
+                */
+               while (m2_p != 0 && ! IS_CLEFSIG_FEED(m2_p)) {
+                       m2_p = m2_p->next;
+               }
+               if (m2_p == 0) {
+                       return; /* there is no more music, can't move reh */
+               }
+
+               /*
+                * We found the FEED.  Move the rehearsal mark to the pseudo
+                * bar after the FEED.
+                */
+               m2_p->next->u.clefsig_p->bar_p->reh_type =
+                               mainll_p->u.bar_p->reh_type;
+               mainll_p->u.bar_p->reh_type = REH_NONE;
+
+               m2_p->next->u.clefsig_p->bar_p->reh_string =
+                               mainll_p->u.bar_p->reh_string;
+               mainll_p->u.bar_p->reh_string = 0;
+
+               m2_p->next->u.clefsig_p->bar_p->dist =
+                               mainll_p->u.bar_p->dist;
+               mainll_p->u.bar_p->dist = 0;
+
+               m2_p->next->u.clefsig_p->bar_p->dist_usage =
+                               mainll_p->u.bar_p->dist_usage;
+               mainll_p->u.bar_p->dist_usage = SD_NONE;
+       }
+}
+\f
+/*
+ * Name:        clrinhprint()
+ *
+ * Abstract:    Clear the inhibitprint on tablature staffs when appropriate.
+ *
+ * Returns:     void
+ *
+ * Description: This function clears the inhibitprint bit in the first group
+ *             of a tablature staff after a scorefeed.  (Because in that
+ *             situation, the group should be printed regardless of the usual
+ *             conditions that inhibit printing.)  Also, parentheses should be
+ *             put around every note (fret number) in such groups.
+ */
+
+static void
+clrinhprint()
+
+{
+       struct MAINLL *mainll_p;        /* point along main linked list */
+       struct GRPSYL *gs_p;            /* point at first group */
+       int sawscorefeed;               /* did we just see a scorefeed? */
+       int vidx;                       /* voice index */
+       int n;                          /* loop through the notes */
+
+       
+       debug(16, "clrinhprint");
+       sawscorefeed = YES;             /* "new score" at start of song */
+
+       /*
+        * Loop through main linked list, looking for visible tablature STAFFs,
+        * scorefeeds, and bar lines.
+        */
+       for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
+
+               switch (mainll_p->str) {
+               case S_FEED:
+                       sawscorefeed = YES;     /* just saw a feed */
+                       continue;
+               case S_BAR:
+                       sawscorefeed = NO;      /* next bar, forget the feed */
+                       continue;
+               case S_STAFF:
+                       /*
+                        * If we just saw a scorefeed, and this is a visible
+                        * tablature staff, break to handle it.  Otherwise
+                        * continue to the next loop iteration.
+                        */
+                       if (sawscorefeed == YES &&
+                       mainll_p->u.staff_p->visible == YES &&
+                       is_tab_staff(mainll_p->u.staff_p->staffno))
+                               break;
+                       continue;
+               default:
+                       continue;
+               }
+
+               /* loop through each possible voice on tab staff */
+               for (vidx = 0; vidx < MAXVOICES; vidx++) {
+
+                       /* if voice doesn't exist, break out */
+                       gs_p = mainll_p->u.staff_p->groups_p[vidx];
+                       if (gs_p == 0)
+                               break;
+
+                       /* if not a note group, there's nothing to do */
+                       if (gs_p->grpcont != GC_NOTES)
+                               continue;
+
+                       /*
+                        * If inhibitprint was set, we need to put parens
+                        * around the notes (frets) and clear the bit.
+                        */
+                       if (gs_p->inhibitprint == YES) {
+                               for (n = 0; n < gs_p->nnotes; n++)
+                                       gs_p->notelist[n].FRET_HAS_PAREN = YES;
+                               gs_p->inhibitprint = NO;
+                       }
+               }
+       }
+}
+\f
+/*
+ * Name:        hidestaffs()
+ *
+ * Abstract:    Make staffs invisible if visible=whereused and they're empty.
+ *
+ * Returns:     void
+ *
+ * Description: If the user set visible=whereused for staffs, up until now we
+ *             have been treating it as visible=y, because the internal field
+ *             visible==YES.  But now that we know where all the scorefeeds
+ *             are, this function looks at hidesilent for the given score, and
+ *             when a staff should be invisible based on that, inserts SSVs
+ *             and sets the visible field in the STAFF structures to make it
+ *             invisible.
+ */
+
+static int
+hidestaffs(mainll_p, ml2_p)
+
+struct MAINLL *mainll_p;       /* point at feed at start of score */
+struct MAINLL *ml2_p;          /* point at last thing on the score */
+
+{
+       struct SSV *ssv_p;      /* a static SSV containing visibility */
+       int s;                  /* staff number */
+       int firstvis;           /* first visible staff in a score */
+       int foundvis;           /* is a staff after first still visible? */
+       int forced_invis;       /* did we force any staffs invisible? */
+       int ressv;              /* must we reapply SSVs from the start? */
+
+
+       debug(16, "hidestaffs");
+
+       /*
+        * Loop through main linked list, applying SSVs and looking for FEEDs.
+        * When a FEED is found, check all the staffs and make the appropriate
+        * ones invisible.
+        */
+       /* find the first (currently) visible staff in this score */
+       firstvis = 0;
+       for (s = 1; s <= Score.staffs; s++) {
+               if (svpath(s, VISIBLE)->visible == YES) {
+                       firstvis = s;
+                       break;
+               }
+       }
+       if (firstvis == 0) {
+               pfatal("no visible staffs in score");
+       }
+
+       /*
+        * Working bottom up, check each currently visible staff to see whether
+        * it should be made invisible.  If so, make it so.  But if nothing
+        * below the first visible staff ends up visible, we leave it alone,
+        * since at least one staff must always be visible.
+        * There are cases when silent() has to apply some SSVs.  In such
+        * cases, it sets ressv=YES.  Sadly, we have to reapply SSVs from the
+        * start in that case.
+        */
+       foundvis = NO;
+       forced_invis = NO;
+       for (s = Score.staffs; s >= firstvis; s--) {
+               if (s == firstvis && foundvis == NO) {
+                       /* only the top visible staff remains visible */
+                       break;
+               }
+               ssv_p = svpath(s, VISIBLE);
+               if (ssv_p->visible == NO) {     /* already invisible */
+                       continue;
+               }
+               if (ssv_p->hidesilent == YES) {
+                       if (silent(mainll_p, ml2_p, s, &ressv) == YES) {
+                               /* silent() forced it invisible */
+                               forced_invis = YES;
+                       } else {
+                               /* silent() left it visible */
+                               foundvis = YES;
+                       }
+                       if (ressv == YES) {
+                               setssvstate(mainll_p);
+                       }
+               } else {        /* hidesilent == NO */
+                       foundvis = YES; /* leave it visible */
+               }
+       }
+
+       return (forced_invis);
+}
+\f
+/*
+ * Name:        silent()
+ *
+ * Abstract:    Make a staff invisible for this score, if appropriate.
+ *
+ * Returns:     YES if we made it invisible, else NO
+ *
+ * Description: This function decides whether the given staff should be made
+ *             invisible on the given score (line).  It should be called only
+ *             when visible==YES and hidesilent==YES.  If it should be made
+ *             invisible, it does that by inserting new "input" SSVs into the
+ *             MLL before and after that line, and setting the visible field
+ *             in the staffs to NO.  There are cases where this function calls
+ *             asgnssv(); in those cases it sets *ressv_p to YES, otherwise NO.
+ */
+
+static int
+silent(feedmll_p, ml2_p, s, ressv_p)
+
+struct MAINLL *feedmll_p;      /* point along main linked list */
+struct MAINLL *ml2_p;          /* point at MLL item at end of this score */
+int s;                         /* staff number */
+int *ressv_p;                  /* must the caller reapply SSVs? */
+
+{
+       struct MAINLL *mll_p;   /* point along MLL */
+       struct MAINLL *lastbar_p; /* last bar line in score */
+       struct MAINLL *ins_p;   /* point at MLL after which a new SSV goes */
+       struct MAINLL *new_p;   /* point at MLL struct for a new SSV */
+       struct SSV *ssv_p;      /* an SSV */
+       struct STAFF *staff_p;  /* point at a STAFF */
+       struct STAFF *pstaff_p; /* point at the previous STAFF */
+       struct GRPSYL *gs_p;    /* point at a group or syllable */
+       struct STUFF *stuff_p;  /* point along a STUFF list */
+       int vidx;               /* voice or verse index */
+
+
+       *ressv_p = NO;          /* no SSVs have been applied yet */
+
+       /* find the last bar line in this score; it's where we should stop */
+       lastbar_p = 0;
+       for (mll_p = feedmll_p->next; mll_p != ml2_p && mll_p->str != S_FEED;
+                               mll_p = mll_p->next) {
+               if (mll_p->str == S_BAR) {
+                       lastbar_p = mll_p;
+               }
+       }
+       /* if none, there is no music here */
+       if (lastbar_p == 0) {
+               return (NO);    /* nothing to hide */
+       }
+
+       /*
+        * Loop through this score, checking SSVs and looking in the STAFFs for
+        * this staff number, looking for reasons we must keep the staff
+        * visible.
+        */
+       for (mll_p = feedmll_p; mll_p != lastbar_p; mll_p = mll_p->next) {
+               switch (mll_p->str) {
+               case S_SSV:
+                       /*
+                        * To minimize the chances that we will apply an SSV
+                        * and thus have to initstructs() and reapply from the
+                        * beginning, apply only if it is relevent to what we
+                        * are doing.
+                        */
+                       ssv_p = mll_p->u.ssv_p;
+                       if (ssv_p->context != C_SCORE && ssv_p->staffno != s) {
+                               /* this SSV is irrelevant to our staff */
+                               continue;
+                       }
+                       if (ssv_p->used[VISIBLE] != NO) {
+                               /*
+                                * This SSV could affect our staff's visibility.
+                                * Apply it, and remember that we've now messed
+                                * with the fixed SSVs, and so we'll have to
+                                * reapply from the start.
+                                */
+                               asgnssv(ssv_p);
+                               *ressv_p = YES;
+                       }
+                       /*
+                        * This staff started this score with visible==YES and
+                        * hidesilent==YES.  We know we are not going to see an
+                        * SSV that causes our staff to go invisible, since
+                        * that would have forced a scorefeed.  But we could
+                        * see one that causes our hidesilent value to be NO,
+                        * and in that case we can immediately return NO, since
+                        * it must remain visible.
+                        */
+                       if (svpath(s, VISIBLE)->hidesilent == NO) {
+                               return (NO);
+                       }
+                       continue;
+
+               case S_STAFF:
+                       staff_p = mll_p->u.staff_p;
+                       if (staff_p->staffno != s) {
+                               continue;       /* some other staff, ignore */
+                       }
+                       break;          /* break out to handle our staff */
+
+               default:
+                       continue;
+               }
+
+               /* decide whether this staff can be made invisible */
+
+               /*
+                * Look at each group in each possible voice.  If any contain
+                * notes, our staff must remain visible.
+                */
+               for (vidx = 0; vidx < MAXVOICES; vidx++) {
+                       for (gs_p = staff_p->groups_p[vidx]; gs_p != 0;
+                                       gs_p = gs_p->next) {
+                               if (gs_p->grpcont == GC_NOTES) {
+                                       return (NO);
+                               }
+                       }
+               }
+
+               /* if there are any syllables, our staff must remain visible */
+               if (staff_p->nsyllists != 0) {
+                       return (NO);
+               }
+
+               /* if there is any stuff, our staff must remain visible */
+               if (staff_p->stuff_p != 0) {
+                       return (NO);
+               }
+
+               /*
+                * If the previous MLL structure is a staff, it could have
+                * lyrics or "stuff" between it and our staff.  If this
+                * previous staff is already invisible, ignore it since these
+                * things would be invisible.  But the previous staff is
+                * visible, check for any of them being "between", in which
+                * case our staff must remain visible.
+                */
+               if (mll_p->prev->str == S_STAFF) {
+                       pstaff_p = mll_p->prev->u.staff_p;
+                       if (pstaff_p->visible == YES) {
+                               for (vidx = 0; vidx < pstaff_p->nsyllists;
+                                                       vidx++) {
+                                       if (pstaff_p->sylplace[vidx] ==
+                                                               PL_BETWEEN) {
+                                               return (NO);
+                                       }
+                               }
+                               for (stuff_p = pstaff_p->stuff_p; stuff_p != 0;
+                                               stuff_p = stuff_p->next) {
+                                       if (stuff_p->place == PL_BETWEEN) {
+                                               return (NO);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /*
+        * At this point we've looked through everything and found that there
+        * is no need to keep this staff visible.  So we are going to force it
+        * invisible.  If a staff's SSV says visible==NO but it has voice(s)
+        * with visible==YES, it ends up being visible anyhow.  So in addition
+        * to forcing the staff to visible=NO, we will unset all its voices'
+        * visibility.  Rather than checking how many voices there are, it's
+        * easiest just to force all possible ones invisible.
+        */
+
+       /*
+        * Set ins_p to the SSV after which the new ones should be put.  There
+        * may be a CLEFSIG before the FEED; if so, they should be put before
+        * there, otherwise just before the FEED.  This is to maintain the
+        * correct ordering of structures; see comment at the end of structs.h.
+        * If the FEED is at the start, ins_p will be 0.
+        */
+       ins_p = feedmll_p->prev;
+       if (ins_p != 0 && ins_p->str == S_CLEFSIG) {
+               ins_p = ins_p->prev;
+       }
+
+       /* force staff's visible to NO */
+       new_p = newMAINLLstruct(S_SSV, -1);
+       ssv_p = new_p->u.ssv_p;
+       ssv_p->context = C_STAFF;
+       ssv_p->staffno = s;
+       ssv_p->used[VISIBLE] = YES;
+       ssv_p->visible = NO;
+       insertMAINLL(new_p, ins_p);
+
+       /* force voices' visible to unset */
+       for (vidx = 0; vidx < MAXVOICES; vidx++) {
+               new_p = newMAINLLstruct(S_SSV, -1);
+               ssv_p = new_p->u.ssv_p;
+               ssv_p->context = C_VOICE;
+               ssv_p->staffno = s;
+               ssv_p->voiceno = vidx + 1;
+               ssv_p->used[VISIBLE] = UNSET;
+               insertMAINLL(new_p, ins_p);
+       }
+
+       /* do not let any SSVs on this line alter this staff's visibility */
+       for (mll_p = feedmll_p; mll_p != lastbar_p; mll_p = mll_p->next) {
+               if (mll_p->str != S_SSV) {
+                       continue;
+               }
+               ssv_p = mll_p->u.ssv_p;
+               /*
+                * Since we know we are overriding the score, we don't care if
+                * the score is changing.  Just force all staff and voice SSVs
+                * for this staff to not be setting VISIBLE.
+                */
+               if (ssv_p->context != C_SCORE && ssv_p->staffno == s) {
+                       ssv_p->used[VISIBLE] = NO;
+               }
+       }
+
+       /* the SSVs to be put at the end go after the last bar line */
+       ins_p = lastbar_p;
+
+       /*
+        * Insert "input" SSVs that will cause the staff's fixed SSV and its
+        * voices' fixed SSVs to be restored to how they would have been if we
+        * hadn't changed anything.  That is the state they are in right now.
+        */
+       new_p = newMAINLLstruct(S_SSV, -1);
+       ssv_p = new_p->u.ssv_p;
+       ssv_p->context = C_STAFF;
+       ssv_p->staffno = s;
+       if (Staff[s-1].used[VISIBLE] == YES) {
+               ssv_p->used[VISIBLE] = YES;
+               ssv_p->visible    = Staff[s-1].visible;
+               ssv_p->hidesilent = Staff[s-1].hidesilent;
+       } else {
+               ssv_p->used[VISIBLE] = UNSET;
+       }
+       insertMAINLL(new_p, ins_p);
+
+       for (vidx = 0; vidx < MAXVOICES; vidx++) {
+               new_p = newMAINLLstruct(S_SSV, -1);
+               ssv_p = new_p->u.ssv_p;
+               ssv_p->context = C_VOICE;
+               ssv_p->staffno = s;
+               ssv_p->voiceno = vidx + 1;
+               if (Voice[s-1][vidx].used[VISIBLE] == YES) {
+                       ssv_p->used[VISIBLE] = YES;
+                       ssv_p->visible    = Voice[s-1][vidx].visible;
+                       ssv_p->hidesilent = Voice[s-1][vidx].hidesilent;
+               } else {
+                       ssv_p->used[VISIBLE] = UNSET;
+               }
+               insertMAINLL(new_p, ins_p);
+       }
+
+       /* set visible to NO in every staff of this number on this line */
+       for (mll_p = feedmll_p; mll_p != lastbar_p; mll_p = mll_p->next) {
+               if (mll_p->str == S_STAFF) {
+                       staff_p = mll_p->u.staff_p;
+                       if (staff_p->staffno == s) {
+                               staff_p->visible = NO;
+                       }
+               }
+       }
+
+       return (YES);
+}
+\f
+/*
+ * Name:        getmultinum()
+ *
+ * Abstract:    Find number of measures in the next staff's multirest.
+ *
+ * Returns:     The number, or 0 if next staff is not a multirest.
+ *
+ * Description: This function is given an MLL struct, and if it's not a STAFF,
+ *             searches forward to the next STAFF.  It returns as stated above.
+ */
+
+static int
+getmultinum(mll_p)
+
+struct MAINLL *mll_p;          /* point along MLL, starts at the CLEFSIG */
+
+{
+       int basictime;  /* of the first group in the first following staff */
+
+
+       /* find the first staff after this clefsig */
+       for ( ; mll_p != 0 && mll_p->str != S_STAFF; mll_p = mll_p->next) {
+               ;
+       }
+
+       /* if no staff, there is no multirest */
+       if (mll_p == 0) {
+               return (0);
+       }
+
+       basictime = mll_p->u.staff_p->groups_p[0]->basictime;
+       return (basictime < -1 ? -basictime : 0);
+}