/*************************************************
*    The PMW Music Typesetter - 3rd incarnation  *
*************************************************/

/* Copyright (c) Philip Hazel, 1991 - 2020 */

/* Written by Philip Hazel, starting November 1991 */
/* This file last modified: November 2020 */


/* This file contains part III of the code for reading in a PMW score file. It
contains top-level code for reading in one bar on one stave. */


#include "pmwhdr.h"
#include "readhdr.h"



/* Key signatures tables giving the semitone offsets from C of the white notes
to which accidentals apply, in order for conventional key signatures, for
initializing baraccs. */

static uschar tp_sharporder[] = { 5,0,7,2,9,4,11 };
static uschar tp_flatorder[]  = { 11,4,9,2,7,0,5 };

/* Values for baraccs for the different accidentals. */

static uschar ba_values[] = { 2, 4, 1, 0, 2, 3 };

/* Pitch offsets for custom keys */

static int ba_xkey_offsets[] = { 4, 5, 7, 9, 11, 12, 14, 4, 5, 7 };



/*************************************************
*           Initialize accidentals table         *
*************************************************/

/* The baraccs table is used to hold the number of semitones in the current
accidental for a given "white note" pitch, to enable an absolute pitch to be
calculated. To enable the table to be a byte table containing only positive
values, the values are held offset by 2, so that, for example, 0 => double
flat (-2), and 3 => sharp (+1). When transposition is happening, the baraccs
table is used for the input values, and baraccs_tp for the output values. This
function is called at the start of each bar, and also after [key] and [reset].

Arguments:
  ba         the table that is to be set (baraccs or baraccs_tp)
  key        the key signature

Returns:     nothing
*/

void
read_initbaraccs(uschar *ba, int key)
{
int i;
int value;

/* Clear out the table to no accidentals */

for (i = 0; i < baraccs_len; i++) ba[i] = ba_values[ac_natural];

/* Handle conventional key signatures */

if (key < X_key)
  {
  int countacc = main_keysigtable[key];
  uschar *ordertab;

  if (countacc >= 0)
    {
    ordertab = tp_sharporder;
    value = ba_values[ac_sharp];
    }
  else
    {
    ordertab = tp_flatorder;
    value = ba_values[ac_flat];
    countacc = -countacc;
    }

  /* Put in the key signature accidentals */

  for (i = 0; i < baraccs_len; i += 12)
    {
    int j;
    for (j = 0; j < countacc; j++) ba[i+ordertab[j]] = value;
    }
  }

/* Handle custom key signatures. The 10 values in the custom table refer to the
10 positions on a treble-clef stave; we don't need to know the clef in order to
set up the right pitches. */

else
  {
  int j;
  uschar *p = main_xkeys[key - X_key];   /* Custom accidental list */

  for (j = 0; j < 10; j++)
    {
    int offset = ba_xkey_offsets[j];
    int ac = ba_values[p[j] & 0x7f];     /* Ignore half-accidental bit */
    if (ac == ba_values[ac_natural]) continue;
    for (i = 0; i < baraccs_len; i += 12) ba[i + offset] = ac;
    }
  }
}


/*************************************************
*           Read up/down movement                *
*************************************************/

/* This is used for options for plets. On entry, read_ch should contain either
'u' or 'd', which may either be the first letter after / or might follow 'l' or
'r'. We don't come here for /lx and /rx, but they are mentioned in the error
message for unknown letters because they are something that could have been
present.

Arguments:
  value1      pointer to where the value read is added
  value2      if not NULL, the value is added here as well

Returns:      nothing
*/

static void
read_updown(int *value1, int *value2)
{
int sign, x;
switch(read_ch)
  {
  case 'u': sign = 1;  break;
  case 'd': sign = -1; break;
  default:
  error_moan(ERR37, "/l<n>, /r<n>, /lx, /rx, /lu<n>, /ld<n>, /ru<n> or /rd<n>");
  next_ch();
  return;
  }
x = sign * read_movevalue();
*value1 += x;
if (value2 != NULL) *value2 += x;
}


/*************************************************
*                 Read one bar                   *
*************************************************/

/* Called from read_go(), the top level reading control function.

Arguments:   none
Returns:     TRUE if not reached the end of the stave
*/

BOOL
read_bar(void)
{
void *bardatastart;

read_barlinestyle = stave_barlinestyle;

read_initbaraccs(baraccs, stave_key);
read_initbaraccs(baraccs_tp, stave_key_tp);

stave_resetOK = FALSE;
stave_string_followOK = FALSE;

stave_barrepeatcount = 0;
stave_barlength = stave_maxbarlength = stave_pletlen = 0;
stave_noteflags &= ~nf_cuesize;
stave_checklength = curmovt->check;

if (stave_lastwasdouble) stave_checklength &= curmovt->checkdoublebars;

stave_lastgracestem = 0;
stave_lastwasdouble = stave_overbeam = FALSE;
stave_firstinbar = TRUE;
stave_hadnocount = FALSE;
stave_tripletize = (stave_noteflags & nf_tripletize) != 0;
stave_stemstackptr = stave_beamstackptr = 0;

read_lastensuredtie = NULL;

/* Complain if too many bars */

if (stave_barnumber > curmovt->maxbarcount)
  { error_moan(ERR36, curmovt->maxbarcount); return FALSE; }

/* Update the bar number printing vector if necessary */

if (stave_totalnocount > (curmovt->barnovector)[stave_barnumber])
  (curmovt->barnovector)[stave_barnumber] = stave_totalnocount;

/* Fill in index to the bar's data */

(stavehead->barindex)[stave_barnumber] = store_nextitem();

/* If notes are switched off, insert another control for the benefit of the
playing code, which assumes notes on at the start of each bar. */

if (!stave_notes)
  {
  b_notesstr *p = store_getitem(b_notes);
  p->value = FALSE;
  }

/* If we are in an omitempty stave and the previous bar contained only an ibar
or dbar item, or ended with an unusual barline style, we must insert a prevbar
item here. */

if (stavehead->omitempty &&
    (read_prev_had_dbar ||
     read_prev_had_ibar ||
     read_prev_barlinestyle != stave_barlinestyle))
  {
  b_prevbarstr *p = store_getitem(b_prevbar);
  p->dbar = read_prev_had_dbar;
  p->ibar = read_prev_had_ibar;
  p->style = read_prev_barlinestyle;
  }

/* Remember where the real data starts for detecting an empty bar */

bardatastart = store_nextitem();

/* This is one big loop which stops after an [endstave] directive, or at the
end of file, or at the end of the bar. If nothing has been generated, reset the
index to null instead of inserting an End item. */

for (;;)
  {
  sigch();
  if (read_endstave || read_ch == EOF) break;  /* exit the loop */

  /* Deal with bar lines */

  if (read_ch == '|')
    {
    next_ch();
    if (read_ch == '|')               /* Double or internal end bar */
      {
      next_ch();
      if (read_ch == '|')
        {
        next_ch();
        (void)store_getitem(b_ebar);
        }
      else
        {
        (void)store_getitem(b_dbar);
        if (!curmovt->checkdoublebars) stave_checklength = FALSE;
        stave_lastwasdouble = TRUE;
        }
      }
    else if (read_ch == '?')          /* Invisible bar */
      {
      next_ch();
      (void)store_getitem(b_ibar);
      }
    else if (isdigit(read_ch))        /* Specific bar line type */
      {
      read_barlinestyle = read_integer(FALSE);
      }
    if (read_ch == '=')               /* Continue beam over bar line */
      {
      next_ch();
      stave_overbeam = TRUE;
      }

    /* Cancel the ensured space for a tied chord with seconds at the end of a
    bar. */

    if (read_lastensuredtie != NULL) read_lastensuredtie->value = 0;

    break;                            /* Exit the loop */
    }

  /* Deal with a staff directive or other construction that can occur in square
  brackets. */

  if (read_ch == '[')
    {
    error_skip = skip_KET;
    next_sigch();

    for (;;)
      {
      /* If a name is already read, handle it, possibly repeatedly. Note that
      at the start of a stave we enter this procedure with '[' set and possible
      "name" in read_word. That is why this test comes first. */

      while (read_stavedir) read_stavedirective();

      sigch();
      if (read_ch == ']') { next_ch(); break; }
      if (read_ch == EOF) break;

      /* Deal with the various things that can occur in brackets */

      if (isalpha(read_ch))
        {
        next_word();
        read_stavedir = TRUE;
        }

      /* Try for accent defaulting */

      else if (read_ch == '\\')
        {
        stave_accentflags = 0;
        stave_ornament = -1;
        next_ch();

        for (;;)
          {
          accent *ap;
          sigch();
          if (read_ch == '\\') { next_ch(); break; }   /* End default */

          /* Search the table of accents and ornaments */

          for (ap = accent_chars; ap->string != NULL; ap++)
            {
            int i;
            int ln = (int)Ustrlen(ap->string);
            for (i = 0; i < ln; i++)
              if (read_chptr[i-1] != ap->string[i]) break;
            if (i >= ln)
              {
              read_chptr += ln - 1;
              next_ch();
              break;
              }
            }

          /* Found in the table */

          if (ap->string != NULL)
            {
            if (ap->flag < 256) stave_ornament = ap->flag;
              else stave_accentflags |= ap->flag;
            }

          /* Not found in the table */

          else if (read_ch == 'a')
            {
            int x;
            next_ch();
            error_skip = skip_OFF;  /* Temporarily no skip */
            if (read_expect_integer(&x, FALSE, FALSE))
              {
              if (x <= dyn_max)
                stave_accentflags |= accent_list[x];
              else error_moan(ERR128, x);
              }
            error_skip = skip_KET;
            }

          else
            {
            error_moan(ERR45);
            if (read_ch == ']') break;
            next_sigch();
            }
          }

        if ((stave_accentflags & (af_staccato|af_staccatiss)) ==
          (af_staccato|af_staccatiss)) error_moan(ERR129);
        }

      /* Deal with repeated bars; expect number > 0, but subtract one from it.
      If number is followed by nd, rd, or th, it is an nth time bar marking. */

      else if (isdigit(read_ch))
        {
        int n = read_integer(FALSE);

        if (n > 0)
          {
          int done = FALSE;
          sigch();
          if (isalpha(read_ch))
            {
            next_word();
            if (Ustrcmp(read_word, "st") == 0 ||
                Ustrcmp(read_word, "nd") == 0 ||
                Ustrcmp(read_word, "rd") == 0 ||
                Ustrcmp(read_word, "th") == 0)
              {
              b_nbarstr *p = store_getitem(b_nbar);
              p->x = p->y = 0;
              p->n = n;
              p->s = NULL;
              p->ssize = 0;   /**** Not currently in use ****/

              sigch();
              while (read_ch == '/')
                {
                int xsign = 1;
                int ysign = 1;

                next_ch();

                switch(read_ch)
                  {
                  case 'd':
                  ysign = -1;
                  /* Fall through */

                  case 'u':
                  next_ch();
                  read_expect_integer(&n, TRUE, FALSE);
                  p->y += n * ysign;
                  break;

                  case 'l':
                  xsign = -1;
                  /* Fall through */

                  case 'r':
                  next_ch();
                  read_expect_integer(&n, TRUE, FALSE);
                  p->x += n * xsign;
                  break;

                  /* One day we might want individual sizes for strings, but for
                  the moment this is not implemented. */

                  /*****************
                  case 's':
                  next_ch();
                  read_expect_integer(&n, FALSE, FALSE);
                  p->ssize = n;
                  break;
                  *****************/

                  case '\"':
                  p->s = string_read();
                  break;

                  default:
                  error_moan(ERR10, "\"u\", \"d\", \"l\", \"r\", or quoted string");
                  break;
                  }
                }

              done = TRUE;
              }
            else read_stavedir = TRUE;
            }

          /* Repeated bar */

          if (!done)
            {
            stave_barrepeatcount = n - 1;
            stave_barrepeatptr = store_nextitem();
            }
          }
        else error_moan(ERR10, "Number greater than zero");
        }

      /* Deal with rehearsal string */

      else if (read_ch == '\"') string_stavestring(TRUE);

      /* Unrecognized */

      else error_moan(ERR10, "Staff directive");
      }

    error_skip = skip_EOL;
    }

  /* Deal with items not in directive brackets. Note that '|' has been dealt
  with earlier. */

  else switch (read_ch)
    {
    /* Curly brackets are used for the start and end of plets. The end is often
    dealt with in read_note, but can sometimes turn up here if stave directives
    intervene. */

    case '{':
    if (stave_pletlen != 0)
      {
      error_moan(ERR38);
      next_ch();
      }
    else
      {
      b_pletstr *p;
      int sign;
      int  flags = 0;
      int  adjustx = 0;
      int  adjustyleft = stave_plety;
      int  adjustyright = adjustyleft;

      next_sigch();

      /* The length of each note in the plet will be multiplied by
      stave_pletsupnum, and divided by stave_pletlen times stave_pletsupden. */

      stave_pletsupnum = 0;   /* Indicates not explicitly set */
      stave_pletsupden = 1;   /* Normal case */
      stave_pletlen = 3;      /* Default to triplet */

      /* Read an explicit specification */

      if (isdigit(read_ch))
        {
        stave_pletlen = read_integer(FALSE);  /* First number */

        /* A minus indicates that we are dividing a smaller number of notes
        than the default. This is accomplished by dividing by 2. */

        if (read_ch == '-') { next_sigch(); stave_pletsupden = 2; }

        /* A slash indicates that the first number is really the number of
        notes we are dividing, and the real value of stave_pletlen follows. */

        if (read_ch == '/' && isdigit(*read_chptr))
          {
          stave_pletsupnum = stave_pletlen;
          next_ch();
          stave_pletlen = read_integer(FALSE);
          }
        }

      /* If we are dividing into a power of two, assume we are dividing 3 into
      this number if a numerator was not provided; otherwise assume 2. */

      if (stave_pletsupnum == 0) stave_pletsupnum =
        ((stave_pletlen & (-stave_pletlen)) == stave_pletlen)? 3 : 2;

      /* If the number of irregular notes is more than twice the number of
      regular notes, we double the numerator, because the irregulars must be
      the next note size down; similarly quadruple the numerator for even more
      notes. */

      if (stave_pletlen >= 4*stave_pletsupnum) stave_pletsupnum *= 4;
        else if (stave_pletlen >= 2*stave_pletsupnum) stave_pletsupnum *= 2;

      /* Now deal with the options */

      while (read_ch == '/')
        {
        next_ch();
        switch(read_ch)
          {
          case 'a':
          flags &= ~plet_b;
          flags |= plet_a | plet_by;
          sign = +1;
          goto ABSPLET;

          case 'b':
          flags &= ~plet_a;
          flags |= plet_b | plet_by;
          sign = -1;
        ABSPLET:
          flags &= ~(plet_abs | plet_bn);
          adjustyleft = adjustyright = 0;
          next_sigch();
          if (isdigit(read_ch))
            {
            flags |= plet_abs;
            adjustyleft = adjustyright = sign * read_integer(TRUE);
            }
          break;

          case 'n':
          flags &= ~plet_by;
          flags |= plet_bn;
          next_sigch();
          break;

          case 'x': flags |= plet_x; next_sigch(); break;

          case 'u':
          case 'd':
          read_updown(&adjustyleft, &adjustyright);
          break;

          case 'l':
          next_ch();
          if (isdigit(read_ch)) adjustx -= read_integer(TRUE); else
            {
            if (read_ch == 'x') { flags |= plet_lx; next_ch(); }
              else read_updown(&adjustyleft, NULL);
            }
          break;

          case 'r':
          next_ch();
          if (isdigit(read_ch)) adjustx += read_integer(TRUE); else
            {
            if (read_ch == 'x') { flags |= plet_rx; next_ch(); }
              else read_updown(&adjustyright, NULL);
            }
          break;

          default:
          error_moan(ERR37, "a, b, n, x, u, d, l or r");
          next_sigch();
          break;
          }
        sigch();
        }

      /* If neither /a nor /b has been given, use the default flags */

      if ((flags & (plet_a | plet_b)) == 0) flags |= stave_pletflags;

      /* Now generate the data block */

      p = store_getitem(b_plet);
      p->pletlen = stave_pletlen;
      p->flags = flags;
      p->x = adjustx;
      p->yleft = adjustyleft;
      p->yright = adjustyright;
      }

    /* Prevent reset (but resets are forbidden inside plets anyway). */

    stave_resetOK = FALSE;
    break;

    /* Ends of plets are often dealt with at the end of a note, but can
    occasionally arise here if another directive intervenes. */

    case '}':
    if (stave_pletlen == 0) error_moan(ERR38); else
      {
      stave_pletlen = 0;
      (void)store_getitem(b_endplet);
      }
    next_ch();
    break;

    /* Angle brackets signal hairpins */

    case '>':
    case '<':
      {
      int flags = stave_hairpinflags;
      int width = stave_hairpinwidth;
      int type = read_ch;
      int ending = (type == '<' && stave_hairpinbegun == '<') ||
                   (type == '>' && stave_hairpinbegun == '>');
      int y = ending? 0 : stave_hairpiny;
      int x = 0, h = 0, offset = 0;
      int slu = 0, sru = 0;

      next_ch();
      while (read_ch == '/')
        {
        next_ch();

        /* b is anomalous - /bar is always allowed, but /b only on
        beginning hairpins. */

        if (read_ch =='b' && *read_chptr == 'a' && read_chptr[1] == 'r')
          {
          flags |= hp_bar;
          next_ch();
          next_ch();
          next_ch();
          }

        else switch(read_ch)
          {
          case 'u': y += read_movevalue(); break;
          case 'd': y -= read_movevalue(); break;

          case 'l':
          if (*read_chptr == 'c')
            {
            next_ch();
            offset -= read_movevalue();
            }
          else x -= read_movevalue();
          break;

          case 'r':
          if (*read_chptr == 'c')
            {
            next_ch();
            offset += read_movevalue();
            }
          else x += read_movevalue();
          break;

          case 'h':
          next_ch();
          h = 500;
          if (isdigit(read_ch)) h = read_integer(TRUE);
          flags |= hp_halfway;
          break;

          case 's':
          next_ch();
          if (read_ch == 'l' || read_ch == 'r')
            {
            int sign, amount;
            int *a = (read_ch == 'l')? &slu : &sru;
            next_ch();
            if (read_ch == 'u' || read_ch == 'd')
              {
              sign = (read_ch == 'u')? +1 : -1;
              }
            else
              {
              error_moan(ERR37, "u or d");
              break;
              }
            next_ch();
            if (!read_expect_integer(&amount, TRUE, FALSE)) break;
            *a += sign*amount;
            }
          else error_moan(ERR37, "\"slu\", \"sld\", \"sru\" or \"srd\"");
          break;

          default:
          if (!ending)
            {
            int sign;
            switch(read_ch)
              {
              case 'a':
              flags &= ~hp_below;
              sign = 1;
              goto ABSHP;

              case 'b':
              flags |= hp_below;
              sign = -1;
            ABSHP:
              y = 0;
              flags &= ~(hp_middle | hp_abs);
              next_ch();
              if (isdigit(read_ch))
                {
                flags |= hp_abs;
                y = sign * read_integer(TRUE);
                }
              break;

              case 'm':
              flags |= hp_below | hp_middle;
              next_ch();
              break;

              case 'w':
              next_ch();
              read_expect_integer(&width, TRUE, FALSE);
              break;

              default:
              error_moan(ERR37,
                "\"u\", \"d\", \"l\", \"r\", \"a\", \"b\", \"m\", \"w\", \"slu\", \"sru\" or \"h\"");
              break;
              }
            }
          else error_moan(ERR37, "\"u\", \"d\", \"l\", \"r\", \"su\" or \"h\"");
          break;
          }
        }

      if (stave_hairpinbegun)
        {
        b_hairpinstr *p = store_getitem(b_hairpin);
        p->opt = 0;
        if (ending)
          {
          p->flags = flags;
          p->x = x;
          p->y = y;
          p->offset = offset;
          p->su = stave_hairpinsru;
          p->h = h;
          }
        else p->flags = p->x = p->y = p->offset = p->su = p->h = 0;
        }

      if (ending) stave_hairpinbegun = 0; else
        {
        b_hairpinstr *p = store_getitem(b_hairpin);
        p->opt = stave_hairpinbegun = type;
        p->flags = flags;
        p->x = x;
        p->y = y;
        p->su = slu;
        p->h = h;
        p->offset = offset;
        p->width = width;
        stave_hairpinsru = sru;
        stave_resetOK = FALSE;
        }
      }
    break;

    /* A slash must be followed by another, for a caesura */

    case '/':
    next_ch();
    if (read_ch != '/') error_moan(ERR37, "\"/\" (to make // for caesura)"); else
      {
      (void)store_getitem(b_caesura);
      next_ch();
      }
    break;

    /* Quotes introduce text */

    case '\"':
    string_stavestring(FALSE);
    break;

    /* A colon can be a dotted bar or the end of a repeat. */

    case ':':
    next_ch();
    if (read_ch == ')')
      {
      (void)store_getitem(b_rrepeat);
      next_ch();
      if (stave_barlength == 0) error_moan(ERR138, "End", "start");  /* Warning */
      }
    else (void)store_getitem(b_dotbar);
    break;

    /* A semi-colon used to be a general separator, way back in early days.
    Later, it was used as a beam breaking character, but still kept as a
    separator. This meant that if a beam break was incorrectly placed, no
    warning was given. It is therefore no longer a general separator, unless
    the very old, undocumented OldBeamBreak option is turned on (don't know if
    anyone will every feed PMW files with that set, but you never know). */

    case ';':
    if (!opt_oldbeambreak) error_moan(ERR5, "semicolon");
    next_sigch();
    break;

    /* Might as well give a similar error for a stray comma (which isn't old
    enough to be tied up with old beam breaks). */

    case ',':
    error_moan(ERR5, "comma");
    next_sigch();
    break;

    /* A bracket can be the start of a repeat, or the start of a chord. We peek
    at the next character to make sure. */

    case '(':
    if (*read_chptr == ':')
      {
      store_getitem(b_lrepeat);
      next_ch();
      next_sigch();
      if (read_ch == '|') error_moan(ERR138, "Start", "end");  /* Warning */
      break;
      }

    /* Else fall through ... */

    /* If all else fails, try to read a note */

    default:
    read_note();
    break;
    }
  }

/* Special handling for omitempty bars. If there is nothing in the bar except
an invisible or double bar specification, and/or if the barline style was
specified, set the bar as empty, and set flags so that a prevbar block is
created at the start of the next bar. */

if (stavehead->omitempty)
  {
  void *nextitem = store_nextitem();
  bstr *p = bardatastart;

  read_prev_had_ibar = FALSE;
  read_prev_had_dbar = FALSE;
  read_prev_barlinestyle = stave_barlinestyle;

  while (p != nextitem)
    {
    int type = p->type;
    switch(type)
      {
      case b_Jump:
      p = (bstr *)(((b_Jumpstr *)p)->next);
      break;

      case b_ibar:
      read_prev_had_ibar = TRUE;
      break;

      case b_dbar:
      read_prev_had_dbar = TRUE;
      break;

      default:
      goto NORMALBAREND;
      }
    p = (bstr *)((uschar *)p + length_table[type]);
    }

  /* If control gets here, 'twas only ibar or dbar */

  if (read_prev_had_ibar ||
      read_prev_had_dbar ||
      read_barlinestyle != stave_barlinestyle)
    {
    read_prev_barlinestyle = read_barlinestyle;
    bardatastart = nextitem;             /* make empty */
    }
  }

NORMALBAREND:

/* If nothing has been generated, reset the index to NULL. Otherwise, mark the
end of the bar and, if required, check its length. Then terminate any
incomplete beam and set the stems of any pending notes. */

if (bardatastart == store_nextitem())
  {
  (stavehead->barindex)[stave_barnumber] = NULL;
  stave_barrepeatptr = NULL;    /* for repeated nothing */
  }
else
  {
  b_Endstr *b = store_getitem(b_End);
  b->overbeam = stave_overbeam;
  b->barlinestyle = read_barlinestyle;

  /* Set stave_maxbarlength to the longest length encountered, allowing for
  [reset]s. */

  if (stave_barlength > stave_maxbarlength)
    stave_maxbarlength = stave_barlength;

  /* Check length. If the bar contains tuplets of prime numbers greater than
  13, there may be rounding errors because the length of a breve is not a
  multiple of, for example, 17. This can also happen with tuplets of other
  kinds that involve hemidemisemiquavers. In these cases we allow a small
  discrepancy. */

  if (stave_checklength && stave_maxbarlength != 0)
    {
    int extra = stave_maxbarlength - stave_requiredbarlength;
    if (abs(extra) > TUPLET_ROUND)
      {
      format_movt = curmovt;
      error_moan(ERR49, stave_barnumber, curstave,
        (extra > 0)? "long":"short", abs(extra));
      }
    }

  /* Deal with incomplete beams and unchosen stems */

  if (stave_beaming) read_setbeamstems();
  mac_setstackedstems(stave_laststemup? nf_stemup : 0);

  /* If tripletizing was set at any point in this bar, scan it for dotted-
  quaver groups and adjust the note lengths. Also handle dotted crotchet
  followed by two semiquavers. */

  if (stave_tripletize)
    {
    b_notestr *first = (b_notestr *)((stavehead->barindex)[stave_barnumber]);
    if (first->type != b_note) first = misc_nextnote(first, NULL);

    while (first != NULL)
      {
      b_notestr *next = misc_nextnote(first, NULL);
      if (next == NULL) break;

      /* Both notes must be flagged for tripletizing */

      if ((first->flags & next->flags & nf_tripletize) != 0)
        {
        b_notestr *third;

        if (first->length == (len_quaver * 3)/2 &&
            next->length == len_squaver)
          {
          first->length = (len_crotchet * 2)/3;
          next->length = len_crotchet/3;
          first = misc_nextnote(next, NULL);
          continue;
          }

        third = misc_nextnote(next, NULL);
        if (third == NULL) break;
        if ((third->flags & nf_tripletize) != 0 &&
            first->length == (len_crotchet * 3)/2 &&
            next->length == len_squaver &&
            third->length == len_squaver)
          {
          first->length = (len_crotchet * 4)/3;
          next->length = len_crotchet/3;
          third->length = len_crotchet/3;
          first = third;
          continue;
          }
        }

      /* No tripletizing this pair */

      first = next;
      }
    }
  }

/* Grumble if unclosed plet */

if (stave_pletlen != 0) error_moan(ERR83);

/* Check for end of file and return FALSE at end of stave */

sigch();
read_endstave |= (read_ch == EOF);
return !read_endstave;
}

/* End of read3.c */
