chiark / gitweb /
Merge branch 'arkkra' into shiny
[mup] / mup / mup / ssv.c
diff --git a/mup/mup/ssv.c b/mup/mup/ssv.c
new file mode 100644 (file)
index 0000000..4ec37b2
--- /dev/null
@@ -0,0 +1,1420 @@
+/* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ * 2006 by Arkkra Enterprises */
+/* All rights reserved */
+/*
+ * Name:       ssv.c
+ *
+ * Description:        This file defines the instances of score, staff, and voice
+ *             structures that are used for viewpathing.  It also contains
+ *             functions for accessing them.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "defines.h"
+#include "rational.h"
+#include "structs.h"
+#include "globals.h"
+
+/* define the default order of stacking for marks */
+static char Defmarkorder[] = {
+       1,      /* MK_MUSSYM    */
+       2,      /* MK_OCTAVE    */
+       3,      /* MK_DYN       */
+       3,      /* MK_OTHERTEXT */
+       3,      /* MK_CHORD     */
+       4,      /* MK_LYRICS    */
+       5,      /* MK_ENDING    */
+       6,      /* MK_REHEARSAL */
+       7,      /* MK_PEDAL     */
+};
+
+/* default timesig of 4/4 */
+static char Dflt_timerep[] = { 4, TSR_SLASH, 4, TSR_END };
+
+static void freestaffset P((int num, struct STAFFSET *ss_p));
+static void asgnstaffset P((int num, struct STAFFSET **new_p_p,
+               struct STAFFSET *old_p));
+static void setorder P((int place, struct SSV *i_p, struct SSV *f_p));
+\f
+/*
+ * Name:        initstructs()
+ *
+ * Abstract:    Init the fixed instances of the SSV structures.
+ *
+ * Returns:     void
+ *
+ * Description: This function initializes all the fixed structures Score,
+ *             Staff[s], and Voice[s][v].  This needs to be done before
+ *             scanning through the main linked list of user input
+ *             structures.
+ */
+
+void
+initstructs()
+
+{
+       int s;          /* staff number */
+       int v;          /* voice number */
+       int n;          /* another loop variable */
+       int p;          /* place (PL_*) */
+       int m;          /* mark (MK_*) */
+       int hs;         /* head shape number */
+
+
+       /*
+        * Call zapssv() to release any malloc'ed memory, if any of the
+        * structures have been used before, and mark all fields unused.
+        */
+       zapssv(&Score);
+
+       for (s = 0; s < MAXSTAFFS; s++) {
+               zapssv(&Staff[s]);
+               for (v = 0; v < MAXVOICES; v++)
+                       zapssv(&Voice[s][v]);
+       }
+
+       /*
+        * Fill the Score structure with the proper default values, and
+        * mark all its fields as used.
+        */
+       /* score context */
+       Score.scale_factor = DEFSCALE;
+       Score.units = INCHES;
+       Score.pageheight = DEFPAGEHEIGHT;
+       Score.pagewidth = DEFPAGEWIDTH;
+       Score.panelsperpage = DEFPANELSPERPAGE;
+       Score.topmargin = DEFVMARGIN;
+       Score.botmargin = DEFVMARGIN;
+       Score.leftmargin = DEFHMARGIN;
+       Score.rightmargin = DEFHMARGIN;
+       Score.restcombine = NORESTCOMBINE;
+       Score.firstpage = NOFIRSTPAGE;
+       Score.staffs = DEFSTAFFS;
+       Score.minscsep = DEFMINSCSEP;
+       Score.maxscsep = DEFMAXSCSEP;
+       Score.minscpad = DEFMINSCPAD;
+       Score.maxscpad = DEFMAXSCPAD;
+       Score.nbrace = 0;
+       Score.nbrack = 0;
+       Score.nbarst = 0;
+       Score.timerep = Dflt_timerep;
+       Score.timenum = 4;
+       Score.timeden = 4;
+       Score.time.n = 1;
+       Score.time.d = 1;
+       Score.division = DEFDIVISION;
+       Score.endingstyle = ENDING_TOP;
+       Score.gridsatend = NO;
+       Score.measnum = NO;
+       Score.measnumfamily = BASE_TIMES;
+       Score.measnumfont = FONT_TR;
+       Score.measnumsize = MNUM_SIZE;
+       Score.packfact = DFLTPACKFACT;
+       Score.packexp = DFLTPACKEXP;
+       Score.warn = YES;
+
+       /* score and staff context */
+       Score.staffscale = DEFSTFSCALE;
+       Score.visible = YES;
+       Score.hidesilent = NO;
+       Score.stafflines = 5;
+       Score.strinfo = 0;
+       Score.printclef = SS_NORMAL;
+       Score.gridswhereused = NO;
+       Score.gridscale = DEFGRIDSCALE;
+       Score.gridfret = DEFGRIDFRET;
+       Score.numbermrpt = YES;
+       Score.printmultnum = YES;
+       Score.restsymmult = NO;
+       Score.vscheme = V_1;
+       for (v = 0; v < MAXVOICES; v++) {
+               Score.vcombine[v] = 0;
+       }
+       Score.vcombinequal = VC_NOOVERLAP;
+       Score.sharps = DEFSHARPS;
+       Score.is_minor = NO;
+       Score.cancelkey = NO;
+       Score.inttype = PERFECT;
+       Score.intnum = 1;
+       Score.addinttype = PERFECT;
+       Score.addintnum = 1;
+       Score.clef = TREBLE;
+       Score.rehstyle = RS_BOXED;
+       Score.fontfamily = BASE_TIMES;
+       Score.font = FONT_TR;
+       Score.size = DFLT_SIZE;
+       Score.lyricsfamily = BASE_TIMES;
+       Score.lyricsfont = FONT_TR;
+       Score.lyricssize = DFLT_SIZE;
+       Score.lyricsalign = DEFLYRICSALIGN;
+       Score.sylposition = DEFSYLPOSITION;
+       Score.minstsep = DEFMINSTSEP;
+       Score.staffpad = DEFSTPAD;
+       for (p = 0; p < NUM_PLACE; p++) {
+               for (m = 0; m < NUM_MARK; m++) {
+                       Score.markorder[p][m] = Defmarkorder[m];
+               }
+       }
+       Score.pedstyle = P_LINE;
+       Score.chorddist = DEFCHORDDIST;
+       Score.dist = DEFDIST;
+       Score.dyndist = DEFDYNDIST;
+       Score.label = 0;
+       Score.label2 = 0;
+
+       /* score, staff, and voice context */
+       Score.nbeam = 0;
+       Score.nsubbeam = 0;
+       Score.beamfact = DEFBEAMFACT;
+       Score.beammax = DEFBEAMMAX;
+       Score.pad = DEFPAD;
+       Score.stemlen = DEFSTEMLEN;
+       Score.stemshorten = DEFSTEMSHORTEN;
+       Score.defoct = DEFOCTAVE;
+       Score.timeunit.n = 1;
+       Score.timeunit.d = 4;
+       Score.timelist_p = 0;
+       Score.swingunit = Zero;
+       Score.release = DEFRELEASE;
+       Score.ontheline = YES;
+       Score.tabwhitebox = NO;
+       hs = get_shape_num("norm");
+       for (n = 0; n < 7; n++) {
+               Score.noteheads[n] = hs;
+       }
+
+       for (n = 0; n < NUMFLDS; n++) {
+               Score.used[n] = YES;    /* all items will be set in Score */
+       }
+}
+\f
+/*
+ * Name:        zapssv()
+ *
+ * Abstract:    Init a fixed instance of the SSV structure to empty.
+ *
+ * Returns:     void
+ *
+ * Description: This function initializes a fixed SSV structure to say that
+ *             all fields are unused.
+ */
+
+void
+zapssv(s_p)
+
+struct SSV *s_p;               /* pointer to the structure to be zapped */
+
+{
+       int n;          /* loop variable */
+
+
+       /*
+        * If the structure has been used before, we first have to free any
+        * memory that was malloc'ed.  It's okay to check "used" the first
+        * time, because global variables are set to 0 by the compiler;
+        * thus, used[X] == NO.
+        */
+       if (s_p->used[BRACELIST] == YES) {
+               freestaffset(s_p->nbrace, s_p->bracelist);
+               s_p->bracelist = 0;
+       }
+       if (s_p->used[BRACKLIST] == YES) {
+               freestaffset(s_p->nbrack, s_p->bracklist);
+               s_p->bracklist = 0;
+       }
+       if (s_p->used[BARSTLIST] == YES && s_p->barstlist != 0) {
+               FREE(s_p->barstlist);
+               s_p->barstlist = 0;
+       }
+       if (s_p->used[LABEL] == YES && s_p->label != 0) {
+               FREE(s_p->label);
+               s_p->label = 0;
+       }
+       if (s_p->used[LABEL2] == YES && s_p->label2 != 0) {
+               FREE(s_p->label2);
+               s_p->label2 = 0;
+       }
+       if (s_p->used[BEAMSTLIST] == YES && s_p->beamstlist != 0) {
+               FREE(s_p->beamstlist);
+               s_p->beamstlist = 0;
+       }
+
+       /*
+        * Mark all fields unused.
+        */
+       for (n = 0; n < NUMFLDS; n++) {
+               s_p->used[n] = NO;
+       }
+}
+\f
+/*
+ * Name:        freestaffset()
+ *
+ * Abstract:    Free all malloc'ed memory associated with a staffset list.
+ *
+ * Returns:     void
+ *
+ * Description: This function frees staffset structures, along with their
+ *             labels if they exist.
+ */
+
+static void
+freestaffset(num, ss_p)
+
+int num;                       /* how many staffsets are in list? */
+struct STAFFSET *ss_p;         /* pointer to first staffset */
+
+{
+       int n;                  /* loop variable */
+
+
+       if (ss_p == 0)                  /* if there is no list, just return */
+               return;
+
+       /* for each staffset in the list, free labels if present */
+       for (n = 0; n < num; n++) {
+               if (ss_p[n].label != 0) {               /* if label, free it */
+                       FREE(ss_p[n].label);
+                       ss_p[n].label = 0;
+               }
+               if (ss_p[n].label2 != 0) {      /* if label2, free it */
+                       FREE(ss_p[n].label2);
+                       ss_p[n].label2 = 0;
+               }
+       }
+
+       FREE(ss_p);                     /* free the staffset list itself */
+}
+\f
+/*
+ * Name:        asgnstaffset()
+ *
+ * Abstract:    Set up a staffset list in another SSV structure.
+ *
+ * Returns:     void
+ *
+ * Description: This function sets up a new staffset list identical to
+ *             another one, including the labels if they exist.
+ */
+
+static void
+asgnstaffset(num, new_p_p, old_p)
+
+int num;                       /* how many staffsets are in list? */
+struct STAFFSET **new_p_p;     /* ptr to ptr to first staffset of new list */
+struct STAFFSET *old_p;                /* pointer to first staffset of old list */
+
+{
+       int n;          /* loop variable */
+
+
+       if (num == 0)           /* if there's no old list, nothing to do */
+               return;
+
+       /*
+        * Allocate the new list and point at it.
+        */
+       MALLOC(STAFFSET, *new_p_p, num);
+
+       /*
+        * Loop through the old list.  Wherever there is a label, allocate
+        * one for the new list.  When there isn't, set that pointer to null.
+        */
+       for (n = 0; n < num; n++) {
+               (*new_p_p)[n].topstaff = old_p[n].topstaff;
+               (*new_p_p)[n].botstaff = old_p[n].botstaff;
+
+               if (old_p[n].label != 0) {
+                       MALLOCA(char, (*new_p_p)[n].label,
+                                       strlen(old_p[n].label) + 1);
+                       (void)strcpy((*new_p_p)[n].label, old_p[n].label);
+               } else {
+                       (*new_p_p)[n].label = 0;
+               }
+
+               if (old_p[n].label2 != 0) {
+                       MALLOCA(char, (*new_p_p)[n].label2,
+                                       strlen(old_p[n].label2) + 1);
+                       (void)strcpy((*new_p_p)[n].label2, old_p[n].label2);
+               } else {
+                       (*new_p_p)[n].label2 = 0;
+               }
+       }
+}
+\f
+/*
+ * Name:        svpath()
+ *
+ * Abstract:    Find a field for a staff, using the viewpath.
+ *
+ * Returns:     pointer to structure containing correct field value
+ *
+ * Description: This function, given a staff number and a field number, looks
+ *             down the viewpath to find the first structure where the field
+ *             is set.  It returns a pointer to that structure.  (However, see
+ *             below for a special kluge for the "visible" field.)
+ *             Note:  0 is allowed for the staff number, and that means use
+ *             the Score value.
+ */
+
+struct SSV *
+svpath(s, field)
+
+int s;         /* staff number, 1 to MAXSTAFFS; or 0, meaning score */
+int field;     /* the defined symbol for the field desired */
+
+{
+       static struct SSV phony;        /* phony SSV; see below */
+
+
+       if (s == 0)
+               return (&Score);
+
+       /*
+        * For the "visible" field, special kluges are needed, for two reasons.
+        * One is that there is a command line option (-s) that overrides the
+        * visibility requests in the Mup input file.  The other is that, even
+        * though it is a score/staff/voice parameter, it is easier to manage
+        * visibility at the staff level than at the voice level.  Otherwise,
+        * to know if a staff should be drawn, we would have to check vscheme
+        * and the visibility state of each voice.
+        *
+        * The design is as follows:  in mkchords.c, if a voice is to be
+        * invisible, a measure space is put in place of its original GRPSYL
+        * list.  Users can use svpath() to check if a staff is visible
+        * instead of having to call vvpath() for its voice(s).  The field
+        * staff_p->visible is set by calling here, so that will also be
+        * consistent.
+        *
+        * If the -s option was used on the command line, only staffs/voices
+        * listed there are ever allowed to be visible.  So although SSVs are
+        * set as for other score/staff/voice parameters, the results from
+        * svpath() also consider what the -s option said.  In vvpath() this
+        * is also done.
+        */
+       if (field == VISIBLE) {
+               int num_voices;         /* how many voices on this staff */
+
+               /*
+                * In case we are going to return the phony SSV, load the
+                * hidesilent field into it.  Both visible and hidesilent are
+                * controlled by VISIBLE, but hidesilent is not to be affected
+                * by the kluges needed for "visible".
+                */
+               /* if the field is set in the staff structure, use that */
+               if (Staff[s-1].used[VISIBLE] == YES) {
+                       phony.hidesilent = Staff[s-1].hidesilent;
+               } else {
+                       /* use the score structure; it's always set there */
+                       phony.hidesilent = Score.hidesilent;
+               }
+
+               /* always return NO if the command line says staff invisible */
+               if (Staff_vis[s] == NO) {
+                       /* return phony SSV with NO, ignore real SSV */
+                       phony.visible = NO;
+                       return (&phony);
+               }
+
+               num_voices = vscheme_voices(svpath(s, VSCHEME)->vscheme);
+
+               /*
+                * If a voice that the command line is allowing to be visible
+                * was requested via an SSV to be visible, we must let the
+                * staff be visible.
+                */
+               if ((Voice_vis[s][1] == YES &&
+                    Voice[s-1][0].used[VISIBLE] == YES &&
+                    Voice[s-1][0].visible == YES) ||
+
+                   (num_voices >= 2 &&
+                    Voice_vis[s][2] == YES &&
+                    Voice[s-1][1].used[VISIBLE] == YES &&
+                    Voice[s-1][1].visible == YES) ||
+
+                   (num_voices >= 3 &&
+                    Voice_vis[s][3] == YES &&
+                    Voice[s-1][2].used[VISIBLE] == YES &&
+                    Voice[s-1][2].visible == YES)) {
+
+                       /* return phony SSV with YES, ignore real SSV */
+                       phony.visible = YES;
+                       return (&phony);
+               }
+
+               /*
+                * If, for each voice that exists, either the command line is
+                * forcing it to be invisible or else it was requested via an
+                * SSV to be invisible, then the staff must be invisible.
+                */
+               if ((Voice_vis[s][1] == NO ||
+                    Voice[s-1][0].used[VISIBLE] == YES &&
+                    Voice[s-1][0].visible == NO) &&
+
+                   (num_voices < 2 ||
+                    (Voice_vis[s][2] == NO ||
+                     Voice[s-1][1].used[VISIBLE] == YES &&
+                     Voice[s-1][1].visible == NO)) &&
+
+                   (num_voices < 3 ||
+                    (Voice_vis[s][3] == NO ||
+                     Voice[s-1][2].used[VISIBLE] == YES &&
+                     Voice[s-1][2].visible == NO))) {
+
+                       /* return phony SSV with NO, ignore real SSV */
+                       phony.visible = NO;
+                       return (&phony);
+               }
+
+               /*
+                * The command line and the voice(s) aren't forcing the issue.
+                * So fall through to determine the staff's visibility the
+                * normal way.
+                */
+       }
+
+       /* if the field is set in the staff structure, use that */
+       if (Staff[s-1].used[field] == YES)
+               return (&Staff[s-1]);
+
+       /* else use the score structure; it's always set there */
+       return (&Score);
+}
+\f
+/*
+ * Name:        vvpath()
+ *
+ * Abstract:    Find a field for a voice, using the viewpath.
+ *
+ * Returns:     pointer to structure containing correct field value
+ *
+ * Description: This function, given a staff number, voice number on that
+ *             staff, and a field number, looks down the viewpath to find
+ *             the first structure where the field is set.  It returns a
+ *             pointer to that structure.  (However, see below for a special
+ *             kluge for the "visible" field.)
+ *             Note:  0 is allowed for the voice number, and that means use
+ *             the staff's value.  If staff is 0, the Score is used,
+ *             regardless of the voice number.
+ */
+
+struct SSV *
+vvpath(s, v, field)
+
+int s;         /* staff number, 1 to MAXSTAFFS; or 0, meaning score */
+int v;         /* voice number, 1 to MAXVOICES; or 0, meaning staff */
+int field;     /* the defined symbol for the field desired */
+
+{
+       static struct SSV phony;        /* phony SSV; see below */
+
+
+       if (s == 0 || v == 0)
+               return (svpath(s, field));
+
+       /*
+        * See the comment in svpath() regarding the "visible" field.  There's
+        * probably no need to call vvpath() for "visible" after mkchords.c has
+        * run, since voices that are to be invisible are changed to measure
+        * spaces there.  But in mkchords.c itself, and earlier, there is
+        * sometimes a need.
+        *
+        * For the "visible" field, first check the command line to see if this
+        * voice or its staff must always be invisible.  If so, return a phony
+        * SSV that says that.  Otherwise fall through to handle the normal way.
+        */
+       if (field == VISIBLE && (Staff_vis[s] == NO || Voice_vis[s][v] == NO)) {
+               /*
+                * Since we are going to force visible to NO, it's irrelevant
+                * what hidesilent is, so don't bother setting it, unlike what
+                * we had to do in svpath().
+                */
+               phony.visible = NO;
+               return (&phony);
+       }
+
+       /* if the field is set in the voice structure, use that */
+       if (Voice[s-1][v-1].used[field] == YES)
+               return (&Voice[s-1][v-1]);
+
+       /* else if the field is set in the staff structure, use that */
+       if (Staff[s-1].used[field] == YES)
+               return (&Staff[s-1]);
+
+       /* else use the score structure; it's always set there */
+       return (&Score);
+}
+\f
+/*
+ * Name:        asgnssv()
+ *
+ * Abstract:    Assign fields from an input SSV to a fixed one.
+ *
+ * Returns:     void
+ *
+ * Description: This function is passed an input SSV structure (from an input
+ *             context).  For each field where "used" is YES in the input SSV,
+ *             it copies it to the appropriate fixed SSV and sets its "used"
+ *             flag to YES.  For each field where "used" is UNSET in the input
+ *             SSV, it sets "used" to NO in the appropriate fixed SSV.
+ *             In some cases, there are side effects, where it also
+ *             alters a lower level structure.  E.g., changing the number of
+ *             voices of a staff inits its voice structures.  In the case
+ *             of stafflines, setting a staff to be a tablature staff
+ *             automatically automatically forces some other fields to be set
+ *             not only in the given staff, but also in the preceding tabnote
+ *             staff.  Note also that the Score "used" flags are already
+ *             always set and don't need to be set here.  And Score fields can
+ *             never be unset.
+ */
+
+void
+asgnssv(i_p)
+
+struct SSV *i_p;       /* input SSV structure to be copied from */
+
+{
+       struct SSV *f_p;        /* ptr to fixed SSV structure to copy into */
+       int s, v;               /* used for looping through staffs & voices */
+       int start, stop;        /* loop limits */
+       int n;                  /* another loop variable */
+
+
+       f_p = 0;        /* to prevent "uninitialized variable" warnings */
+
+       /*
+        * Using the selector fields in the input structure, set a pointer to
+        * the fixed structure that is to be populated.
+        */
+       switch (i_p->context) {
+       case C_SCORE:
+               f_p = &Score;
+               break;
+       case C_STAFF:
+               /* silently ignore bogus staff no.; it is caught elsewhere */
+               if (i_p->staffno < 1 || i_p->staffno > MAXSTAFFS) {
+                       return;
+               }
+               f_p = &Staff[ i_p->staffno - 1 ];
+               break;
+       case C_VOICE:
+               /* silently ignore bogus staff/voice; it is caught elsewhere */
+               if (i_p->staffno < 1 || i_p->staffno > MAXSTAFFS ||
+                   i_p->voiceno < 1 || i_p->voiceno > MAXVOICES) {
+                       return;
+               }
+               f_p = &Voice[ i_p->staffno - 1 ][ i_p->voiceno - 1 ];
+               break;
+       }
+
+       /*
+        * ========== ITEMS FOR SCORE CONTEXT ONLY ===========
+        * There's no need to set f_p->used[] = YES here; since this is the
+        * score, those bits are already always YES.
+        */
+       if (i_p->used[SCALE_FACTOR] == YES) {
+               f_p->scale_factor = i_p->scale_factor;
+       }
+
+       if (i_p->used[UNITS] == YES) {
+               f_p->units = i_p->units;
+       }
+
+       /*
+        * PAGEHEIGHT, PAGEHEIGHT, and PANELSPERPAGE interact, because when the
+        * user sets PAGE*, they are referring to the paper, but internally we
+        * want page* to refer to one "panel" of music, which is a 90 degree
+        * rotated half of the sheet of paper when panelsperpage is 2.
+        */
+       if (i_p->used[PAGEHEIGHT] == YES) {
+               if (f_p->panelsperpage == 1) {
+                       f_p->pageheight = i_p->pageheight;
+               } else {
+                       f_p->pagewidth = i_p->pageheight / 2.0;
+               }
+       }
+
+       if (i_p->used[PAGEWIDTH] == YES) {
+               if (f_p->panelsperpage == 1) {
+                       f_p->pagewidth = i_p->pagewidth;
+               } else {
+                       f_p->pageheight = i_p->pagewidth;
+               }
+       }
+
+       if (i_p->used[PANELSPERPAGE] == YES) {
+               /* depending on how this is changing, flip height and width */
+               float oldwidth;
+               if (f_p->panelsperpage == 1 && i_p->panelsperpage == 2) {
+                       oldwidth = f_p->pagewidth;
+                       f_p->pagewidth = f_p->pageheight / 2.0;
+                       f_p->pageheight = oldwidth;
+               } else if (f_p->panelsperpage == 2 && i_p->panelsperpage == 1) {
+                       oldwidth = f_p->pagewidth;
+                       f_p->pagewidth = f_p->pageheight;
+                       f_p->pageheight = oldwidth * 2.0;
+               }
+               f_p->panelsperpage = i_p->panelsperpage;
+       }
+
+       if (i_p->used[TOPMARGIN] == YES) {
+               f_p->topmargin = i_p->topmargin;
+       }
+
+       if (i_p->used[BOTMARGIN] == YES) {
+               f_p->botmargin = i_p->botmargin;
+       }
+
+       if (i_p->used[LEFTMARGIN] == YES) {
+               f_p->leftmargin = i_p->leftmargin;
+       }
+
+       if (i_p->used[RIGHTMARGIN] == YES) {
+               f_p->rightmargin = i_p->rightmargin;
+       }
+
+       if (i_p->used[RESTCOMBINE] == YES) {
+               f_p->restcombine = i_p->restcombine;
+       }
+
+       if (i_p->used[FIRSTPAGE] == YES) {
+               f_p->firstpage = i_p->firstpage;
+       }
+
+       if (i_p->used[NUMSTAFF] == YES) {
+               f_p->staffs = i_p->staffs;
+
+               /* this destroys all staff and voice info */
+               for (s = 0; s < MAXSTAFFS; s++) {
+                       zapssv(&Staff[s]);
+                       for (v = 0; v < MAXVOICES; v++)
+                               zapssv(&Voice[s][v]);
+               }
+       }
+
+       if (i_p->used[MINSCSEP] == YES) {
+               f_p->minscsep = i_p->minscsep;
+       }
+
+       if (i_p->used[MAXSCSEP] == YES) {
+               f_p->maxscsep = i_p->maxscsep;
+       }
+
+       if (i_p->used[MINSCPAD] == YES) {
+               f_p->minscpad = i_p->minscpad;
+       }
+
+       if (i_p->used[MAXSCPAD] == YES) {
+               f_p->maxscpad = i_p->maxscpad;
+       }
+
+       if (i_p->used[BRACELIST] == YES) {
+               /* if it was already used, free old list if present */
+               if (f_p->used[BRACELIST] == YES) {
+                       freestaffset(f_p->nbrace, f_p->bracelist);
+                       f_p->bracelist = 0;
+               }
+
+               /* set up new list */
+               f_p->nbrace = i_p->nbrace;
+               asgnstaffset(f_p->nbrace, &f_p->bracelist, i_p->bracelist);
+       }
+
+       if (i_p->used[BRACKLIST] == YES) {
+               /* if it was already used, free old list if present */
+               if (f_p->used[BRACKLIST] == YES) {
+                       freestaffset(f_p->nbrack, f_p->bracklist);
+                       f_p->bracklist = 0;
+               }
+
+               /* set up new list */
+               f_p->nbrack = i_p->nbrack;
+               asgnstaffset(f_p->nbrack, &f_p->bracklist, i_p->bracklist);
+       }
+
+       if (i_p->used[BARSTLIST] == YES) {
+               /* if it was already used, free old list if present */
+               if (f_p->used[BARSTLIST] == YES && f_p->nbarst != 0)
+                       FREE(f_p->barstlist);
+
+               /* set up new list */
+               f_p->nbarst = i_p->nbarst;
+               /* the +1 is to guard against allocating 0 */
+               MALLOC(TOP_BOT, f_p->barstlist, f_p->nbarst + 1);
+               for (n = 0; n < f_p->nbarst; n++) {
+                       f_p->barstlist[n] = i_p->barstlist[n];
+               }
+       }
+
+       if (i_p->used[TIME] == YES) {
+               f_p->timenum  = i_p->timenum;
+               f_p->timeden  = i_p->timeden;
+               f_p->timevis  = i_p->timevis;
+               f_p->timerep  = i_p->timerep;
+               f_p->time     = i_p->time;
+
+               /*
+                * Changing the time sig forces a change in default time unit.
+                * Set it to one "beat" for the score, and unset it for all
+                * staffs and voices.
+                */
+               f_p->timeunit.n = 1;
+               f_p->timeunit.d = f_p->timeden;
+               f_p->timelist_p = 0;
+               for (s = 0; s < MAXSTAFFS; s++) {
+                       Staff[s].used[TIMEUNIT] = NO;
+                       for (v = 0; v < MAXVOICES; v++)
+                               Voice[s][v].used[TIMEUNIT] = NO;
+               }
+
+               /*
+                * Changing the time also destroys all beamstyle lists.
+                * However, the special empty beamstyle list that was set up
+                * for a tablature staff must be retained, so that it will
+                * continue to override any score beamstyle list that may be
+                * set up later on.
+                */
+               if (Score.used[BEAMSTLIST] == YES) {
+                       if (Score.nbeam != 0) {
+                               FREE(Score.beamstlist);
+                               Score.beamstlist = 0;
+                               Score.nbeam = 0;
+                               FREE(Score.subbeamstlist);
+                               Score.subbeamstlist = 0;
+                               Score.nsubbeam = 0;
+                       }
+               }
+               for (s = 0; s < MAXSTAFFS; s++) {
+                       if (Staff[s].used[BEAMSTLIST] == YES &&
+                           Staff[s].strinfo == 0) {    /* not tablature */
+                               if (Staff[s].nbeam != 0) {
+                                       FREE(Staff[s].beamstlist);
+                                       Staff[s].beamstlist = 0;
+                                       Staff[s].nbeam = 0;
+                                       FREE(Staff[s].subbeamstlist);
+                                       Staff[s].subbeamstlist = 0;
+                                       Staff[s].nsubbeam = 0;
+                               }
+                               Staff[s].used[BEAMSTLIST] = NO;
+                       }
+                       for (v = 0; v < MAXVOICES; v++) {
+                               if (Voice[s][v].used[BEAMSTLIST] == YES) {
+                                       if (Voice[s][v].nbeam != 0) {
+                                               FREE(Voice[s][v].beamstlist);
+                                               Voice[s][v].beamstlist = 0;
+                                               Voice[s][v].nbeam = 0;
+                                               FREE(Voice[s][v].subbeamstlist);
+                                               Voice[s][v].subbeamstlist = 0;
+                                               Voice[s][v].nsubbeam = 0;
+                                       }
+                                       Voice[s][v].used[BEAMSTLIST] = NO;
+                               }
+                       }
+               }
+       }
+
+       if (i_p->used[DIVISION] == YES) {
+               f_p->division = i_p->division;
+       }
+
+       if (i_p->used[ENDINGSTYLE] == YES) {
+               f_p->endingstyle = i_p->endingstyle;
+       }
+
+       if (i_p->used[GRIDSATEND] == YES) {
+               f_p->gridsatend = i_p->gridsatend;
+       }
+
+       if (i_p->used[MEASNUM] == YES) {
+               f_p->measnum = i_p->measnum;
+       }
+
+       if (i_p->used[MEASNUMFAMILY] == YES) {
+               f_p->measnumfamily = i_p->measnumfamily;
+       }
+
+       if (i_p->used[MEASNUMFONT] == YES) {
+               f_p->measnumfont = i_p->measnumfont;
+       }
+
+       if (i_p->used[MEASNUMSIZE] == YES) {
+               f_p->measnumsize = i_p->measnumsize;
+       }
+
+       if (i_p->used[PACKFACT] == YES) {
+               f_p->packfact = i_p->packfact;
+       }
+
+       if (i_p->used[PACKEXP] == YES) {
+               f_p->packexp = i_p->packexp;
+       }
+
+       if (i_p->used[WARN] == YES) {
+               f_p->warn = i_p->warn;
+       }
+
+       /*
+        * ========== ITEMS FOR SCORE AND STAFF CONTEXT ===========
+        */
+       /*
+        * Most parameters involve just a single field, and have no side
+        * effects.  For this, we can use the following switch statement to
+        * do the work, for parameters that can exist on staff or voice.
+        * (Score-only ones don't need it, since that can't be unset.)
+        */
+#define        SETPARM(name, NAME)                     \
+       switch (i_p->used[NAME]) {              \
+       case YES:                               \
+               f_p->name = i_p->name;          \
+               f_p->used[NAME] = YES;          \
+               break;                          \
+       case UNSET:                             \
+               f_p->used[NAME] = NO;           \
+               break;                          \
+       /* default is NO; do nothing */         \
+       }
+
+       SETPARM(staffscale, STAFFSCALE)
+
+       switch (i_p->used[STAFFLINES]) {
+       case YES: {
+               struct SSV *tabnote_p;  /* ptr to tabnote fixed SSV */
+               f_p->stafflines = i_p->stafflines;
+
+               if (i_p->strinfo != 0) {        /* tablature */
+                       struct SSV *voice_p;    /* ptr to a voice's fixed SSV*/
+
+                       /*
+                        * This is a tablature staff.  Set printclef to normal
+                        * (even though tab isn't particularly "normal").
+                        * Point f_p->strinfo at the same array that
+                        * i_p->strinfo points at.
+                        */
+                       f_p->printclef = SS_NORMAL;
+                       f_p->strinfo = i_p->strinfo;
+
+                       /*
+                        * Force some other score/staff items to fixed values
+                        * for tab.  The parser blocks the user from setting
+                        * these.  We need to set them here in the staff SSV to
+                        * override whatever may be in the score SSV.  This
+                        * will make it possible to avoid special checks for
+                        * tablature in many places; the right values will be
+                        * set for this staff.  Also force score/staff/voice
+                        * items here.
+                        */
+                       f_p->sharps = 0;
+                       f_p->is_minor = NO;
+                       f_p->used[SHARPS] = YES;
+
+                       f_p->inttype = PERFECT;
+                       f_p->intnum = 1;
+                       f_p->used[TRANSPOSITION] = YES;
+
+                       f_p->addinttype = PERFECT;
+                       f_p->addintnum = 1;
+                       f_p->used[ADDTRANSPOSITION] = YES;
+
+                       f_p->clef = TABCLEF;
+                       f_p->used[CLEF] = YES;
+
+                       if (f_p->used[BEAMSTLIST] == YES && f_p->nbeam != 0) {
+                               /* if already used, free old list if present */
+                               FREE(f_p->beamstlist);
+                               FREE(f_p->subbeamstlist);
+                       }
+                       f_p->nbeam = 0;
+                       f_p->beamstlist = 0;
+                       f_p->nsubbeam = 0;
+                       f_p->subbeamstlist = 0;
+                       f_p->used[BEAMSTLIST] = YES;
+
+                       f_p->defoct = 4;
+                       f_p->used[DEFOCT] = YES;
+
+                       /* blow away the following in tab staff's voices */
+                       for (v = 0; v < MAXVOICES; v++) {
+                               voice_p = &Voice[ i_p->staffno - 1][ v ];
+
+                               if (voice_p->used[BEAMSTLIST] == YES) {
+                                       if (voice_p->nbeam != 0) {
+                                               FREE(f_p->beamstlist);
+                                               FREE(f_p->subbeamstlist);
+                                       }
+                                       voice_p->used[BEAMSTLIST] = NO;
+                               }
+                               voice_p->used[DEFOCT] = NO;
+                       }
+
+                       /*
+                        * Force fields on the tabnote staff above this tab
+                        * staff.
+                        */
+                       tabnote_p = &Staff[ i_p->staffno - 2 ];
+
+                       /*
+                        * The parse phase wouldn't let this be another tab
+                        * staff, so we don't need to check for that.  But it
+                        * might be a 1-line staff.  If so, override it to 5
+                        * line.  If this parameter wasn't set, force printclef
+                        * to normal, but if it was, keep the old value.  (We
+                        * might as well allow 5n and 5 drum as well as 5,
+                        * though that would be a weird usage.)
+                        */
+                       tabnote_p->stafflines = 5;
+                       if (tabnote_p->used[STAFFLINES] == NO)
+                               tabnote_p->printclef = SS_NORMAL;
+                       tabnote_p->used[STAFFLINES] = YES;
+
+               } else {        /* not tablature */
+                       /*
+                        * If this staff used to be tablature, we need to unset
+                        * some "used" fields that were forced.
+                        */
+                       if (f_p->strinfo != 0) {
+                               f_p->used[SHARPS] = NO;
+                               f_p->used[TRANSPOSITION] = NO;
+                               f_p->used[ADDTRANSPOSITION] = NO;
+                               f_p->used[CLEF] = NO;
+                               f_p->used[BEAMSTLIST] = NO;
+                               f_p->used[DEFOCT] = NO;
+                               tabnote_p = &Staff[ i_p->staffno - 2 ];
+
+                               /* make it non-tablature */
+                               f_p->strinfo = 0;
+                       }
+
+                       f_p->printclef = i_p->printclef;
+               }
+               f_p->used[STAFFLINES] = YES;
+               } break;
+       case UNSET:
+               f_p->used[STAFFLINES] = NO;
+               break;
+       }
+
+       SETPARM(gridswhereused, GRIDSWHEREUSED)
+
+       SETPARM(gridscale, GRIDSCALE)
+
+       SETPARM(gridfret, GRIDFRET)
+
+       SETPARM(numbermrpt, NUMBERMRPT)
+
+       SETPARM(printmultnum, PRINTMULTNUM)
+
+       SETPARM(restsymmult, RESTSYMMULT)
+
+       switch (i_p->used[VSCHEME]) {
+       case YES:
+               /*
+                * If the vscheme change changes the *number* of voices, we
+                * have to wipe out the voice information, but otherwise not.
+                */
+               if (i_p->context == C_SCORE) {
+                       start = 0;              /* if score, do test for */
+                       stop = MAXSTAFFS - 1;   /*  all staffs */
+               } else {        /* C_STAFF */
+                       start = stop = i_p->staffno - 1; /* do just this one */
+               }
+
+               /* for each staff affected by this change . . . */
+               for (n = start; n <= stop; n++) {
+                       int oldvoices, newvoices; /* how many before & after */
+
+                       oldvoices = vscheme_voices(svpath(n + 1,
+                                       VSCHEME)->vscheme);
+                       newvoices = vscheme_voices(i_p->vscheme);
+
+                       if (oldvoices != newvoices) {
+
+                               for (v = 0; v < MAXVOICES; v++)
+                                       zapssv(&Voice[n][v]);
+                       }
+               }
+
+               f_p->vscheme = i_p->vscheme;
+               f_p->used[VSCHEME] = YES;
+               break;
+       case UNSET:
+               f_p->used[VSCHEME] = NO;
+               break;
+       }
+
+       switch (i_p->used[VCOMBINE]) {
+       case YES:
+               for (v = 0; v < MAXVOICES; v++) {
+                       f_p->vcombine[v] = i_p->vcombine[v];
+               }
+               f_p->vcombinequal = i_p->vcombinequal;
+               f_p->used[VCOMBINE] = YES;
+               break;
+       case UNSET:
+               f_p->used[VCOMBINE] = NO;
+               break;
+       }
+
+       switch (i_p->used[SHARPS]) {
+       case YES:
+               f_p->sharps = i_p->sharps;
+               f_p->is_minor = i_p->is_minor;
+               f_p->used[SHARPS] = YES;
+               break;
+       case UNSET:
+               f_p->used[SHARPS] = NO;
+               break;
+       }
+
+       SETPARM(cancelkey, CANCELKEY)
+
+       switch (i_p->used[TRANSPOSITION]) {
+       case YES:
+               f_p->inttype = i_p->inttype;
+               f_p->intnum = i_p->intnum;
+               f_p->used[TRANSPOSITION] = YES;
+               break;
+       case UNSET:
+               f_p->used[TRANSPOSITION] = NO;
+               break;
+       }
+
+       switch (i_p->used[ADDTRANSPOSITION]) {
+       case YES:
+               f_p->addinttype = i_p->addinttype;
+               f_p->addintnum = i_p->addintnum;
+               f_p->used[ADDTRANSPOSITION] = YES;
+               break;
+       case UNSET:
+               f_p->used[ADDTRANSPOSITION] = NO;
+               break;
+       }
+
+       switch (i_p->used[CLEF]) {
+       case YES:
+               f_p->clef = i_p->clef;
+               f_p->used[CLEF] = YES;
+
+               /*
+                * Reset the default octave so that the middle line of the
+                * staff lies within it.  If the user also set octave in
+                * this context, this will get changed again later in this
+                * function.
+                */
+               if (f_p->clef > ALTO)           /* lower than alto */
+                       f_p->defoct = 3;
+               else if (f_p->clef < TREBLE)    /* higher than treble */
+                       f_p->defoct = 5;
+               else
+                       f_p->defoct = 4;
+               f_p->used[DEFOCT] = YES;
+               break;
+       case UNSET:
+               f_p->used[DEFOCT] = NO;
+               break;
+       }
+
+       SETPARM(rehstyle, REHSTYLE)
+
+       SETPARM(fontfamily, FONTFAMILY)
+
+       SETPARM(font, FONT)
+
+       SETPARM(size, SIZE)
+
+       SETPARM(lyricsfamily, LYRICSFAMILY)
+
+       SETPARM(lyricsfont, LYRICSFONT)
+
+       SETPARM(lyricssize, LYRICSSIZE)
+
+       SETPARM(lyricsalign, LYRICSALIGN)
+
+       SETPARM(sylposition, SYLPOSITION)
+
+       SETPARM(minstsep, MINSTSEP)
+
+       SETPARM(staffpad, STAFFPAD)
+
+       switch (i_p->used[ABOVEORDER]) {
+       case YES:
+               setorder(PL_ABOVE, i_p, f_p);
+               f_p->used[ABOVEORDER] = YES;
+               break;
+       case UNSET:
+               f_p->used[ABOVEORDER] = NO;
+               break;
+       }
+
+       switch (i_p->used[BELOWORDER]) {
+       case YES:
+               setorder(PL_BELOW, i_p, f_p);
+               f_p->used[BELOWORDER] = YES;
+               break;
+       case UNSET:
+               f_p->used[BELOWORDER] = NO;
+               break;
+       }
+
+       switch (i_p->used[BETWEENORDER]) {
+       case YES:
+               setorder(PL_BETWEEN, i_p, f_p);
+               f_p->used[BETWEENORDER] = YES;
+               break;
+       case UNSET:
+               f_p->used[BETWEENORDER] = NO;
+               break;
+       }
+
+       SETPARM(pedstyle, PEDSTYLE)
+
+       SETPARM(chorddist, CHORDDIST)
+
+       SETPARM(dist, DIST)
+
+       SETPARM(dyndist, DYNDIST)
+
+       switch (i_p->used[LABEL]) {
+       case YES:
+               /* if it was already used, free old label */
+               if (f_p->used[LABEL] == YES && f_p->label != 0) {
+                       FREE(f_p->label);
+                       f_p->label = 0;
+               }
+
+               /* set up new label */
+               MALLOCA(char, f_p->label, strlen(i_p->label) + 1);      
+               (void)strcpy(f_p->label, i_p->label);
+
+               f_p->used[LABEL] = YES;
+               break;
+       case UNSET:
+               /* if it was already used, free old label */
+               if (f_p->used[LABEL] == YES && f_p->label != 0) {
+                       FREE(f_p->label);
+                       f_p->label = 0;
+               }
+               f_p->used[LABEL] = NO;
+               break;
+       }
+
+       switch (i_p->used[LABEL2]) {
+       case YES:
+               /* if it was already used, free old label2 */
+               if (f_p->used[LABEL2] == YES && f_p->label2 != 0) {
+                       FREE(f_p->label2);
+                       f_p->label2 = 0;
+               }
+
+               /* set up new label */
+               MALLOCA(char, f_p->label2, strlen(i_p->label2) + 1);
+               (void)strcpy(f_p->label2, i_p->label2);
+
+               f_p->used[LABEL2] = YES;
+               break;
+       case UNSET:
+               /* if it was already used, free old label2 */
+               if (f_p->used[LABEL2] == YES && f_p->label2 != 0) {
+                       FREE(f_p->label2);
+                       f_p->label2 = 0;
+               }
+               f_p->used[LABEL2] = NO;
+               break;
+       }
+
+       /*
+        * ========== ITEMS FOR SCORE, STAFF, AND VOICE CONTEXT ===========
+        */
+       switch (i_p->used[VISIBLE]) {
+       case YES:
+               f_p->visible = i_p->visible;
+               f_p->hidesilent = i_p->hidesilent;
+               f_p->used[VISIBLE] = YES;
+               break;
+       case UNSET:
+               f_p->used[VISIBLE] = NO;
+       }
+
+       switch (i_p->used[BEAMSTLIST]) {
+       case YES:
+               /* if it was already used, free old list if present */
+               if (f_p->used[BEAMSTLIST] == YES && f_p->nbeam != 0) {
+                       FREE(f_p->beamstlist);
+                       FREE(f_p->subbeamstlist);
+                       f_p->beamstlist = 0;
+                       f_p->subbeamstlist = 0;
+               }
+
+               /* set up new list */
+               f_p->nbeam = i_p->nbeam;
+               f_p->beamrests = i_p->beamrests;
+               f_p->beamspaces = i_p->beamspaces;
+               f_p->nsubbeam = i_p->nsubbeam;
+               /* the +1 is to guard against allocating 0 */
+               MALLOCA(RATIONAL, f_p->beamstlist, f_p->nbeam + 1);
+               MALLOCA(RATIONAL, f_p->subbeamstlist, f_p->nsubbeam + 1);
+               for (n = 0; n < f_p->nbeam; n++) {
+                       f_p->beamstlist[n] = i_p->beamstlist[n];
+               }
+               for (n = 0; n < f_p->nsubbeam; n++) {
+                       f_p->subbeamstlist[n] = i_p->subbeamstlist[n];
+               }
+
+               f_p->used[BEAMSTLIST] = YES;
+               break;
+       case UNSET:
+               /* if it was already used, free old list if present */
+               if (f_p->used[BEAMSTLIST] == YES && f_p->nbeam != 0) {
+                       FREE(f_p->beamstlist);
+                       FREE(f_p->subbeamstlist);
+                       f_p->beamstlist = 0;
+                       f_p->subbeamstlist = 0;
+               }
+               f_p->used[BEAMSTLIST] = NO;
+               break;
+       }
+
+       switch (i_p->used[BEAMSLOPE]) {
+       case YES:
+               f_p->beamfact = i_p->beamfact;
+               f_p->beammax = i_p->beammax;
+               f_p->used[BEAMSLOPE] = YES;
+               break;
+       case UNSET:
+               f_p->used[BEAMSLOPE] = NO;
+               break;
+       }
+
+       SETPARM(pad, PAD)
+
+       SETPARM(stemlen, STEMLEN)
+
+       SETPARM(stemshorten, STEMSHORTEN)
+
+       SETPARM(defoct, DEFOCT)
+
+       switch (i_p->used[TIMEUNIT]) {
+       case YES:
+               f_p->timeunit = i_p->timeunit;
+               f_p->timelist_p = i_p->timelist_p;
+               f_p->used[TIMEUNIT] = YES;
+               break;
+       case UNSET:
+               f_p->used[TIMEUNIT] = NO;
+       }
+
+       SETPARM(swingunit, SWINGUNIT)
+
+       SETPARM(release, RELEASE)
+
+       SETPARM(ontheline, ONTHELINE)
+
+       SETPARM(tabwhitebox, TABWHITEBOX)
+
+       switch (i_p->used[NOTEHEADS]) {
+       case YES:
+               for (n = 0; n < 7; n++) {
+                       f_p->noteheads[n] = i_p->noteheads[n];
+               }
+               f_p->used[NOTEHEADS] = YES;
+               break;
+       case UNSET:
+               f_p->used[NOTEHEADS] = NO;
+               break;
+       }
+}
+\f
+/*
+ * Name:        setssvstate()
+ *
+ * Abstract:    Set the static SSVs to the state for a given place in the MML.
+ *
+ * Returns:     void
+ *
+ * Description: This function, given any structure from the main linked list,
+ *             initializes the static SSVs, and then runs through the MLL up
+ *             to just before that point, assigning SSVs.  It assigns not only
+ *             the SSVs in the MLL, but also the timed SSVs hanging off
+ *             barlines.  You can pass a null pointer, and then it will go
+ *             through the whole MLL.
+ */
+
+void
+setssvstate(mainll_p)
+
+struct MAINLL *mainll_p;       /* place in the MLL to stop */
+
+{
+       struct MAINLL *mll_p;           /* for looping through MLL */
+       struct TIMEDSSV *tssv_p;        /* for looping through TIMEDSSV lists*/
+
+
+       initstructs();
+       for (mll_p = Mainllhc_p; mll_p != 0 && mll_p != mainll_p;
+                       mll_p = mll_p->next) {
+               switch (mll_p->str) {
+               case S_SSV:
+                       /* assign this normal input SSV */
+                       asgnssv(mll_p->u.ssv_p);
+                       break;
+               case S_BAR:
+                       /* assign each timed SSV, if any */
+                       for (tssv_p = mll_p->u.bar_p->timedssv_p; tssv_p != 0;
+                                       tssv_p = tssv_p->next) {
+                               asgnssv(&tssv_p->ssv);
+                       }
+                       break;
+               }
+       }
+}
+\f
+/*
+ * Name:        setorder()
+ *
+ * Abstract:    Assign an "order" field from an input SSV to a fixed one.
+ *
+ * Returns:     void
+ *
+ * Description: This function is called by asgnssv() to assign to the
+ *             appropriate part of the markorder array, based on above, below,
+ *             or between.
+ */
+
+static void
+setorder(place, i_p, f_p)
+
+int place;             /* PL_* */
+struct SSV *i_p;       /* input SSV structure to be copied from */
+struct SSV *f_p;       /* ptr to fixed SSV structure to copy into */
+
+{
+       int m;          /* mark (MK_*) */
+       int stk;        /* stacking order */
+
+
+       /*
+        * First assign all the marks' stacking orders as given.  Keep track of
+        * the highest stacking order number found.
+        */
+       stk = 0;
+       for (m = 0; m < NUM_MARK; m++) {
+               f_p->markorder[place][m] = i_p->markorder[place][m];
+
+               if (f_p->markorder[place][m] > stk)
+                       stk = f_p->markorder[place][m];
+       }
+
+       /*
+        * For every mark type that the user didn't list, the stacking order is
+        * now 0.  Set all these to default settings, higher than all the ones
+        * the user listed, but in the same order as Defmarkorder.  They will
+        * all be separate numbers, none set equal, unlike Defmarkorder, where
+        * some are equal.
+        */
+       for (m = 0; m < NUM_MARK; m++) {
+               if (f_p->markorder[place][m] == 0) {
+                       f_p->markorder[place][m] = ++stk;
+               }
+       }
+}