💾 Archived View for runjimmyrunrunyoufuckerrun.com › src › foreign › pmw › src › read2.c captured on 2021-12-17 at 13:26:06.

View Raw

More Information

-=-=-=-=-=-=-

/*************************************************


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

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


/* This file contains part II of the code for reading in a PMW score file. It
deals with heading information. The main control function is last, preceded by
a table of heading directives which refers to earlier functions for processing
them. */


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


#define oo offsetof

/* Options for reading integer values */

#define int_u       1    /* unsigned */
#define int_s       2    /* signed */
#define int_rs      3    /* relative if signed */
#define int_less1   4    /* take away 1 (flag) */
#define int_f       8    /* fixed point (flag, must be last) */

/* Data for the b2pffont directive */

#ifdef SUPPORT_B2PF
typedef struct b2pfopt {
  const char *name;
  uint32_t option;
} b2pfopt;

static b2pfopt b2pf_options[] = {
  { "input_backchars", B2PF_INPUT_BACKCHARS },
  { "input_backcodes", B2PF_INPUT_BACKCODES },
  { "output_backchars", B2PF_OUTPUT_BACKCHARS },
  { "output_backcodes", B2PF_OUTPUT_BACKCODES }
};

#define b2pf_options_size (sizeof(b2pf_options)/sizeof(b2pfopt))
#endif


/*************************************************


/* Since we cannot put global addresses directly into the static table of
structures, we have to indirect them via a separate vector. (This restriction
is in the C language.) */

static int *global_vars[] = {
  (int *)(&main_magnification),
  (int *)(&main_maxvertjustify),
  (int *)(&midi_for_notes_off),
  (int *)(&opt_oldbeambreak),
  (int *)(&opt_oldrestlevel),
  (int *)(&opt_oldstemlength),
  (int *)(&main_truepagelength),
  (int *)(&main_righttoleft),
  (int *)(&main_sheetheight),
  (int *)(&main_sheetwidth),
  (int *)(&opt_stretchrule),
  (int *)(&main_kerning)
};

enum {
  glob_magnification,
  glob_maxvertjustify,
  glob_midifornotesoff,
  glob_oldbeambreak,
  glob_oldrestlevel,
  glob_oldstemlength,
  glob_pagelength,
  glob_righttoleft,
  glob_sheetdepth,
  glob_sheetwidth,
  glob_stretchrule,
  glob_kerning
};


/*************************************************


static int read_map[STAVE_BITVEC_SIZE];  /* For reading lists of staves */


/*************************************************


/* If a recognized font word ("roman", "italic", etc.) is the next thing in the
input, read it and put its index number into the current movement data
structure, at a given offset. If what follows is not a recognized font word, do
nothing.

Argument:   the offset for the result
Returns:    nothing


static void
set_fontname(int offset)
{
sigch();
if (isalpha(read_ch))
  {
  uschar *save_readchptr = read_chptr;
  int save_readch = read_ch;
  int x;
  if ((x = font_fontword(TRUE)) > 0)
    {
    *((int *)(((uschar *)curmovt) + offset)) = x;
    }
  else
    {
    read_ch = save_readch;
    read_chptr = save_readchptr;
    }
  }
}



/*************************************************


/* This is for the table of font sizes in the movement structure. If the next
thing in the input is a digit, read it as a font size. If not, do nothing.
The size may be followed by a stretch and shear, if permitted.

Arguments:
  offset      offset in current movement data structure for the size result
  stretchOK   TRUE if stretch and/or shearing are permitted for the font

Returns:      nothing


static void
set_fontsize(int offset, BOOL stretchOK)
{
sigch();
if (isdigit(read_ch))
  {
  int *sizeptr, **matrixptr;

  if (!read_copied_fontsizestr)
    {
    fontsizestr *new = store_Xget(sizeof(fontsizestr));
    *new = *(curmovt->fontsizes);
    curmovt->fontsizes = new;
    read_copied_fontsizestr = TRUE;
    }

  /* Compute the addresses of where to put the size and the stretch/shear
  matrix, allowing for the fact that pointers may not be the same size as ints,
  an error that wasn't fixed till February 2014. */

  sizeptr = (int *)(((uschar *)(curmovt->fontsizes)) + offset);
  matrixptr = (int **)((uschar *)(curmovt->fontsizes) +
              oo(fontsizestr, fontmatrix_music) +
              (offset/sizeof(int)) * sizeof(int *));

  /* Read size, default no matrix */

  *sizeptr = read_integer(TRUE);
  *matrixptr = NULL;

  /* Handle stretch and shear */

  if (read_ch == '/')
    {
    int stretch;
    int shear = 0;
    next_ch();
    if (!read_expect_integer(&stretch, TRUE, FALSE)) return;
    if (read_ch == '/')
      {
      next_ch();
      if (!read_expect_integer(&shear, TRUE, TRUE)) return;
      }
    if (!stretchOK) error_moan(ERR97, "allowed", "with this directive"); else
      {
      int *matrix = store_Xget(4*sizeof(int));
      *matrixptr = matrix;
      matrix[0] = mac_muldiv(stretch, 65536, 1000);
      matrix[1] = 0;
      matrix[2] = (int)(tan(((double)shear)*atan(1.0)/45000.0)*65536.0);
      matrix[3] = 65536;
      }
    }
  }
}


/*******************************************************************



/*************************************************


static void
movt_fszproc(void)
{
sigch();
if (isdigit(read_ch)) set_fontsize(read_dir->arg1, read_dir->arg2);
  else error_moan(ERR10, "Number");
}


/*************************************************


static void
movt_cboolproc(void)
{

}


/*************************************************


static void
movt_fontproc(void)
{
int x;
set_fontsize(read_dir->arg2, TRUE);
if ((x = font_fontword(FALSE)) > 0)
  *((int *)(((uschar *)curmovt) + read_dir->arg1)) = x;
}


/*************************************************


static void
opt_boolproc(void)
{
if (main_lastmovement == 1)
  *global_vars[read_dir->arg1] = read_dir->arg2;
else error_moan(ERR20);
}


/*************************************************


static void
movt_intproc(void)
{
int x;
int value = 0;
int flags = read_dir->arg2;
int offset = ((flags & int_less1) != 0)? -1 : 0;
int arg2 = flags & ~(int_f | int_less1);

int *address = (int *)((uschar *)curmovt + read_dir->arg1);
sigch();
if (arg2 == int_rs && (read_ch == '+' || read_ch == '-')) value = *address;
if (read_expect_integer(&x, flags >= int_f, arg2 != int_u))
  *address = value + x + offset;
}


/*************************************************


static void
opt_intproc(void)
{
int x;
int *address = global_vars[read_dir->arg1];
int value = 0;
int arg2 = (read_dir->arg2) & ~int_f;
sigch();
if (arg2 == int_rs && (read_ch == '+' || read_ch == '-')) value = *address;
if (read_expect_integer(&x, read_dir->arg2 >= int_f, arg2 != int_u))
  *address = value + x;
if (main_lastmovement != 1) error_moan(ERR20);
}


/*************************************************


static void
movt_listproc(void)
{
stave_list **p = (stave_list **)(((uschar *)curmovt) + read_dir->arg1);


sigch();
while (isdigit(read_ch))
  {
  int s = read_integer(FALSE);
  int t = s;
  sigch();
  if (read_ch == '-')
    {
    next_sigch();
    if (!read_expect_integer(&t, FALSE, TRUE)) return;
    }

  if (t < s) error_moan(ERR21);
    else if (t > MAX_STAVE) error_moan(ERR22, MAX_STAVE+1);
  else
    {
    stave_list *q = store_Xget(sizeof(stave_list));
    q->next = NULL;
    q->first = s;
    q->last = t;
    *p = q;
    p = &(q->next);
    }

  sigch();
  if (read_ch == ',') next_sigch();
  }
}


/*************************************************


static void
movt_mapproc(void)
{
mac_initstave(read_map, 0);
if (read_dir->arg2 >= 0) mac_setstave(read_map, read_dir->arg2);

sigch();
while (isdigit(read_ch))
  {
  int s = read_integer(FALSE);
  int t = s;
  sigchNL();
  if (read_ch == '-')
    {
    next_sigch();
    if (!read_expect_integer(&t, FALSE, TRUE)) return;
    }

  if (t < s) error_moan(ERR21);
    else if (t > MAX_STAVE) error_moan(ERR22, MAX_STAVE + 1);
      else while (s <= t) { mac_setstave(read_map, s); s++; }  /* Can't put */
                                                               /* s++ inside */
  sigchNL();
  if (read_ch == ',') next_sigch();
  }

if (read_dir->arg1 != 0)
  memcpy(((uschar *)curmovt) + read_dir->arg1, read_map,
    STAVE_BITVEC_SIZE*sizeof(int));
}


/*************************************************


/* The bits in the map remember which have been read, for flushing at the start
of a new movement. */

static void
movt_headproc(void)
{
headstr **oldp = (headstr **)(((uschar *)curmovt) + read_dir->arg1);
headstr *new = store_Xget(sizeof(headstr));

/* If this is the first one of this type of heading, reset the pointer
so as not to copy the ones from the previous movement. */

if ((read_headmap & read_dir->arg2) == 0)
  {
  read_headmap |= (read_dir->arg2 & ~rh_ps);
  *oldp = NULL;
  }

/* Else find the end of the chain */

else while (*oldp != NULL) oldp = &((*oldp)->next);

/* Add the new block onto the chain and initialize */



read_headfootingtext(new, read_dir->arg2);
}


/*************************************************


/* This code is also called from read4 for reading the text of footnotes and
sysnotes, so it has to be globally addressible. */

void
read_headfootingtext(headstr *new, int type)
{
int defaultsize = 0;
BOOL is_pshdr = (type & rh_ps) != 0;

new->next = NULL;
new->stretch = 0;
new->font = font_rm;

/* For a non-PostScript header, if the next character is a letter, we expect to
read "draw <name>"; otherwise we expect an optional type size and a text
string. */

sigch();
if (!is_pshdr && isalpha(read_ch))
  {
  tree_node *node;
  int argcount = 0;
  drawitem *drawargs = NULL;
  drawitem args[20];

  next_word();
  if (Ustrcmp(read_word, "draw") != 0)
    {
    error_moan(ERR10, "\"draw\"");
    return;
    }

  sigch();
  while (isdigit(read_ch) || read_ch == '-' || read_ch == '+' || read_ch == '\"')
    {
    if (read_ch == '\"')
      {
      args[++argcount].d.ptr = read_draw_text();
      args[argcount].dtype = dd_text;
      }
    else
      {
      if (!read_expect_integer(&(args[++argcount].d.val), TRUE, TRUE)) break;
      args[argcount].dtype = dd_number;
      }
    sigch();
    }

  if (argcount > 0)
    {
    int i;
    drawargs = store_Xget((argcount+1)*sizeof(drawitem));
    drawargs[0].dtype = dd_number;
    drawargs[0].d.val = argcount;
    for (i = 1; i <= argcount; i++) drawargs[i] = args[i];
    }

  next_word();
  node = Tree_Search(draw_tree, read_word);
  if (node == NULL)
    {
    error_moan(ERR70, read_word);
    return;
    }
  new->drawing = node;
  new->drawargs = drawargs;
  new->size = -1;
  }

/* Deal with PostScript and non-draw heading/footing */

else
  {
  /* In the non-PostScript case, set the type size, either by reading, or by
  algorithm. If explicit, can be followed by stretch and possibly shear,
  otherwise a NULL matrix. */

  new->matrix = NULL;
  if (!is_pshdr && isdigit(read_ch))
    {
    new->size = read_integer(TRUE);
    if (read_ch == '/')
      {
      int *matrix = store_Xget(4*sizeof(int));
      int stretch;
      int shear = 0;
      next_ch();
      if (!read_expect_integer(&stretch, TRUE, FALSE)) return;
      if (read_ch == '/')
        {
        next_ch();
        if (!read_expect_integer(&shear, TRUE, TRUE)) return;
        }
      new->matrix = matrix;
      matrix[0] = mac_muldiv(stretch, 65536, 1000);
      matrix[1] = 0;
      matrix[2] = (int)(tan(((double)shear)*atan(1.0)/45000.0)*65536.0);
      matrix[3] = 65536;
      }
    sigch();
    }
  else switch(type)
    {
    case rh_heading:
    new->size = read_headingsizes[read_headcount];
    break;

    case rh_footing:
    case rh_pagefooting:
    case rh_lastfooting:
    new->size = read_footingsize;
    break;

    case rh_pageheading:
    new->size = read_pageheadingsize;
    break;

    case rh_footnote:
    new->size = curmovt->fontsizes->fontsize_footnote;
    new->matrix = curmovt->fontsizes->fontmatrix_footnote;
    break;

    default:        /* PostScript headings/footings */
    new->size = 0;  /* This indicates that it's PostScript */
    break;
    }

  /* Default size is type size */

  defaultsize = new->size;

  /* Must increment headcount whether explicit size supplied or not */

  if (type == rh_heading && read_headcount < read_maxheadcount)
    read_headcount++;

  /* We now expect a heading string; if not PostScript, check its escapes */

  new->text = string_read();
  new->spaceabove = 0;
  if (new->text != NULL && !is_pshdr) new->text = string_check(new->text);
  }

/* The stretch value is set to zero here; if required it will be set later. */

new->stretch = 0;

/* For a non-PostScript header, if another number follows, it is the space that
follows the heading; if not, default it. */

sigch();
if (is_pshdr) new->space = 0; else
  {
  if (isdigit(read_ch) || ((read_ch == '+' || read_ch == '-') &&
      isdigit(*read_chptr)))
    read_expect_integer(&(new->space), TRUE, TRUE);
  else new->space = defaultsize;
  }
}


/*************************************************


static void
accadjusts(void)
{
int i;
int *x = store_Xget(8*sizeof(int));
for (i = 0; i < 8; i++)
  {
  sigch();
  if (read_ch == ',') next_sigch();
  if (read_ch != '-' && read_ch != '+' && !isdigit(read_ch)) x[i] = 0;
    else (void)read_expect_integer(x+i, TRUE, TRUE);
  }
curmovt->accadjusts = x;
}


/*************************************************


static void
accspacing(void)
{
int i;
int *x = store_Xget(6*sizeof(int));
for (i = 1; i < 6; i++)
  {
  sigch();
  if (read_ch == ',') next_sigch();
  if (!read_expect_integer(x+i, TRUE, TRUE)) break;
  }
curmovt->accspacing = x;
}


/*************************************************


/* The b2pffont directive is available only if explicitely selected when PMW is
built. It is permitted only in the first movement of a file (like textfont). */

static void
b2pffont(void)
{
#ifndef SUPPORT_B2PF
error_moan(ERR151);   /* Hard error: not supported */
#else

int fontid, rc;
unsigned int ln;
uint32_t options = 0;

if (main_lastmovement != 1) { error_moan(ERR20); return; }

sigch();
if ((fontid = font_fontword(FALSE)) < 0) return;
if (font_b2pf_contexts[fontid] != NULL) error_moan(ERR154);  /* Hard */

/* Read B2PF options */

sigch();
while (read_ch != '\"')
  {
  size_t i;
  next_word();
  for (i = 0; i < b2pf_options_size; i++)
    {
    if (Ustrcmp(read_word, b2pf_options[i].name) == 0)
      {
      options |= b2pf_options[i].option;
      break;
      }
    }
  if (i >= b2pf_options_size)
    {
    error_moan(ERR152, read_word);
    break;
    }
  sigch();
  }

font_b2pf_options[fontid] = options;  /* Remember processing options */

/* Read the B2PF context name */

if (!read_plainstring())
  {
  error_moan(ERR10, "B2PF context name (quoted)");
  return;
  }

/* Create a B2PF context for this font (there are currently no context create
options). */

rc = b2pf_context_create((const char *)read_word,
  (const char *)font_data_extra, 0, font_b2pf_contexts + fontid,
  NULL, NULL, NULL, &ln);

/* If there are more strings in quotes, add them as extra context rules. */

if (rc == B2PF_SUCCESS)
  {
  sigch();
  while (read_ch == '\"')
    {
    (void)read_plainstring();  /* Can't fail if leading quote seen */
    rc = b2pf_context_add_file(font_b2pf_contexts[fontid],
      (const char *)read_word, (const char *)font_data_extra, 0, &ln);
    if (rc != B2PF_SUCCESS) break;
    sigch();
    }
  }

if (rc != B2PF_SUCCESS)
  {
  size_t buffused;
  char buffer[128];
  (void)b2pf_get_error_message(rc, buffer, sizeof(buffer), &buffused, 0);
  buffer[buffused] = 0;
  error_moan(ERR155, buffer);  /* Hard */
  }

#endif  /* SUPPORT_B2PF */
}


/*************************************************


static void
barlinespace(void)
{
sigch();
if (read_ch == '*')
  {
  curmovt->barlinespace = (int)0x80000000;
  next_sigch();
  }
else movt_intproc();
}


/*************************************************


/* A sign is mandatory */

static void
barnumberlevel(void)
{
sigch();
if (read_ch != '+' && read_ch != '-') error_moan(ERR10, "\"+\" or \"-\"");
  else movt_intproc();
}


/*************************************************


static void
barnumbers(void)
{
int wordread = FALSE;
curmovt->barno_textflags = 0;
sigch();
if (isalpha(read_ch))
  {
  next_word();
  sigch();
  if (Ustrcmp(read_word, "boxed") == 0) curmovt->barno_textflags = text_box;
    else if (Ustrcmp(read_word, "ringed") == 0) curmovt->barno_textflags = text_ring;
      else wordread = TRUE;
  }

if (!wordread && isalpha(read_ch))
  { next_word(); wordread = TRUE; }

if (wordread)
  {
  if (Ustrcmp(read_word, "line") == 0) curmovt->barno_interval = -1;
    else { error_moan(ERR10, "\"line\""); return; }
  }
else
  {
  if (!read_expect_integer(&(curmovt->barno_interval), FALSE, FALSE)) return;
  }

set_fontsize(oo(fontsizestr, fontsize_barno), TRUE);
set_fontname(oo(movtstr, font_barnumber));
}


/*************************************************


static void
bracestyle(void)
{
movt_intproc();
if (curmovt->bracestyle > 1)
  {
  error_moan(ERR37, "Number less than 2");
  curmovt->bracestyle = 0;
  }
}


/*************************************************


static void
breakbarlines(void)
{
sigch();
if (!isdigit(read_ch))
  {
  usint *map = (usint *)(((uschar *)curmovt) + read_dir->arg1);
  mac_initstave(map, -1);
  }
else movt_mapproc();
curmovt->fullbarend = FALSE;
}


/*************************************************


static void
breakbarlinesx(void)
{
breakbarlines();
curmovt->fullbarend = TRUE;
}


/*************************************************


static void
caesurastyle(void)
{
movt_intproc();
if (curmovt->caesurastyle > 1)
  {
  error_moan(ERR37, "Number less than 2");
  curmovt->caesurastyle = 0;
  }
}


/*************************************************


static void
clefsize(void)
{
sigch();
if (isdigit(read_ch))
  {
  set_fontsize(oo(fontsizestr,fontsize_clefs), FALSE);
  (curmovt->fontsizes)->fontsize_clefs *= 10;
  }
else error_moan(ERR10, "Number");
}


/************************************************


static void
clefstyle(void)
{
movt_intproc();
if (curmovt->clefstyle > 3) error_moan(ERR10, "Number less than 4");
}


/************************************************


static int shape[] = { clef_treble, clef_bass, clef_alto, clef_h, clef_none };

static void
clefwidths(void)
{
int i;
sigch();
for (i = 0; i < 5 && isdigit(read_ch); i++)
  {
  int width, j;
  (void)read_expect_integer(&width, FALSE, FALSE);
  if (read_ch == ',') next_ch();
  sigch();
  for (j = 0; j < clef_count; j++)
    if (main_cleftypes[j] == shape[i]) curmovt->clefwidths[j] = width;
  }
}


/*************************************************


static void
copyzero(void)
{
zcopystr **pp = &(curmovt->zcopy);
sigch();

if (!isdigit(read_ch)) { error_moan(ERR10, "stave number"); return; }

while (isdigit(read_ch))
  {
  zcopystr *p = store_Xget(sizeof(zcopystr));
  *pp = p;
  pp = &(p->next);
  p->next = NULL;
  p->baradjust = 0;
  p->stavenumber = read_integer(FALSE);
  if (read_ch == '/')
    {
    next_ch();
    if (!read_expect_integer(&(p->adjust), TRUE, TRUE)) break;
    }
  else p->adjust = 0;
  sigch();
  if (read_ch == ',') next_sigch();
  }
}


/*************************************************


static void
doublenotes(void)
{
main_notenum *= 2;
curmovt->time = read_scaletime(curmovt->time);
}


/*************************************************


/* Read up to two fixed point numbers, second defaulting to the first; "+" and
"-" can be used to adjust values. */

static void
gracespacing(void)
{
int arg;
int value = 0;
BOOL wasrelative = FALSE;

sigch();
if (read_ch == '+' || read_ch == '-')
  {
  value = curmovt->gracespacing[0];
  wasrelative = TRUE;
  }
if (!read_expect_integer(&arg, TRUE, TRUE)) return;
curmovt->gracespacing[0] = value + arg;

sigch();
if (read_ch == ',') next_ch();
sigch();
if (!isdigit(read_ch) && read_ch != '+' && read_ch != '-')
  {
  curmovt->gracespacing[1] = wasrelative?
    curmovt->gracespacing[1] + arg :
    curmovt->gracespacing[0];
  return;
  }

value = (read_ch == '+' || read_ch == '-')? curmovt->gracespacing[1] : 0;
if (!read_expect_integer(&arg, TRUE, TRUE)) return;
curmovt->gracespacing[1] = value + arg;
}


/*************************************************


static void
halvenotes(void)
{
if (main_notenum > 1) main_notenum /= 2; else main_noteden *= 2;
curmovt->time = read_scaletime(curmovt->time);
}


/*************************************************


static void
hyphenstring(void)
{
if (read_plainstring()) curmovt->hyphenstring = store_copystring(read_word);
  else error_moan(ERR10, "String");
}


/*************************************************


static void
justify(void)
{
int yield = 0;
for (;;)
  {
  sigch();
  if (isalpha(read_ch))
    {
    uschar *backupptr = read_chptr;
    int  backup = read_ch;
    next_word();
    if (Ustrcmp(read_word, "top") == 0)         yield |= just_top;
    else if (Ustrcmp(read_word, "bottom") == 0) yield |= just_bottom;
    else if (Ustrcmp(read_word, "left") == 0)   yield |= just_left;
    else if (Ustrcmp(read_word, "right") == 0)  yield |= just_right;
    else if (Ustrcmp(read_word, "all") == 0)    yield |= just_all;
    else { read_chptr = backupptr; read_ch = backup; break; }
    }
  else break;
  }
curmovt->justify = yield;
}


/*************************************************


/* After setting the key for this movement, ensure the relevant stave data is
fudged, in case there are transposed text strings in the subsequent header
lines. The transpose value will already be fudged. */

static void
key(void)
{
curmovt->key = read_key();
stave_key = curmovt->key;
stave_key_tp = transpose_key(stave_key, stave_transpose);
}


/*************************************************


static void
keytranspose(void)
{
int i, oldkey;
keytransposestr *k;

oldkey = read_key();
if (oldkey == N_key)
  {
  error_moan(ERR10, "Transposable key signature");
  return;
  }

for (k = main_keytranspose; k != NULL; k = k->next)
  {
  if (k->oldkey == oldkey) break;
  }

if (k == NULL)
  {
  k = store_Xget(sizeof(keytransposestr));
  k->oldkey = oldkey;
  k->next = main_keytranspose;
  main_keytranspose = k;
  for (i = 0; i < 12; i++)
    {
    k->newkeys[i] = -1;
    k->letterchanges[i] = -1;
    }
  }

for (;;)
  {
  int sign = 1;
  next_sigch();
  if (read_ch == '-')
    {
    sign = -1;
    next_ch();
    }
  else if (!isdigit(read_ch)) break;

  i = read_integer(FALSE) * sign;
  while (i > 11) i -= 12;
  while (i < 0) i += 12;
  sigch();

  if (read_ch != '=')
    {
    error_moan(ERR10, "'='");
    goto ERROR;
    }
  next_ch();
  k->newkeys[i] = read_key();

  if (read_ch != '/')
    {
    error_moan(ERR10, "'/'");
    goto ERROR;
    }
  else
    {
    int x;
    next_ch();
    if (!read_expect_integer(&x, FALSE, TRUE)) goto ERROR;
    if (x < -6 || x > 6)
      {
      error_moan(ERR10, "Number in the range -6 to +6");
      goto ERROR;
      }
    if (abs(x) > i + 1)
      {
      error_moan(ERR146, x, i);
      goto ERROR;
      }
    k->letterchanges[i] = x;
    }
  }

return;

ERROR:
k->newkeys[i] = oldkey;
k->letterchanges[i] = 0;
return;
}



/*************************************************


static void
landscape(void)
{
int temp = curmovt->truelinelength;
curmovt->truelinelength = main_truepagelength;
main_truepagelength = temp;
if (opt_sheetsize == sheet_A5) main_truepagelength -= 28000;

temp = main_sheetwidth;
main_sheetwidth = main_sheetheight;
main_sheetheight = temp;
opt_landscape = TRUE;
if (main_lastmovement != 1) error_moan(ERR20);
}


/*************************************************


static void
layout(void)
{
int *temp = store_Xget(LAYOUT_MAXSIZE * 2 * sizeof(int));
int stack[20];
int ptr = 0;
int level = 0;

for (;;)
  {
  int value;
  sigch();
  if (!isdigit(read_ch))
    {
    if (ptr == 0 || level > 0) { error_moan(ERR10, "Number"); return; }

    /* Final item is always repeat back to start */

    temp[ptr++] = lv_repeatptr;
    temp[ptr++] = 0;

    /* Save in correct size piece of store */

    curmovt->layout = store_Xget(ptr*sizeof(int));
    memcpy(curmovt->layout, temp, ptr * sizeof(int));
    store_free(temp);
    return;
    }

  /* Value must be > 0 */

  read_expect_integer(&value, FALSE, FALSE);
  if (value == 0)
    {
    error_moan(ERR16, "Zero value changed to 1");
    value = 1;
    }

  /* If number followed by '(' it is a repeat count */

  sigch();
  if (read_ch == '(')
    {
    next_ch();
    temp[ptr++] = lv_repeatcount;
    temp[ptr++] = value;
    stack[level++] = ptr;
    }

  /* Else it is a barcount value, with varying terminators. If none of the
  specials, it does nothing, and another number will be a continuation, while
  anything else is the next directive. There may be a number of these
  terminators. */

  else
    {
    temp[ptr++] = lv_barcount;
    temp[ptr++] = value;

    for (;;)
      {
      if (read_ch == ',') { next_sigch(); break; }

      /* Close bracket is the end of a repeat. Check nesting. It can be followed
      by comma, semicolon, or another bracket. */

      else if (read_ch == ')')
        {
        if (level <= 0) { error_moan(ERR10, "Bracket not"); return; } else
          {
          temp[ptr++] = lv_repeatptr;
          temp[ptr++] = stack[--level];
          }
        next_sigch();
        }

      /* Semicolon generates a new page item */

      else if (read_ch == ';')
        {
        temp[ptr++] = lv_newpage;
        next_sigch();
        }

      /* Anything else, just carry on with the big loop */

      else break;
      }
    }
  }
}


/*************************************************


/* The table sets pointers so the value goes into the curmovt->ledger variable.
Translate 0/1 into the old/new ledger characters. */

static void
ledgerstyle(void)
{
movt_intproc();
curmovt->ledger = (curmovt->ledger == 0)? '=' : 184;
}


/*************************************************


/* Read the definition of a custom key signature. We preserve the accidentals
specified for each line or space, and also the order in which they are defined.
A maximum of 10 are supported. */

static void
makekey(void)
{
int i, j, n;

sigch();
if (toupper(read_ch) != 'X' ||
    (next_ch(), !read_expect_integer(&n, FALSE, FALSE)) ||
    n == 0 || n > MAX_XKEYS)
  {
  error_moan(ERR144, MAX_XKEYS);
  return;
  }

n--;  /* Now in range 0-9 */
for (i = 0; i < 10; i++) main_xkeys[n][i] = 0;  /* Reset all accidentals */

for (j = 0; j < 10; j++)
  {
  int ac;

  sigch();
  switch(read_ch)
    {
    case '#':
    next_ch();
    if (read_ch == '#')
      {
      ac = ac_dsharp;
      next_ch();
      }
    else
      {
      ac = ac_sharp;
      if (read_ch == '-')
        {
        ac |= 0x80;
        next_ch();
        }
      }
    break;

    case '


:
    next_ch();
    if (read_ch == '


)
      {
      ac = ac_dflat;
      next_ch();
      }
    else
      {
      ac = ac_flat;
      if (read_ch == '-')
        {
        ac |= 0x80;
        next_ch();
        }
      }
    break;

    case '%':
    ac = ac_natural;
    next_ch();
    break;

    default:
    if (j < 10) main_xkeyorder[n][j] = 255;  /* Mark end of order list */
    return;
    }

  if (!read_expect_integer(&i, FALSE, FALSE)) return;
  if (i > 9)
    {
    error_moan(10, "Number between 0 and 9");
    return;
    }

  main_xkeys[n][i] = ac;
  main_xkeyorder[n][j] = i;
  }
}


/*************************************************


static void
maxbeamslope(void)
{
if (!read_expect_integer(&curmovt->maxbeamslope1, TRUE, FALSE)) return;
if (!read_expect_integer(&curmovt->maxbeamslope2, TRUE, FALSE)) return;
}


/*************************************************


/* Local subroutine to make a copy of a map if this is the first time it's been
updated in this movement. */

static void
copy_midi_map(int flag, int size, uschar **anchor)
{
if ((read_headmap & flag) == 0)
  {
  uschar *new = store_Xget(size);
  memcpy(new, *anchor, size);
  *anchor = new;
  read_headmap |= flag;
  }
}

/* The main function */

static void
midichannel(void)
{
int channel;

debug_printf("midichannel start\n");

/* A channel number is always expected */

if (!read_expect_integer(&channel, FALSE, FALSE)) return;

if (channel < 1 || channel > MIDI_MAXCHANNEL)
  {
  error_moan(ERR109, "channel", MIDI_MAXCHANNEL);
  channel = 1;
  }

/* Deal with an optional voice setting */

if (read_plainstring())
  {
  int voicenumber;
  if (read_word[0] == 0) voicenumber = 129; else  /* => don't do MIDI voice setting */
    {
    if (read_word[0] == '#') voicenumber = Uatoi(read_word+1);
      else voicenumber = read_getmidinumber(midi_voicenames, read_word, US"voice");
    if (voicenumber < 1 || voicenumber > 128)
      {
      if (midi_filename != NULL) error_moan(ERR109, "voice", 128);
      voicenumber = 1;
      }
    }

  copy_midi_map(rh_midivoice, MIDI_MAXCHANNEL, &(curmovt->midi_voice));
  curmovt->midi_voice[channel-1] = voicenumber - 1;

  /* There may be an optional volume setting */

  if (read_ch == '/')
    {
    int vol;
    next_ch();
    if (read_expect_integer(&vol, FALSE, FALSE))
      {
      if (vol > 15) error_moan(ERR10, "Number between 0 and 15"); else
        {
        copy_midi_map(rh_midivolume, MIDI_MAXCHANNEL, &(curmovt->midi_volume));
        curmovt->midi_volume[channel-1] = vol;
        }
      }
    }
  }

/* Deal with an optional stave list */

sigch();
if (isdigit(read_ch))
  {
  int i;
  int pitch = 0;

  copy_midi_map(rh_midichannel, MAX_STAVE+1, &(curmovt->midi_channel));

  movt_mapproc();    /* read stave list into read_map */

  /* Deal with optional 'pitch' forcing */

  if (read_plainstring())
    {
    if (read_word[0] == 0) pitch = 0;  /* => don't do MIDI pitch forcing */
      else if (read_word[0] == '#') pitch = Uatoi(read_word+1);
        else pitch = read_getmidinumber(midi_percnames, read_word, US"percussion instrument");
    copy_midi_map(rh_midinote, MAX_STAVE+1, &(curmovt->midi_note));
    }

  /* Now update the per-stave data */

  for (i = 1; i <= MAX_STAVE; i++)
    {
    if (mac_teststave(read_map, i))
      {
      if (pitch) curmovt->midi_note[i] = pitch;
      curmovt->midi_channel[i] = channel;
      }
    }
  }

debug_printf("midichannel end\n");
}


/*************************************************


static void
midistart(void)
{
int max = 0;
int count = 0;
int *temp = NULL;

for (;;)
  {
  int value;
  sigch();
  if (!isdigit(read_ch))
    {
    if (count == 0) { error_moan(ERR10, "Number"); return; }
    temp[0] = count;
    curmovt->midi_start = temp;
    return;
    }

  /* Get more store if needed */

  if (++count > max)
    {
    int newmax = max + 20;
    int *new = store_Xget((newmax + 1) * sizeof(int));
    if (max > 0)
      {
      memcpy(new, temp, (max+1) * sizeof(int));
      store_free(temp);
      }
    temp = new;
    max = newmax;
    }

  read_expect_integer(&value, FALSE, FALSE);
  temp[count] = value;
  }
}


/*************************************************


static void
notespacing(void)
{
int i;
sigch();
if (read_ch == '*')
  {
  int f;
  next_ch();
  if (!read_expect_integer(&f, TRUE, FALSE)) return;
  if (read_ch == '/')
    {
    int d;
    next_ch();
    if (!read_expect_integer(&d, TRUE, FALSE)) return;
    f = mac_fdiv(f, d);
    }
  for (i = 0; i < 8; i++)
    curmovt->notespacing[i] = (f * curmovt->notespacing[i])/1000;
  }

else
  {
  for (i = 0; i < 8; i++)
    {
    if (!read_expect_integer(main_notespacing+i, TRUE, FALSE)) return;
    curmovt->notespacing[i] = main_notespacing[i];
    sigch();
    if (read_ch == ',') next_ch();
    }
  }
}



/*************************************************


/* This used to be a standard boolean, but it's now extended to an integer
option called "stretchrule". This is a backwards-compatibility function. */

static void
oldstretchrule(void)
{
opt_stretchrule = 0;
}


/*************************************************


static void
page(void)
{
main_pageinc = 1;
if (main_lastmovement != 1) { error_moan(ERR20); return; }
if (!read_expect_integer(&main_firstpage, FALSE, FALSE)) return;
sigch();
if (!isdigit(read_ch)) return;
read_expect_integer(&main_pageinc, FALSE, FALSE);
}



/*************************************************


static void
playtempo(void)
{
movt_intproc();           /* Read a single number */

/* Now look for additional data giving tempo changes within a movement */

sigch();
if (read_ch == ',') next_sigch();

if (isdigit(read_ch))
  {
  BOOL barerror = FALSE;
  int ii;
  int i = 0;
  int lastbar = 0;
  int *copylist;
  int list[100];

  while (isdigit(read_ch))
    {
    int bar = read_integer(TRUE);
    int tempo;
    if (read_ch != '/')
      {
      error_moan(ERR10, "/");
      break;
      }
    next_ch();
    if (!read_expect_integer(&tempo, FALSE, FALSE)) break;

    if (bar <= lastbar)
      {
      if (!barerror) { error_moan(ERR86); barerror = TRUE; }
      }
    lastbar = bar;

    if (i > 99)
      {
      error_moan(ERR85, 50);
      break;
      }

    list[i++] = bar;
    list[i++] = tempo;

    sigch();
    if (read_ch == ',') next_sigch();
    }

  copylist = store_get((i+1) * sizeof(int));
  for (ii = 0; ii < i; ii++) copylist[ii] = list[ii];
  copylist[ii] = BIGNUMBER;
  curmovt->play_tempo_changes = copylist;
  }
}


/*************************************************


static void
playtranspose(void)
{
sigch();
while (isdigit(read_ch))
  {
  int amount;
  int stave = read_integer(FALSE);
  sigch();

  if (read_ch != '/')
    {
    error_moan(ERR10, "/");
    return;
    }

  next_ch();
  if (!read_expect_integer(&amount, FALSE, TRUE)) return;
  sigch();
  (curmovt->playtranspose)[stave] = amount;
  if (read_ch == ',') next_ch();
  sigch();
  }
}


/*************************************************


static void
playvolume(void)
{
int i, v;
uschar *vv;

if (!read_expect_integer(&v, FALSE, FALSE)) return;  /* Default setting */

if (v > 15)
  {
  error_moan(ERR10, "Number between 0 and 15");
  return;
  }

curmovt->play_volume = vv = store_Xget(MAX_STAVE + 1);
for (i = 1; i <= MAX_STAVE; i++) vv[i] = v;

/* Now look for additional data giving stave volumes */

sigch();
if (read_ch == ',') next_sigch();

while (isdigit(read_ch))
  {
  int stave = read_integer(FALSE);
  if (read_ch != '/')
    {
    error_moan(ERR10, "/");
    break;
    }
  next_ch();
  if (!read_expect_integer(&v, FALSE, FALSE)) break;

  if (v > 15)
    {
    error_moan(ERR10, "Number between 0 and 15");
    break;
    }

  vv[stave] = v;
  sigch();
  if (read_ch == ',') next_sigch();
  }
}


/*************************************************


static void
pmwversion(void)
{
BOOL ok = FALSE;
int c = '=';
int v;

sigch();
if (read_ch == '>' || read_ch == '<' || read_ch == '=')
  {
  c = read_ch;
  next_ch();
  }
if (!read_expect_integer(&v, TRUE, FALSE)) return;

switch (c)
  {
  case '<': ok = version_fixed < v; break;
  case '=': ok = version_fixed == v; break;
  case '>': ok = version_fixed > v; break;
  }

if (!ok) error_moan(ERR28, c, v, version_fixed);
}


/*************************************************


/* Local subroutine to deal with one string possibly followed by /s and a
number.

Arguments:
  s          pointer to where to put a pointer to the string
  offset     pointer to where to put the font offset

Returns:     TRUE if all goes well; FALSE on error


static BOOL
ptstring(uschar **s, uschar *offset)
{


if (read_ch == '/')
  {
  int fo;
  next_ch();
  if (read_ch == 's')
    {
    next_ch();
    if (!read_expect_integer(&fo, FALSE, FALSE)) return FALSE;
    if ((fo -= 1) >= MaxFontSizes) error_moan(ERR39, MaxFontSizes);
    }
  else
    {
    error_moan(ERR10, "/s");
    return FALSE;
    }
  *offset = fo;
  }
else *offset = ff_offset_ts;

return TRUE;
}


/* The actual routine */

static void
printtime(void)
{
ptimestr *p = store_Xget(sizeof(ptimestr));
p->next = main_printtime;
p->movt_number = main_lastmovement;
main_printtime = p;
if ((p->time = read_time()) == 0) return;
if (!ptstring(&(p->top), &(p->offsettop))) return;
(void)ptstring(&(p->bot), &(p->offsetbot));
}


/*************************************************


static void
printkey(void)
{
pkeystr *p = store_Xget(sizeof(pkeystr));
p->next = main_printkey;
p->movt_number = main_lastmovement;
main_printkey = p;
p->key = read_key();
p->clef = read_clef();
p->string = string_check(string_read());
sigch();
p->cstring = (read_ch == '"')? string_check(string_read()) : US"";
}


/*************************************************


static void
pssetup(void)
{
if (main_lastmovement != 1) error_moan(ERR20); else
  {
  headstr *h = main_pssetup;
  headstr **hh = &main_pssetup;

  while (h != NULL)
    {
    hh = &(h->next);
    h = *hh;
    }

  if (read_plainstring())
    {
    h = store_Xget(sizeof(headstr));
    h->next = NULL;
    h->size = 0;
    h->text = store_copystring(read_word);
    h->spaceabove = 0;
    *hh = h;
    }

  else error_moan(ERR10, "String");
  }
}


/*************************************************


static void
rehearsalmarks(void)
{
sigch();

while (isalpha(read_ch))
  {
  next_word();
  sigch();
  if (Ustrcmp(read_word, "linestartleft") == 0 ||
      Ustrcmp(read_word, "nolinestartleft") == 0)
    {
    curmovt->rehearsallsleft = (read_word[0] == 'l');
    continue;
    }

  if (Ustrcmp(read_word, "boxed") == 0) curmovt->rehearsalstyle = text_box;
    else if (Ustrcmp(read_word, "ringed") == 0) curmovt->rehearsalstyle = text_ring;
      else if (Ustrcmp(read_word, "plain") == 0) curmovt->rehearsalstyle = 0;
        else error_moan(ERR10, "\"boxed\", \"ringed\", or \"plain\"");
  break;
  }

set_fontsize(oo(fontsizestr, fontsize_rehearse), TRUE);
set_fontname(oo(movtstr, font_rehearse));
}


/*************************************************


static void
sheetdim(void)
{
opt_sheetsize = sheet_unknown;
opt_intproc();
}


/*************************************************


static void
sheetsize(void)
{
next_word();
if (Ustrcmp(read_word, "a4") == 0 || Ustrcmp(read_word, "A4") == 0)
  {
  curmovt->truelinelength = 480000;
  main_truepagelength = 720000;
  main_sheetwidth = 595000;
  main_sheetheight = 842000;
  opt_sheetsize = sheet_A4;
  }
else if (Ustrcmp(read_word, "a3") == 0 || Ustrcmp(read_word, "A3") == 0)
  {
  curmovt->truelinelength = 730000;
  main_truepagelength = 1060000;
  main_sheetwidth = 842000;
  main_sheetheight = 1190000;
  opt_sheetsize = sheet_A3;
  }
else if (Ustrcmp(read_word, "a5") == 0 || Ustrcmp(read_word, "A5") == 0)
  {
  curmovt->truelinelength = 366000;
  main_truepagelength = 480000;
  main_sheetwidth = 421000;
  main_sheetheight = 595000;
  opt_sheetsize = sheet_A5;
  }
else if (Ustrcmp(read_word, "b5") == 0 || Ustrcmp(read_word, "B5") == 0)
  {
  curmovt->truelinelength = 420000;
  main_truepagelength = 590000;
  main_sheetwidth = 499000;
  main_sheetheight = 709000;
  opt_sheetsize = sheet_B5;
  }
else if (Ustrcmp(read_word, "letter") == 0)
  {
  curmovt->truelinelength = 500000;
  main_truepagelength = 670000;
  main_sheetwidth = 612000;
  main_sheetheight = 792000;
  opt_sheetsize = sheet_letter;
  }
else error_moan(ERR10, "\"A3\", \"A4\", \"A5\", \"B5\", or \"letter\"");
if (main_lastmovement != 1) error_moan(ERR20);
}


/*************************************************


static void
startbracketbar(void)
{
sigch();
if (isalpha(read_ch))
  {
  next_word();
  if (Ustrcmp(read_word, "join") == 0) curmovt->startjoin = TRUE;
    else if (Ustrcmp(read_word, "nojoin") == 0) curmovt->startjoin = FALSE;
      else { error_moan(ERR10, "\"join\" or \"nojoin\""); return; }
  }

read_expect_integer(&curmovt->startbracketbar, FALSE, FALSE);
}


/*************************************************


static void
stavesize(void)
{
int *stavesizes = store_Xget((MAX_STAVE+1)*sizeof(int));
memcpy(stavesizes, curmovt->stavesizes, (MAX_STAVE+1)*sizeof(int));
curmovt->stavesizes = stavesizes;

sigch();
while (isdigit(read_ch))
  {
  int size;
  int stave = read_integer(FALSE);
  if (stave > MAX_STAVE) { error_moan(ERR22, MAX_STAVE+1); stave = 1; }
  if (read_ch != '/') { error_moan(ERR10, "/"); return; }
  next_ch();
  if (!read_expect_integer(&size, TRUE, FALSE)) return;
  stavesizes[stave] = size;
  sigch();
  if (read_ch == ',') next_sigch();
  }
}


/*************************************************


static void
stavespacing(void)
{
BOOL first = TRUE;
int i;
int *newsp = store_Xget((MAX_STAVE+1) * sizeof(int));
int *newen = store_Xget((MAX_STAVE+1) * sizeof(int));
usint done[STAVE_BITVEC_SIZE];

newsp[0] = newen[0] = 0;
mac_initstave(done, 0);

for (i = 1; i <= MAX_STAVE; i++)
  {
  newsp[i] = 44000;
  newen[i] = 0;
  }

sigch();
while (isdigit(read_ch))
  {
  int space;
  int ensure = 0;
  int stave = read_integer(FALSE);

  sigch();

  if (read_ch != '/')
    {
    if (first)
      {
      space = stave*1000;
      if (read_ch == '.') space += read_integer(TRUE);
      for (i = 1; i <= MAX_STAVE; i++) newsp[i] = space;
      goto NEXT;
      }
    else
      {
      error_moan(ERR10, "/");
      return;
      }
    }

  next_ch();
  if (!read_expect_integer(&space, TRUE, FALSE)) return;
  sigch();
  if (read_ch == '/')
    {
    ensure = space;
    next_ch();
    if (!read_expect_integer(&space, TRUE, FALSE)) return;
    }

  if (stave == 0) error_moan(ERR107); else
    {
    if (mac_teststave(done, stave)) error_moan(ERR106, stave, US"stavespacing");
    mac_setstave(done, stave);
    newsp[stave] = space;
    newen[stave] = ensure;
    }

NEXT:

  if (read_ch == ',') next_ch();
  sigch();
  first = FALSE;
  }

curmovt->stave_spacing = newsp;
curmovt->stave_ensure = newen;
}


/*************************************************


static void
startlinespacing(void)
{
startlinestr *p = store_Xget(sizeof(startlinestr));
p->clefspace = p->keyspace = p->timespace = p->notespace = 0;

sigch();
if (isdigit(read_ch) || read_ch == '-' || read_ch == '+')
  {
  (void)read_expect_integer(&(p->clefspace), TRUE, TRUE);
  if (read_ch == ',') next_ch();
  sigch();
  }
if (isdigit(read_ch) || read_ch == '-' || read_ch == '+')
  {
  (void)read_expect_integer(&(p->keyspace), TRUE, TRUE);
  if (read_ch == ',') next_ch();
  sigch();
  }
if (isdigit(read_ch) || read_ch == '-' || read_ch == '+')
  {
  (void)read_expect_integer(&(p->timespace), TRUE, TRUE);
  if (read_ch == ',') next_ch();
  sigch();
  }
if (isdigit(read_ch) || read_ch == '-' || read_ch == '+')
  {
  (void)read_expect_integer(&(p->notespace), TRUE, TRUE);
  }

curmovt->startline = p;
}


/*************************************************


static void
stemswap(void)
{
next_word();
if (Ustrcmp(read_word, "up") == 0) curmovt->stemswaptype = stemswap_up;
else if (Ustrcmp(read_word, "down") == 0) curmovt->stemswaptype = stemswap_down;
else if (Ustrcmp(read_word, "left") == 0) curmovt->stemswaptype = stemswap_left;
else if (Ustrcmp(read_word, "right") == 0) curmovt->stemswaptype = stemswap_right;
else error_moan(ERR10, "\"up\", \"down\", \"left\", or \"right\"");
}


/*************************************************


static void
stemswaplevel(void)
{
int i, x;
int *new = store_Xget((MAX_STAVE+1) * sizeof(int));
for (i = 0; i <= MAX_STAVE; i++) new[i] = P_3L;

if (!read_expect_integer(&x, FALSE, TRUE)) return;

if (x > 0 && read_ch == '/')
  {
  for (;;)
    {
    int y;
    next_ch();
    if (!read_expect_integer(&y, FALSE, TRUE)) return;
    new[x] = P_3L + y*2;
    if (read_ch == ',') next_ch();
    sigch();
    if (!isdigit(read_ch)) break;
    x = read_integer(FALSE);
    if (read_ch != '/') { error_moan(ERR10, "/"); return; }
    }
  }

else for (i = 0; i <= MAX_STAVE; i++) new[i] = P_3L + x*2;

curmovt->stemswaplevel = new;
}


/************************************************


static void
stemlengths(void)
{
int i;
sigch();
for (i = 2; i < 8 && isdigit(read_ch); i++)
  {
  (void)read_expect_integer(&(curmovt->tailadjusts[i]), TRUE, TRUE);
  if (read_ch == ',') next_ch();
  sigch();
  }
}


/*************************************************


/* This also handles musicfont, if arg1 is non-zero. */

static void
textfont(void)
{
BOOL include = FALSE;
int fontid, n;

/* Font selection is permitted only in the first movement */

if (main_lastmovement != 1) { error_moan(ERR20); return; }

/* If font id given (a music font), use it, else read a font word or "extra
<n>", optionally followed by "include". */

if (read_dir->arg1 != 0) fontid = read_dir->arg1; else
  {
  if ((fontid = font_fontword(FALSE)) < 0) return;
  sigch();
  if (read_ch != '"')
    {
    uschar *save_chptr = read_chptr;
    next_word();
    if (Ustrcmp(read_word, "include") == 0) include = TRUE;
      else read_chptr = save_chptr;
    }
  }

/* Read the first font name, which must be present, and the
optional second name for the PostScript font. */

if (!read_plainstring()) { error_moan(ERR10, "String"); return; }
if (read_ch == ',') next_ch();

/* See if is already in the font list. If it is, we set its number in the font
table, enable include if required, and return. If not, we set it up as a new
font. */

n = font_search(read_word);
if (n >= 0)
  {
  font_table[fontid] = n;
  if (include) font_List[n].include = TRUE;
  }
else
  {
  fontstr *fs;
  if (font_count >= MAX_FONTS) { error_moan(ERR26, MAX_FONTS); return; }
  fs = &(font_List[font_count]);
  fs->psname = store_copystring(read_word);
  fs->widths = NULL;
  fs->high_tree = NULL;
  fs->utr = NULL;
  fs->utrcount = 0;
  fs->heights = NULL;
  fs->kerns = NULL;
  fs->kerncount = -1;
  fs->stdencoding = fs->fixedpitch = fs->hasfi = FALSE;
  fs->include = include;
  font_table[fontid] = font_count;
  font_loadtables(font_count++);
  }

/* Ensure both music fonts are the same */

if (fontid == font_mf) font_table[font_mu] = font_table[font_mf];
}


/*************************************************


/* Note: set_fontsize() does nothing if the next character is not a digit. */

static void
textsizes(void)
{
int i;
for (i = 0; i < MaxFontSizes; i++)
  {
  set_fontsize(oo(fontsizestr,fontsize_text) + i*sizeof(int), TRUE);
  if (read_ch == ',') next_ch();
  }
sigch();
if (isdigit(read_ch))
  {
  error_moan(ERR143, MaxFontSizes);
  while (isdigit(read_ch) || isspace(read_ch) || read_ch == ',') next_ch();
  }
}


/*************************************************


static void
timeproc(void)
{
int t = read_time();            /* returns 0 after giving error */
if (t != 0) curmovt->time = t;
}



/*************************************************


static void
transpose(void)
{
int temp = curmovt->transpose;
if (temp == no_transpose) temp = 0;
movt_intproc();
curmovt->transpose += temp;
if (abs(curmovt->transpose) > max_transpose)
  error_moan(ERR139, (temp == 0)? "T":"Accumulated t", curmovt->transpose,
    max_transpose);  /* Hard error */

/* In case this is followed by heading/footing lines that contain transposed
text, fudge up some fake stave values. The stave_key will already be set. */

stave_transpose = curmovt->transpose;
stave_key_tp = transpose_key(stave_key, stave_transpose);
}


/*************************************************


static void
transposedkey(void)
{
int oldkey = read_key();
next_word();
if (Ustrcmp(read_word, "use") != 0) error_moan(ERR10, "\"use\""); else
  {
  int newkey = read_key();
  trkeystr *k = store_Xget(sizeof(trkeystr));
  k->oldkey = oldkey;
  k->newkey = newkey;
  k->next = main_transposedkeys;
  main_transposedkeys = k;
  }
}



/*************************************************


static void
transposedacc(void)
{
next_word();
if (Ustrcmp(read_word, "force") == 0) main_transposedaccforce = TRUE; else
  if (Ustrcmp(read_word, "noforce") == 0) main_transposedaccforce = FALSE;
    else error_moan(ERR10, "\"force\" or \"noforce\"");
}


/*************************************************


static void
trillstring(void)
{
sigch();
if (isdigit(read_ch)) set_fontsize(oo(fontsizestr,fontsize_trill), TRUE);
if (read_plainstring()) curmovt->trillstring = store_copystring(read_word);
else error_moan(ERR10, "String");
}



/*************************************************


/* The function for reading a draw list is held separately for convenience. */

static dirstr read_headlist[] = {
  { "accadjusts",       accadjusts,     0, 0 },
  { "accspacing",       accspacing,     0, 0 },
  { "b2pffont",         b2pffont,       0, 0 },
  { "bar",              movt_intproc,   oo(movtstr,baroffset), int_u+int_less1 },
  { "barcount",         movt_intproc,   oo(movtstr,maxbarcount),  int_u },
  { "barlinesize",      movt_intproc,   oo(movtstr,barlinesize), int_u+int_f },
  { "barlinespace",     barlinespace,   oo(movtstr,barlinespace), int_u+int_f },
  { "barlinestyle",     movt_intproc,   oo(movtstr,barlinestyle), int_u },
  { "barnumberlevel",   barnumberlevel, oo(movtstr,barno_level), int_rs+int_f },
  { "barnumbers",       barnumbers,     0, 0 },
  { "beamendrests",     movt_cboolproc, oo(movtstr,beamendrests), TRUE },
  { "beamflaglength",   movt_intproc,   oo(movtstr,beamflaglength), int_u+int_f },
  { "beamthickness",    movt_intproc,   oo(movtstr,beamdepth), int_u+int_f },
  { "bottommargin",     movt_intproc,   oo(movtstr,botmargin), int_u+int_f },
  { "brace",            movt_listproc,  oo(movtstr,bracelist), 0 },
  { "bracestyle",       bracestyle,     oo(movtstr,bracestyle), int_u },
  { "bracket",          movt_listproc,  oo(movtstr,bracketlist), 0 },
  { "breakbarlines",    breakbarlines,  oo(movtstr,breakbarlines), -1 },
  { "breakbarlinesx",   breakbarlinesx, oo(movtstr,breakbarlines), -1 },
  { "breveledgerextra", movt_intproc,   oo(movtstr,breveledgerextra),int_u+int_f},
  { "breverests",       movt_cboolproc, oo(movtstr,breverests), TRUE },
  { "caesurastyle",     caesurastyle,   oo(movtstr,caesurastyle), int_u },
  { "check",            movt_cboolproc, oo(movtstr,check), TRUE },
  { "checkdoublebars",  movt_cboolproc, oo(movtstr,checkdoublebars), TRUE },
  { "clefsize",         clefsize,       0, 0 },
  { "clefstyle",        clefstyle,      oo(movtstr,clefstyle), int_u },
  { "clefwidths",       clefwidths,     0, 0 },
  { "codemultirests",   movt_cboolproc, oo(movtstr,codemultirests), TRUE },
  { "copyzero",         copyzero,       0, 0 },
  { "cuegracesize",     movt_fszproc,   oo(fontsizestr,fontsize_cuegrace), FALSE },
  { "cuesize",          movt_fszproc,   oo(fontsizestr,fontsize_cue), FALSE },
  { "dotspacefactor",   movt_intproc,   oo(movtstr,dotspacefactor), int_u+int_f },
  { "doublenotes",      doublenotes,    0, 0 },
  { "draw",             read_draw,      0, 0 },
  { "endlinesluradjust",movt_intproc,   oo(movtstr,endlinesluradjust), int_f },
  { "endlineslurstyle", movt_intproc,   oo(movtstr,endlineslurstyle), int_u },
  { "endlinetieadjust", movt_intproc,   oo(movtstr,endlinetieadjust), int_f },
  { "endlinetiestyle",  movt_intproc,   oo(movtstr,endlinetiestyle), int_u },
  { "extenderlevel",    movt_intproc,   oo(movtstr,extenderlevel), int_f },
  { "fbsize",           movt_fszproc,   oo(fontsizestr,fontsize_text)+ff_offset_fbass*sizeof(int), TRUE },
  { "footing",          movt_headproc,  oo(movtstr,footing), rh_footing },
  { "footnotesep",      movt_intproc,   oo(movtstr,footnotesep), int_f },
  { "footnotesize",     movt_fszproc,   oo(fontsizestr,fontsize_footnote), TRUE },
  { "gracesize",        movt_fszproc,   oo(fontsizestr,fontsize_grace), FALSE },
  { "gracespacing",     gracespacing,   0, 0 },
  { "gracestyle",       movt_intproc,   oo(movtstr,gracestyle), int_u },
  { "hairpinlinewidth", movt_intproc,   oo(movtstr,hairpinlinewidth), int_rs+int_f },
  { "hairpinwidth",     movt_intproc,   oo(movtstr,hairpinwidth), int_rs+int_f },
  { "halfflatstyle",    movt_intproc,   oo(movtstr,hflatstyle), int_u },
  { "halfsharpstyle",   movt_intproc,   oo(movtstr,hsharpstyle), int_u },
  { "halvenotes",       halvenotes,     0, 0 },
  { "heading",          movt_headproc,  oo(movtstr,heading), rh_heading },
  { "hyphenstring",     hyphenstring,   0, 0 },
  { "hyphenthreshold",  movt_intproc,   oo(movtstr,hyphenthreshold), int_rs+int_f },
  { "join",             movt_listproc,  oo(movtstr,joinlist), 0 },
  { "joindotted",       movt_listproc,  oo(movtstr,joindottedlist), 0 },
  { "justify",          justify,        0, 0 },
  { "key",              key,            0, 0 },
  { "keydoublebar",     movt_cboolproc, oo(movtstr,keydoublebar), TRUE },
  { "keysinglebar",     movt_cboolproc, oo(movtstr,keydoublebar), FALSE },
  { "keytranspose",     keytranspose,   0, 0 },
  { "keywarn",          movt_cboolproc, oo(movtstr,keywarn), TRUE },
  { "landscape",        landscape,      0, 0 },
  { "lastfooting",      movt_headproc,  oo(movtstr,lastfooting), rh_lastfooting },
  { "layout",           layout,         0, 0 },
  { "ledgerstyle",      ledgerstyle,    oo(movtstr,ledger), int_u },
  { "leftmargin",       movt_intproc,   oo(movtstr,leftmargin), int_f },
  { "linelength",       movt_intproc,   oo(movtstr,truelinelength), int_rs+int_f },
  { "longrestfont",     movt_fontproc,  oo(movtstr,font_longrest), oo(fontsizestr, fontsize_restct) },
  { "magnification",    opt_intproc,    glob_magnification, int_u+int_f },
  { "makekey",          makekey,        0, 0 },
  { "maxbeamslope",     maxbeamslope,   0, 0 },
  { "maxvertjustify",   opt_intproc,    glob_maxvertjustify, int_u+int_f },
  { "midichannel",      midichannel,    0, 0 },
  { "midifornotesoff",  opt_boolproc,   glob_midifornotesoff, TRUE },
  { "midistart",        midistart,      0, 0 },
  { "miditempo",        playtempo,      oo(movtstr,play_tempo), int_u },
  { "miditranspose",    playtranspose,  0, 0 },
  { "midivolume",       playvolume,     0, 0 },
  { "midkeyspacing",    movt_intproc,   oo(movtstr,keyspacing), int_f },
  { "midtimespacing",   movt_intproc,   oo(movtstr,timespacing), int_f },
  { "musicfont",        textfont,       font_mf, 0 },
  { "nobeamendrests",   movt_cboolproc, oo(movtstr,beamendrests), FALSE },
  { "nocheck",          movt_cboolproc, oo(movtstr,check), FALSE },
  { "nocheckdoublebars",movt_cboolproc, oo(movtstr,checkdoublebars), FALSE },
  { "nocodemultirests", movt_cboolproc, oo(movtstr,codemultirests), FALSE },
  { "nokerning",        opt_boolproc,   glob_kerning, FALSE },
  { "nokeywarn",        movt_cboolproc, oo(movtstr,keywarn), FALSE },
  { "nosluroverwarnings", movt_cboolproc, oo(movtstr,tiesoverwarnings), FALSE },
  { "nospreadunderlay", movt_cboolproc, oo(movtstr,spreadunderlay), FALSE },
  { "notespacing",      notespacing,    0, 0 },
  { "notime",           movt_cboolproc, oo(movtstr,showtime), FALSE },
  { "notimebase",       movt_cboolproc, oo(movtstr,showtimebase), FALSE },
  { "notimewarn",       movt_cboolproc, oo(movtstr,timewarn), FALSE },
  { "nounderlayextenders",movt_cboolproc, oo(movtstr,underlayextenders), FALSE },
  { "oldbeambreak",     opt_boolproc,   glob_oldbeambreak, TRUE },
  { "oldrestlevel",     opt_boolproc,   glob_oldrestlevel, TRUE },
  { "oldstemlength",    opt_boolproc,   glob_oldstemlength, TRUE },
  { "oldstretchrule",   oldstretchrule, 0, 0 },
  { "overlaydepth",     movt_intproc,   oo(movtstr,overlaydepth), int_f },
  { "overlaysize",      movt_fszproc,   oo(fontsizestr,fontsize_text)+ff_offset_olay*sizeof(int), TRUE },
  { "page",             page,           0, 0 },
  { "pagefooting",      movt_headproc,  oo(movtstr,pagefooting), rh_pagefooting },
  { "pageheading",      movt_headproc,  oo(movtstr,pageheading), rh_pageheading },
  { "pagelength",       opt_intproc,    glob_pagelength, int_rs+int_f },
  { "playtempo",        playtempo,      oo(movtstr,play_tempo), int_u },
  { "playtranspose",    playtranspose,  0, 0 },
  { "playvolume",       playvolume,     0, 0 },
  { "pmwversion",       pmwversion,     0, 0 },
  { "printkey",         printkey,       0, 0 },
  { "printtime",        printtime,      0, 0 },
  { "psfooting",        movt_headproc,  oo(movtstr,footing), rh_footing+rh_ps },
  { "psheading",        movt_headproc,  oo(movtstr,heading), rh_heading+rh_ps },
  { "pslastfooting",    movt_headproc,  oo(movtstr,lastfooting), rh_lastfooting+rh_ps },
  { "pspagefooting",    movt_headproc,  oo(movtstr,pagefooting), rh_pagefooting+rh_ps },
  { "pspageheading",    movt_headproc,  oo(movtstr,pageheading), rh_pageheading+rh_ps },
  { "pssetup",          pssetup,        0, 0 },
  { "rehearsalmarks",   rehearsalmarks, 0, 0 },
  { "repeatbarfont",    movt_fontproc,  oo(movtstr,font_repeat), oo(fontsizestr, fontsize_repno) },
  { "repeatstyle",      movt_intproc,   oo(movtstr,repeatstyle), int_u },
  { "righttoleft",      opt_boolproc,   glob_righttoleft, TRUE },
  { "selectstaff",      movt_mapproc,   oo(movtstr,staves), 0 },
  { "selectstave",      movt_mapproc,   oo(movtstr,staves), 0 },
  { "selectstaves",     movt_mapproc,   oo(movtstr,staves), 0 },
  { "sheetdepth",       sheetdim,       glob_sheetdepth, int_u+int_f },
  { "sheetsize",        sheetsize,      0, 0 },
  { "sheetwidth",       sheetdim,       glob_sheetwidth, int_u+int_f },
  { "shortenstems",     movt_intproc,   oo(movtstr,shorten), int_u+int_f },
  { "sluroverwarnings", movt_cboolproc, oo(movtstr,tiesoverwarnings), TRUE },
  { "smallcapsize",     movt_intproc,   oo(movtstr,smallcapsize), int_u+int_f },
  { "staffsize",        stavesize,      0, 0 },
  { "staffsizes",       stavesize,      0, 0 },
  { "staffspacing",     stavespacing,   0, 0 },
  { "startbracketbar",  startbracketbar, 0, 0 },
  { "startlinespacing", startlinespacing, 0, 0 },
  { "startnotime",      movt_cboolproc, oo(movtstr,startnotime), TRUE},
  { "stavesize",        stavesize,      0, 0 },
  { "stavesizes",       stavesize,      0, 0 },
  { "stavespacing",     stavespacing,   0, 0 },
  { "stemlengths",      stemlengths,    0, 0 },
  { "stemswap",         stemswap,       0, 0 },
  { "stemswaplevel",    stemswaplevel,  0, 0 },
  { "stretchrule",      opt_intproc,    glob_stretchrule, int_u },
  { "suspend",          movt_mapproc,   oo(movtstr,suspend), -1 },
  { "systemgap",        movt_intproc,   oo(movtstr,systemgap), int_u+int_f },
  { "textfont",         textfont,       0, 0 },
  { "textsize",         textsizes,      0, 0 },
  { "textsizes",        textsizes,      0, 0 },
  { "thinbracket",      movt_listproc,  oo(movtstr,thinbracketlist), 0 },
  { "time",             timeproc,       0, 0 },
  { "timebase",         movt_cboolproc, oo(movtstr,showtimebase), TRUE },
  { "timefont",         movt_fontproc,  oo(movtstr,font_time),oo(fontsizestr,fontsize_text)+ff_offset_ts*sizeof(int) },
  { "timewarn",         movt_cboolproc, oo(movtstr,timewarn), TRUE },
  { "topmargin",        movt_intproc,   oo(movtstr,topmargin), int_u+int_f },
  { "transpose",        transpose,      oo(movtstr,transpose), int_s },
  { "transposedacc",    transposedacc,  0, 0 },
  { "transposedkey",    transposedkey,  0, 0 },
  { "trillstring",      trillstring,    0, 0 },
  { "tripletfont",      movt_fontproc,  oo(movtstr,font_triplet),oo(fontsizestr,fontsize_triplet) },
  { "tripletlinewidth", movt_intproc,   oo(movtstr,tripletlinewidth), int_u+int_f },
  { "underlaydepth",    movt_intproc,   oo(movtstr,underlaydepth), int_f },
  { "underlayextenders",movt_cboolproc, oo(movtstr,underlayextenders), TRUE },
  { "underlaysize",     movt_fszproc,   oo(fontsizestr,fontsize_text)+ff_offset_ulay*sizeof(int), TRUE },
  { "underlaystyle",    movt_intproc,   oo(movtstr,underlaystyle), int_u },
  { "unfinished",       movt_cboolproc, oo(movtstr,unfinished), TRUE },
  { "vertaccsize",      movt_fszproc,   oo(fontsizestr,fontsize_vertacc), FALSE }
};

static int read_headsize = sizeof(read_headlist)/sizeof(dirstr);



/*************************************************


/*
Arguments:    none
Returns:      nothing


void
next_heading(void)
{
dirstr *first, *last;

sigch();
if (read_ch == EOF) return;
next_word();

/* A null word is either the end of the heading, or an error */

if (read_word[0] == 0)
  {
  if (read_ch != '[') error_moan(ERR10, "Heading directive");
  return;
  }

/* Look up the word in the list of heading directives and if found, call the
appropriate routine. */

first = read_headlist;
last  = first + read_headsize;

while (last > first)
  {
  int c;
  read_dir = first + (last-first)/2;
  c = Ustrcmp(read_word, read_dir->name);
  if (c == 0)
    {
    (read_dir->proc)();
    return;
    }
  if (c > 0) first = read_dir + 1; else last = read_dir;
  }
error_moan(ERR19, read_word);
}

/* End of read2.c */