/************************************************* * The PMW Music Typesetter - 3rd incarnation * *************************************************/ /* 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 /************************************************* * Global variable list * *************************************************/ /* 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 }; /************************************************* * Local static variables * *************************************************/ static int read_map[STAVE_BITVEC_SIZE]; /* For reading lists of staves */ /************************************************* * Read and set font name, if present * *************************************************/ /* 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; } } } /************************************************* * Read and set font size * *************************************************/ /* 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; } } } } /******************************************************************* * The following functions are referenced in the read_headstring * * vector of heading directives. Each is called when the relevant * * directive is encountered. They all have the same interface: * * read_dir->arg1 and read_dir->arg2 contain up to two arguments * * associated with the directive. There are no arguments to the * * functions themselves, and they do not return any values. * ******************************************************************/ /************************************************* * Deal with plain font size * *************************************************/ static void movt_fszproc(void) { sigch(); if (isdigit(read_ch)) set_fontsize(read_dir->arg1, read_dir->arg2); else error_moan(ERR10, "Number"); } /************************************************* * Deal with heading boolean * *************************************************/ static void movt_cboolproc(void) { *((CBOOL *)((uschar *)curmovt + read_dir->arg1)) = read_dir->arg2; } /************************************************* * Deal with heading font * *************************************************/ 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; } /************************************************* * Deal with global boolean * *************************************************/ static void opt_boolproc(void) { if (main_lastmovement == 1) *global_vars[read_dir->arg1] = read_dir->arg2; else error_moan(ERR20); } /************************************************* * Deal with heading integer * *************************************************/ 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; } /************************************************* * Deal with global integer * *************************************************/ 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); } /************************************************* * Deal with heading stave list * *************************************************/ static void movt_listproc(void) { stave_list **p = (stave_list **)(((uschar *)curmovt) + read_dir->arg1); *p = NULL; 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(); } } /************************************************* * Deal with heading stave map * *************************************************/ 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)); } /************************************************* * Deal with heading/footing text * *************************************************/ /* 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 */ *oldp = new; read_headfootingtext(new, read_dir->arg2); } /************************************************* * Read arguments for heading/footing * *************************************************/ /* 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 "; 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; } } /************************************************* * Accadjusts * *************************************************/ 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; } /************************************************* * Accspacing * *************************************************/ 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; } /************************************************* * Set up font for B2PF processing * *************************************************/ /* 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 */ } /************************************************* * Barlinespace * *************************************************/ static void barlinespace(void) { sigch(); if (read_ch == '*') { curmovt->barlinespace = (int)0x80000000; next_sigch(); } else movt_intproc(); } /************************************************* * Barnumberlevel * *************************************************/ /* A sign is mandatory */ static void barnumberlevel(void) { sigch(); if (read_ch != '+' && read_ch != '-') error_moan(ERR10, "\"+\" or \"-\""); else movt_intproc(); } /************************************************* * Barnumbers * *************************************************/ 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)); } /************************************************* * Bracestyle * *************************************************/ static void bracestyle(void) { movt_intproc(); if (curmovt->bracestyle > 1) { error_moan(ERR37, "Number less than 2"); curmovt->bracestyle = 0; } } /************************************************* * Breakbarlines * *************************************************/ 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; } /************************************************* * Breakbarlinesx * *************************************************/ static void breakbarlinesx(void) { breakbarlines(); curmovt->fullbarend = TRUE; } /************************************************* * Caesurastyle * *************************************************/ static void caesurastyle(void) { movt_intproc(); if (curmovt->caesurastyle > 1) { error_moan(ERR37, "Number less than 2"); curmovt->caesurastyle = 0; } } /************************************************* * Clef size (relative) * *************************************************/ 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"); } /************************************************ * Clef style * ************************************************/ static void clefstyle(void) { movt_intproc(); if (curmovt->clefstyle > 3) error_moan(ERR10, "Number less than 4"); } /************************************************ * Clef widths * ************************************************/ 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; } } /************************************************* * Copyzero * *************************************************/ 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(); } } /************************************************* * Doublenotes * *************************************************/ static void doublenotes(void) { main_notenum *= 2; curmovt->time = read_scaletime(curmovt->time); } /************************************************* * Gracespacing * *************************************************/ /* 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; } /************************************************* * Halvenotes * *************************************************/ static void halvenotes(void) { if (main_notenum > 1) main_notenum /= 2; else main_noteden *= 2; curmovt->time = read_scaletime(curmovt->time); } /************************************************* * Hyphenstring * *************************************************/ static void hyphenstring(void) { if (read_plainstring()) curmovt->hyphenstring = store_copystring(read_word); else error_moan(ERR10, "String"); } /************************************************* * Justify * *************************************************/ 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; } /************************************************* * Key * *************************************************/ /* 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); } /************************************************* * Keytranspose * *************************************************/ 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; } /************************************************* * Landscape * *************************************************/ 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); } /************************************************* * Layout * *************************************************/ 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; } } } } /************************************************* * Ledgerstyle * *************************************************/ /* 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; } /************************************************* * Makekey * *************************************************/ /* 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; } } /************************************************* * Maxbeamslope * *************************************************/ static void maxbeamslope(void) { if (!read_expect_integer(&curmovt->maxbeamslope1, TRUE, FALSE)) return; if (!read_expect_integer(&curmovt->maxbeamslope2, TRUE, FALSE)) return; } /************************************************* * Midichannel * *************************************************/ /* 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"); } /************************************************* * Midistart * *************************************************/ 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; } } /************************************************* * Notespacing * *************************************************/ 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(); } } } /************************************************* * Oldstretchrule * *************************************************/ /* 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; } /************************************************* * Page * *************************************************/ 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); } /************************************************* * Playtempo * *************************************************/ 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; } } /************************************************* * Playtranspose * *************************************************/ 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(); } } /************************************************* * Playvolume * *************************************************/ 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(); } } /************************************************* * PMW version check * *************************************************/ 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); } /************************************************* * Printtime * *************************************************/ /* 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) { *s = string_check(string_read()); 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)); } /************************************************* * Printkey * *************************************************/ 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""; } /************************************************* * Pssetup * *************************************************/ 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"); } } /************************************************* * Rehearsalmarks * *************************************************/ 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)); } /************************************************* * Sheetdepth and sheetwidth * *************************************************/ static void sheetdim(void) { opt_sheetsize = sheet_unknown; opt_intproc(); } /************************************************* * Sheetsize * *************************************************/ 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); } /************************************************* * Startbracketbar * *************************************************/ 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); } /************************************************* * Stavesize * *************************************************/ 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(); } } /************************************************* * Stavespacing * *************************************************/ 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; } /************************************************* * Startlinespacing * *************************************************/ 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; } /************************************************* * Stemswap * *************************************************/ 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\""); } /************************************************* * Stemswaplevel * *************************************************/ 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; } /************************************************ * Stem lengths * ************************************************/ 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(); } } /************************************************* * Textfont * *************************************************/ /* 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 ", 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]; } /************************************************* * Textsizes * *************************************************/ /* 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(); } } /************************************************* * Time * *************************************************/ static void timeproc(void) { int t = read_time(); /* returns 0 after giving error */ if (t != 0) curmovt->time = t; } /************************************************* * Transpose * *************************************************/ 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); } /************************************************* * Transposed key * *************************************************/ 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; } } /************************************************* * Transposed accidental option * *************************************************/ 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\""); } /************************************************* * Trill string * *************************************************/ 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"); } /************************************************* * Heading directive list * *************************************************/ /* 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); /************************************************* * Read next heading directive * *************************************************/ /* 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 */