💾 Archived View for runjimmyrunrunyoufuckerrun.com › src › foreign › abcmidi › parseabc.c captured on 2021-12-17 at 13:26:06.
View Raw
More Information
-=-=-=-=-=-=-
/*
* parseabc.c - code to parse an abc file. This file is used by the
* following 3 programs :
* abc2midi - program to convert abc files to MIDI files.
* abc2abc - program to manipulate abc files.
* yaps - program to convert abc to PostScript music files.
* Copyright (C) 1999 James Allwright
* e-mail: J.R.Allwright@westminster.ac.uk
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
/* Macintosh port 30th July 1996 */
/* DropShell integration 27th Jan 1997 */
/* Wil Macaulay (wil@syndesis.com) */
#define TAB 9
#include "abc.h"
#include "parseabc.h"
#include "music_utils.h"
#include <stdio.h>
#include <stdlib.h>
/* [JM] 2018-02-22 to handle strncasecmp() */
#include <string.h>
/* #define SIZE_ABBREVIATIONS ('Z' - 'H' + 1) [SS] 2016-09-20 */
#define SIZE_ABBREVIATIONS 58
/* [SS] 2015-09-28 changed _snprintf_s to _snprintf */
#ifdef _MSC_VER
#define snprintf _snprintf
#define strncasecmp strnicmp
#endif
#ifdef _MSC_VER
#define ANSILIBS
#define casecmp stricmp
#define _CRT_SECURE_NO_WARNINGS
#else
#define casecmp strcasecmp
#endif
#define stringcmp strcmp
#ifdef __MWERKS__
#define __MACINTOSH__ 1
#endif /* __MWERKS__ */
#ifdef __MACINTOSH__
#define main macabc2midi_main
#define STRCHR
#endif /* __MACINTOSH__ */
/* define USE_INDEX if your C libraries have index() instead of strchr() */
#ifdef USE_INDEX
#define strchr index
#endif
#ifdef ANSILIBS
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#else
extern char *malloc ();
extern char *strchr ();
#endif
int lineno;
int parsing_started = 0;
int parsing, slur;
int ignore_line = 0; /* [SS] 2017-04-12 */
int inhead, inbody;
int parserinchord;
static int ingrace = 0; /* [SS] 2020-06-01 */
int chorddecorators[DECSIZE];
char decorations[] = ".MLRH~Tuv'OPS"; /* 2020-05-01 */
char *abbreviation[SIZE_ABBREVIATIONS];
int num_voices = 0; /* [JA] 2020-10-12 */
int repcheck = 1; /* enables/ disables repeat checking */
/* abc2midi disables repeat checking because it does its own thing */
voice_context_t voicecode[MAX_VOICES];
timesig_details_t master_timesig; /* [JA] 2020-12-10 */
cleftype_t master_clef;
int has_timesig;
int master_unitlen; /* L: field value is 1/unitlen */
int voicenum; /* current voice number */
int has_voice_fields = 0;
int decorators_passback[DECSIZE];
/* this global array is linked as an external to store.c and
* yaps.tree.c and is used to pass back decorator information
* from event_instruction to parsenote.
char inputline[512]; /* [SS] 2011-06-07 2012-11-22 */
char *linestart; /* [SS] 2011-07-18 */
int lineposition; /* [SS] 2011-07-18 */
char timesigstring[16]; /* [SS] 2011-08-19 links with stresspat.c */
int nokey = 0; /* K: none was encountered */
int nokeysig = 0; /* links with toabc.c [SS] 2016-03-03 */
int chord_n, chord_m; /* for event_chordoff */
int fileline_number = 1;
int intune = 1;
int inchordflag; /* [SS] 2012-03-30 */
struct fraction setmicrotone; /* [SS] 2014-01-07 */
int microtone; /* [SS] 2014-01-19 */
int temperament = 0; /* [SS] 2020-06-25 */
extern programname fileprogram;
int oldchordconvention = 0;
char * abcversion = "2.0"; /* [SS] 2014-08-11 */
char lastfieldcmd = ' '; /* [SS] 2014-08-15 */
/* tables mode and modeshift moved to music_utils.c */
int modeminor[10] = { 0, 1, 1,
1, 0, 0, 0, 0, 0, 0
};
int modekeyshift[10] = { 0, 5, 5, 5, 6, 0, 1, 2, 3, 4 };
int *
checkmalloc (bytes)
/* malloc with error checking */
int bytes;
{
int *p;
p = (int *) malloc (bytes);
if (p == NULL)
{
printf ("Out of memory error - malloc failed!\n");
exit (0);
};
return (p);
}
char *
addstring (s)
/* create space for string and store it in memory */
char *s;
{
char *p;
p = (char *) checkmalloc (strlen (s) + 1);
strcpy (p, s);
return (p);
}
/* [SS] 2014-08-16 [SDG] 2020-06-03 */
char * concatenatestring(s1,s2)
char * s1;
char * s2;
{
int len = strlen(s1) + strlen(s2) + 1;
char *p = (char *) checkmalloc(len);
#ifdef NO_SNPRINTF
sprintf(p, "%s%s",s1,s2); /* [SS] 2020-11-01 */
#else
snprintf(p,len, "%s%s",s1,s2);
#endif
return p;
}
void
initvstring (s)
struct vstring *s;
/* initialize vstring (variable length string data structure) */
{
s->len = 0;
s->limit = 40;
s->st = (char *) checkmalloc (s->limit + 1);
*(s->st) = '\0';
}
void
extendvstring (s)
struct vstring *s;
/* doubles character space available in string */
{
char *p;
if (s->limit > 0)
{
s->limit = s->limit * 2;
p = (char *) checkmalloc (s->limit + 1);
strcpy (p, s->st);
free (s->st);
s->st = p;
}
else
{
initvstring (s);
};
}
void
addch (ch, s)
char ch;
struct vstring *s;
/* appends character to vstring structure */
{
if (s->len >= s->limit)
{
extendvstring (s);
};
*(s->st + s->len) = ch;
*(s->st + (s->len) + 1) = '\0';
s->len = (s->len) + 1;
}
void
addtext (text, s)
char *text;
struct vstring *s;
/* appends a string to vstring data structure */
{
int newlen;
newlen = s->len + strlen (text);
while (newlen >= s->limit)
{
extendvstring (s);
};
strcpy (s->st + s->len, text);
s->len = newlen;
}
void
clearvstring (s)
struct vstring *s;
/* set string to empty */
/* does not deallocate memory ! */
{
*(s->st) = '\0';
s->len = 0;
}
void
freevstring (s)
struct vstring *s;
/* deallocates memory allocated for string */
{
if (s->st != NULL)
{
free (s->st);
s->st = NULL;
};
s->len = 0;
s->limit = 0;
}
void
parseron ()
{
parsing = 1;
slur = 0;
parsing_started = 1;
}
void
parseroff ()
{
parsing = 0;
slur = 0;
}
/* [SS] 2017-04-12 */
void handle_abc2midi_parser (line)
char *line;
{
char *p;
p = line;
if (strncasecmp(p,"%%MidiOff",9) == 0) {
ignore_line = 1;
}
if (strncasecmp(p,"%%MidiOn",8) == 0) {
ignore_line = 0;
}
}
int
getarg (option, argc, argv)
/* look for argument 'option' in command line */
char *option;
char *argv[];
int argc;
{
int j, place;
place = -1;
for (j = 0; j < argc; j++)
{
if (strcmp (option, argv[j]) == 0)
{
place = j + 1;
};
};
return (place);
}
void
skipspace (p)
char **p;
{
/* skip space and tab */
while (((int) **p == ' ') || ((int) **p == TAB))
*p = *p + 1;
}
void
skiptospace (p)
char **p;
{
while (((int) **p != ' ') && ((int) **p != TAB) && (int) **p != '\0')
*p = *p + 1;
}
int
readnumf (num)
char *num;
/* read integer from string without advancing character pointer */
{
int t;
char *p;
p = num;
if (!isdigit (*p))
{
event_error ("Missing Number");
};
t = 0;
while (((int) *p >= '0') && ((int) *p <= '9'))
{
t = t * 10 + (int) *p - '0';
p = p + 1;
};
return (t);
}
int
readsnumf (s)
char *s;
/* reads signed integer from string without advancing character pointer */
{
char *p;
p = s;
if (*p == '-')
{
p = p + 1;
skipspace (&p);
return (-readnumf (p));
}
else
{
return (readnumf (p));
}
}
int
readnump (p)
char **p;
/* read integer from string and advance character pointer */
{
int t;
t = 0;
while (((int) **p >= '0') && ((int) **p <= '9'))
{
t = t * 10 + (int) **p - '0';
*p = *p + 1;
};
return (t);
}
int
readsnump (p)
char **p;
/* reads signed integer from string and advance character pointer */
{
if (**p == '-')
{
*p = *p + 1;
skipspace (p);
return (-readnump (p));
}
else
{
return (readnump (p));
}
}
/* [JA] 2020-12-10 */
int check_power_of_two(int denom)
{
int t;
char error_message[80];
t = denom;
while (t > 1) {
if (t % 2 != 0) {
snprintf(error_message, 80, "%d b is not a power of 2", denom);
event_error (error_message);
return 0;
} else {
t = t / 2;
}
}
return denom;
}
/* [JA] 2020-12-10 */
/* read the numerator of a time signature in M: field
*
* abc standard 2.2 allows M:(a + b + c + ...)/d
* This indicates how note lenths within a bar are to be grouped.
* abc standard also allows a+b+c/d to mean the same thing but this
* goes against the convention that division takes precendence
* over addition i.e. a+b+c/d normally means a + b + (c/d).
*/
static int read_complex_has_timesig(char **place, timesig_details_t *timesig)
{
int n;
int total;
int count;
int has_bracket = 0;
if (**place == '(') {
*place = *place + 1;
has_bracket = 1;
skipspace(place);
}
count = 0;
total = 0;
skipspace(place);
while ((**place != '\0') && (isdigit(**place))) {
n = readnump(place);
timesig->complex_values[count] = n;
total = total + n;
count = count + 1;
if (count > 8) {
event_error("Too many parts to complex time (maximum 8)");
return 0;
}
skipspace(place);
if (**place == '+') {
*place = *place + 1;
skipspace(place);
}
}
if (**place == ')') {
*place = *place + 1; /* advance over ')' */
skipspace(place);
if (!has_bracket) {
event_warning("Missing ( in complex time");
}
has_bracket = 0;
}
if (has_bracket) {
event_warning("Missing ) in complex time");
}
/* we have reached the end of the numerator */
timesig->num_values = count;
timesig->num = total;
if (timesig->num_values == 1)
{
timesig->type = TIMESIG_NORMAL;
} else {
timesig->type = TIMESIG_COMPLEX;
}
return 1;
}
/* read time signature (meter) from M: field */
void readsig (char **sig, timesig_details_t *timesig)
/* upgraded [JA] 2020-12-10 */
{
int valid_num;
if ((strncmp (*sig, "none", 4) == 0) ||
(strncmp (*sig, "None", 4) == 0)) {
timesig->num = 4;
timesig->denom = 4;
timesig->type = TIMESIG_FREE_METER;
return;
}
/* [SS] 2012-08-08 cut time (C| or c|) is 2/2 not 4/4 */
if (((**sig == 'C') || (**sig == 'c')) &&
(*(*sig + 1) == '|')) {
timesig->num = 2;
timesig->denom = 2;
timesig->type = TIMESIG_CUT;
return;
}
if ((**sig == 'C') || (**sig == 'c')) {
timesig->num = 4;
timesig->denom = 4;
timesig->type = TIMESIG_COMMON;
return;
}
valid_num = read_complex_has_timesig(sig, timesig);
if (!valid_num) {
/* An error message will have been generated by read_complex_has_timesig */
timesig->num = 4;
timesig->denom = 4;
timesig->type = TIMESIG_FREE_METER;
return;
}
if ((int)**sig != '/') {
event_warning ("No / found, assuming denominator of 1");
timesig->denom = 1;
} else {
*sig = *sig + 1;
skipspace(sig);
if (!isdigit(**sig)) {
event_warning ("Number not found for M: denominator");
}
timesig->denom = readnump (sig);
}
if ((timesig->num == 0) || (timesig->denom == 0)) {
event_error ("Expecting fraction in form A/B");
} else {
timesig->denom = check_power_of_two(timesig->denom);
}
}
void
readlen (a, b, p)
int *a, *b;
char **p;
/* read length part of a note and advance character pointer */
{
int t;
*a = readnump (p);
if (*a == 0)
{
*a = 1;
};
*b = 1;
if (**p == '/')
{
*p = *p + 1;
*b = readnump (p);
if (*b == 0)
{
*b = 2;
while (**p == '/')
{
*b = *b * 2;
*p = *p + 1;
};
};
};
*b = check_power_of_two(*b);
}
/* [JA] 2020-12-10 */
static void read_L_unitlen(int *num, int *denom, char **place)
{
if (!isdigit(**place)) {
event_warning("No digit at the start of L: field");
}
*num = readnump (place);
if (*num == 0) {
*num = 1;
}
if ((int)**place != '/') {
event_error ("Missing / ");
*denom = 1;
} else {
*place = *place + 1;
skipspace(place);
*denom = readnump (place);
}
if ((*num == 0) || (*denom == 0)) {
event_error ("Expecting fraction in form A/B");
} else {
*denom = check_power_of_two(*denom);
}
}
void
read_microtone_value (a, b, p)
int *a, *b;
char **p;
/* read length part of a note and advance character pointer */
{
int t;
*a = readnump (p);
if (*a == 0)
{
*a = 1;
};
*b = 1;
if (**p == '/')
{
*p = *p + 1;
*b = readnump (p);
if (*b == 0)
{
*b = 2;
while (**p == '/')
{
*b = *b * 2;
*p = *p + 1;
};
};
} else {
*b = 0; /* [SS] 2020-06-23 */
/* To signal that the microtone value is not a fraction */
} ;
t = *b;
while (t > 1)
{
if (t % 2 != 0)
{
/*event_warning("divisor not a power of 2"); */
t = 1;
}
else
{
t = t / 2;
};
};
}
int
ismicrotone (p, dir)
char **p;
int dir;
{
int a, b;
char *chp; /* [HL] 2020-06-20 */
chp = *p;
read_microtone_value (&a, &b, p);
/* readlen_nocheck advances past microtone indication if present */
if (chp != *p) /* [HL] 2020-06-20 */
{
/* printf("event_microtone a = %d b = %d\n",a,b); */
event_microtone (dir, a, b);
return 1;
}
setmicrotone.num = 0;
setmicrotone.denom = 0;
return 0;
}
/* part of K: parsing - looks for a clef in K: field */
/* format is K:string where string is treble, bass, baritone, tenor, */
/* alto, mezzo, soprano or K:clef=arbitrary */
/* revised by James Allwright [JA] 2020-10-18 */
int isclef (char *s, cleftype_t * new_clef,
int *gotoctave, int *octave, int expect_clef)
{
int gotclef;
gotclef = 0;
new_clef->octave_offset = 0;
gotclef = get_standard_clef (s, new_clef);
if (!gotclef && expect_clef) {
/* do we have a clef in letter format ? e.g. C1, F3, G3 */
gotclef = get_extended_clef_details (s, new_clef);
if (new_clef->basic_clef == basic_clef_none) {
event_warning ("Found clef=none, but a clef is required. Ignoring");
gotclef = 0;
}
}
if (expect_clef && !gotclef) {
char error_message[80];
#ifdef NO_SNPRINTF
sprintf (error_message, "clef %s not recognized", s);
#else
snprintf (error_message, 80, "clef %s not recognized", s);
#endif
event_warning (error_message);
}
return (gotclef);
}
char *
readword (word, s)
/* part of parsekey, extracts word from input line */
/* besides the space, the symbols _, ^, and = are used */
/* as separators in order to handle key signature modifiers. */
/* [SS] 2010-05-24 */
char word[];
char *s;
{
char *p;
int i;
p = s;
i = 0;
/* [SS] 2015-04-08 */
while ((*p != '\0') && (*p != ' ') && (*p != '\t') && ((i == 0) ||
((*p != '='))))
{
if (i >1 && *p == '^') break; /* allow for double sharps and flats */
if (i >1 && *p == '_') break;
if (i < 29)
{
word[i] = *p;
i = i + 1;
};
p = p + 1;
};
word[i] = '\0';
return (p);
}
void
lcase (s)
/* convert word to lower case */
char *s;
{
char *p;
p = s;
while (*p != '\0')
{
if (isupper (*p))
{
*p = *p + 'a' - 'A';
};
p = p + 1;
};
}
/* initialize a timesig structure to default values */
void init_timesig(timesig_details_t *timesig)
{
timesig->type = TIMESIG_FREE_METER;
timesig->num = 4;
timesig->denom = 4;
timesig->complex_values[0] = 4;
timesig->num_values = 1;
}
/* [JA] 2020-10-12 */
void init_voice_contexts (void)
{
int i;
cleftype_t default_clef; /* [JA] 2020-11-01 */
/* we use treble clef when no clef is explicitly specified */
get_standard_clef ("treble", &default_clef); /* default to treble clef */
for (i = 0; i < MAX_VOICES; i++) { /* [SS} 2015-03-15 */
voicecode[i].label[0] = '\0';
voicecode[i].expect_repeat = 0;
voicecode[i].repeat_count = 0;
copy_clef(&voicecode[i].clef, &default_clef); /* [JA] 2020-11-01 */
}
}
/* copy one timesig_details_t struct to another [JA] 2020-12-10 */
void copy_timesig(timesig_details_t *destination, timesig_details_t *source)
{
int i;
destination->type = source->type;
destination->num = source->num;
destination->denom = source->denom;
for (i = 0; i < 8; i++)
{
destination->complex_values[i] = source->complex_values[i];
}
destination->num_values = source->num_values;
}
/* [JA] 2020-10-12 */
/* called at the start of each tune */
static void reset_parser_status (void)
{
cleftype_t default_clef;
init_timesig(&master_timesig);
get_standard_clef ("treble", &master_clef); /* default to treble clef */
has_timesig = 0;
master_unitlen = -1;
voicenum = 1;
has_voice_fields = 0;
num_voices = 1;
parserinchord = 0;
ingrace = 0;
slur = 0;
init_voice_contexts ();
}
void
print_voicecodes ()
{
int i;
if (num_voices == 0)
return;
printf ("voice mapping:\n");
for (i = 0; i < num_voices; i++)
{
if (i % 4 == 3)
printf ("\n");
printf ("%s %d ", voicecode[i].label, i + 1);
}
printf ("\n");
}
/* [JA] 2020-10-12 */
int interpret_voice_label (char *s, int num, int *is_new)
/* We expect a numeric value indicating the voice number.
* The assumption is that these will ocuur in the order in which voices
* appear, so that we have V:1, V:2, ... V:N if there are N voices.
* The abc standard 2.2 allows strings instead of these numbers to
* represent voices.
* This function should be called with either
* an empty string and a valid num or
* a valid string and num set to 0.
*
* If num is non-zero, we check that it is valid and return it.
* If the number is one above the number of voices currently in use,
* we allocate a new voice.
*
* If num is zero and the string is not empty, we check if string
* has been assigned to one of the existing voices. If not, we
* allocate a new voice and assign the string to it.
*
* If we exceed MAX_VOICES voices, we report an error.
*
* we return a voice number in the range 1 - num_voices
{
int i;
char code[32];
char msg[80]; /* [PHDM] 2012-11-22 */
char *c;
c = readword (code, s);
if (num > 0)
{
if (num > num_voices + 1)
{
char error_message[80];
#ifdef NO_SNPRINT
sprintf(error_message, "V:%d out of sequence, treating as V:%d",
num, num_voices); /* [SS] 2020-10-01 */
#else
snprintf(error_message, 80, "V:%d out of sequence, treating as V:%d",
num, num_voices);
#endif
event_warning(error_message);
num = num_voices + 1;
}
/* declaring a new voice */
if (num == num_voices + 1)
{
*is_new = 1;
if (num_voices >= MAX_VOICES)
{
event_warning("Number of available voices exceeded");
return 1;
}
num_voices = num_voices + 1;
voicecode[num_voices - 1].label[0] = '\0';
} else {
/* we are using a previously declared voice */
*is_new = 0;
}
return num;
}
/* [PHDM] 2012-11-22 */
if (*c != '\0' && *c != ' ' && *c != ']') {
sprintf (msg, "invalid character `%c' in Voice ID", *c);
event_error (msg);
}
/* [PHDM] 2012-11-22 */
if (code[0] == '\0')
{
event_warning("Empty V: field, treating as V:1");
return 1;
}
/* Has supplied label been used for one of the existing voices ? */
if (has_voice_fields)
{
for (i = 0; i < num_voices; i++) {
if (strcmp (code, voicecode[i].label) == 0) {
return i + 1;
}
}
}
/* if we have got here, we have a new voice */
if ((num_voices + 1) > MAX_VOICES) {/* [SS] 2015-03-16 */
event_warning("Number of available voices exceeded");
return 1;
}
/* First V: field is a special case. We are already on voice 1,
* so we don't increment the number of voices, but we will set
* status->has_voice_fields on returning from this function.
*/
if (has_voice_fields)
{
*is_new = 1;
num_voices++;
} else {
*is_new = 0; /* we have already started as voice 1 */
}
strncpy (voicecode[num_voices - 1].label, code, 31);
return num_voices;
}
/* The following three functions parseclefs, parsetranspose,
* parseoctave are used to parse the K: field which not
* only specifies the key signature but also other descriptors
* used for producing a midi file or postscript file.
*
* The char* word contains the particular token that
* is being interpreted. If the token can be understood,
* other parameters are extracted from char ** s and
* s is advanced to point to the next token.
*/
int
parseclef (s, word, gotclef, clefstr, newclef, gotoctave, octave)
char **s;
char *word;
int *gotclef;
char *clefstr; /* [JA] 2020-10-19 */
cleftype_t * newclef;
int *gotoctave, *octave;
/* extracts string clef= something */
{
int successful;
skipspace (s);
*s = readword (word, *s);
successful = 0;
if (casecmp (word, "clef") == 0)
{
skipspace (s);
if (**s != '=')
{
event_error ("clef must be followed by '='");
}
else
{
*s = *s + 1;
skipspace (s);
*s = readword (clefstr, *s);
if (isclef (clefstr, newclef, gotoctave, octave, 1))
{
*gotclef = 1;
};
};
successful = 1;
}
else if (isclef (word, newclef, gotoctave, octave, 0))
{
*gotclef = 1;
strcpy (clefstr, word);
successful = 1;
};
return successful;
}
int
parsetranspose (s, word, gottranspose, transpose)
/* parses string transpose= number */
char **s;
char *word;
int *gottranspose;
int *transpose;
{
if (casecmp (word, "transpose") != 0)
return 0;
skipspace (s);
if (**s != '=')
{
event_error ("transpose must be followed by '='");
}
else
{
*s = *s + 1;
skipspace (s);
*transpose = readsnump (s);
*gottranspose = 1;
};
return 1;
};
int
parseoctave (s, word, gotoctave, octave)
/* parses string octave= number */
char **s;
char *word;
int *gotoctave;
int *octave;
{
if (casecmp (word, "octave") != 0)
return 0;
skipspace (s);
if (**s != '=')
{
event_error ("octave must be followed by '='");
}
else
{
*s = *s + 1;
skipspace (s);
*octave = readsnump (s);
*gotoctave = 1;
};
return 1;
};
int
parsename (s, word, gotname, namestring, maxsize)
/* parses string name= "string" in V: command
for compatability of abc2abc with abcm2ps
char **s;
char *word;
int *gotname;
char namestring[];
int maxsize;
{
int i;
i = 0;
if (casecmp (word, "name") != 0)
return 0;
skipspace (s);
if (**s != '=')
{
event_error ("name must be followed by '='");
}
else
{
*s = *s + 1;
skipspace (s);
if (**s == '"') /* string enclosed in double quotes */
{
namestring[i] = (char) **s;
*s = *s + 1;
i++;
while (i < maxsize && **s != '"' && **s != '\0')
{
namestring[i] = (char) **s;
*s = *s + 1;
i++;
}
namestring[i] = (char) **s; /* copy double quotes */
i++;
namestring[i] = '\0';
}
else /* string not enclosed in double quotes */
{
while (i < maxsize && **s != ' ' && **s != '\0')
{
namestring[i] = (char) **s;
*s = *s + 1;
i++;
}
namestring[i] = '\0';
}
*gotname = 1;
}
return 1;
};
int
parsesname (s, word, gotname, namestring, maxsize)
/* parses string name= "string" in V: command
for compatability of abc2abc with abcm2ps
char **s;
char *word;
int *gotname;
char namestring[];
int maxsize;
{
int i;
i = 0;
if (casecmp (word, "sname") != 0)
return 0;
skipspace (s);
if (**s != '=')
{
event_error ("name must be followed by '='");
}
else
{
*s = *s + 1;
skipspace (s);
if (**s == '"') /* string enclosed in double quotes */
{
namestring[i] = (char) **s;
*s = *s + 1;
i++;
while (i < maxsize && **s != '"' && **s != '\0')
{
namestring[i] = (char) **s;
*s = *s + 1;
i++;
}
namestring[i] = (char) **s; /* copy double quotes */
i++;
namestring[i] = '\0';
}
else /* string not enclosed in double quotes */
{
while (i < maxsize && **s != ' ' && **s != '\0')
{
namestring[i] = (char) **s;
*s = *s + 1;
i++;
}
namestring[i] = '\0';
}
*gotname = 1;
}
return 1;
};
int
parsemiddle (s, word, gotmiddle, middlestring, maxsize)
/* parse string middle=X in V: command
for abcm2ps compatibility
char **s;
char *word;
int *gotmiddle;
char middlestring[];
int maxsize;
{
int i;
i = 0;
if (casecmp (word, "middle") != 0)
return 0;
skipspace (s);
if (**s != '=')
{
event_error ("middle must be followed by '='");
}
else
{
*s = *s + 1;
skipspace (s);
/* we really ought to check the we have a proper note name; for now, just copy non-space
characters */
while (i < maxsize && **s != ' ' && **s != '\0')
{
middlestring[i] = (char) **s;
*s = *s + 1;
++i;
}
middlestring[i] = '\0';
*gotmiddle = 1;
}
return 1;
}
int
parseother (s, word, gotother, other, maxsize) /* [SS] 2011-04-18 */
/* parses any left overs in V: command (eg. stafflines=1) */
char **s;
char *word;
int *gotother;
char other[];
int maxsize;
{
if (word[0] != '\0')
{
if ( (int) strlen (other) < maxsize) /* [SS] 2015-10-08 added (int) */
strncat (other, word, maxsize);
if (**s == '=')
{ /* [SS] 2011-04-19 */
*s = readword (word, *s);
if ( (int) strlen (other) < maxsize) /* [SS] 2015-10-08 added (int) */
strncat (other, word, maxsize);
}
strncat (other, " ", maxsize); /* in case other codes follow */
*gotother = 1;
return 1;
}
return 0;
}
static void process_microtones (int *parsed, char word[],
char modmap[], int modmul[], struct fraction modmicrotone[])
{
int a, b; /* for microtones [SS] 2014-01-06 */
char c;
int j;
int success;
/* shortcuts such as ^/4G instead of ^1/4G not allowed here */
success = sscanf (&word[1], "%d/%d%c", &a, &b, &c);
if (success == 3) /* [SS] 2016-04-10 */
{
*parsed = 1;
j = (int) c - 'A';
if (j > 7) {
j = (int) c - 'a';
}
if (j > 7 || j < 0) {
event_error ("Not a valid microtone");
return;
}
if (word[0] == '_') a = -a;
/* printf("%s fraction microtone %d/%d for %c\n",word,a,b,c); */
} else {
success = sscanf (&word[1], "%d%c", &a, &c); /* [SS] 2020-06-25 */
if (success == 2)
{
b = 0;
/* printf("%s integer microtone %d%c\n",word,a,c); */
if (temperament != 1) { /* [SS] 2020-06-25 2020-07-05 */
event_warning("do not use integer microtone without calling %%MIDI temperamentequal");
}
*parsed = 1;
}
}
/* if (parsed ==1) [SS] 2020-09-30 */
if (success > 0) {
j = (int) c - 'A';
if (j > 7) {
j = (int) c - 'a';
}
if (j > 7 || j < 0) {
event_error ("Not a valid microtone");
return;
}
if (word[0] == '_') a = -a;
modmap[j] = word[0];
modmicrotone[j].num = a;
modmicrotone[j].denom = b;
/* printf("%c microtone = %d/%d\n",modmap[j],modmicrotone[j].num,modmicrotone[j].denom); */
}
} /* finished ^ = _ */
int
parsekey (str)
/* parse contents of K: field */
/* this works by picking up a strings and trying to parse them */
/* returns 1 if valid key signature found, 0 otherwise */
/* This is unfortunately a complicated function because it does alot.
* It prepares the data:
* sf = number of sharps or flats in key signature
* modeindex:= major 0, minor 1, ... dorian, etc.
* modmap: eg "^= _^ " corresponds to A# B Cb D#
* modmul: 2 signals double sharp or flat
* modmicrotone: eg {2/4 0/0 0/0 2/0 0/0 -3/0 0/0} for ^2/4A ^2D _3F
* clefstr: treble, bass, treble+8, ...
* octave: eg. 1,2,-1 ...
* transpose: 1,2 for handling certain clefs
* and various flags like explict, gotoctave, gottranspose, gotclef, gotkey
* which are all sent to abc2midi (via store.c), yaps (via yapstree), abc2abc
* via (toabc.c), using the function call event_key().
*
* The variables sf, modeindex, modmul, and modmicrotone control which notes
* are sharpened or flatten in a musical measure.
* The variable clefstr selects one of the clefs, (treble, bass, ...)
* The variable octave allows you to shift everything up and down by an octave
* The variable transpose allows you to automatically transpose every note.
*
* All of this information is extracted from the string str from the
* K: command.
*/
char *str;
{
char *s;
char word[30];
int parsed = 0; /* [SDG] 2020-06-03 */
int gotclef, gotkey, gotoctave, gottranspose;
int explict; /* [SS] 2010-05-08 */
int modnotes; /* [SS] 2010-07-29 */
int foundmode;
int transpose, octave;
char clefstr[30];
cleftype_t newclef;
char modestr[30];
char msg[80];
char *moveon;
int sf = -1, minor = -1;
char modmap[7];
int modmul[7];
struct fraction modmicrotone[7];
int i, j;
int cgotoctave, coctave;
char *key = "FCGDAEB";
int modeindex;
int a, b; /* for microtones [SS] 2014-01-06 */
int success;
char c;
clefstr[0] = (char) 0;
modestr[0] = (char) 0;
s = str;
transpose = 0;
gottranspose = 0;
octave = 0;
gotkey = 0;
gotoctave = 0;
gotclef = 0;
cgotoctave = 0;
coctave = 0;
init_new_clef (&newclef);
modeindex = 0;
explict = 0;
modnotes = 0;
nokey = nokeysig; /* [SS] 2016-03-03 */
for (i = 0; i < 7; i++)
{
modmap[i] = ' ';
modmul[i] = 1;
modmicrotone[i].num = 0; /* [SS] 2014-01-06 */
modmicrotone[i].denom = 0;
};
word[0] = 0; /* in case of empty string [SDG] 2020-06-04 */
while (*s != '\0')
{
parsed = parseclef (&s, word, &gotclef, clefstr, &newclef, &cgotoctave, &coctave);
if (gotclef) {
/* make clef an attribute of current voice */
if (inhead) {
copy_clef (&master_clef, &newclef);
}
if (inbody){
copy_clef (&voicecode[voicenum - 1].clef, &newclef);
}
}
/* parseclef also scans the s string using readword(), placing */
/* the next token into the char array word[]. */
if (!parsed)
parsed = parsetranspose (&s, word, &gottranspose, &transpose);
if (!parsed)
parsed = parseoctave (&s, word, &gotoctave, &octave);
if ((parsed == 0) && (casecmp (word, "Hp") == 0))
{
sf = 2;
minor = 0;
gotkey = 1;
parsed = 1;
};
if ((parsed == 0) && (casecmp (word, "none") == 0))
{
gotkey = 1;
parsed = 1;
nokey = 1;
minor = 0;
sf = 0;
}
if (casecmp (word, "exp") == 0)
{
explict = 1;
parsed = 1;
}
/* if K: not 'none', 'Hp' or 'exp' then look for key signature
* like Cmaj Amin Ddor ...
* The key signature is expressed by sf which indicates the
* number of sharps (if positive) or flats (if negative) */
if ((parsed == 0) && ((word[0] >= 'A') && (word[0] <= 'G')))
{
gotkey = 1;
parsed = 1;
/* parse key itself */
sf = strchr (key, word[0]) - key - 1;
j = 1;
/* deal with sharp/flat */
if (word[1] == '#')
{
sf += 7;
j = 2;
}
else
{
if (word[1] == 'b')
{
sf -= 7;
j = 2;
};
}
minor = 0;
foundmode = 0;
if ((int) strlen (word) == j)
{
/* look at next word for mode */
skipspace (&s);
moveon = readword (modestr, s);
lcase (modestr);
for (i = 0; i < 10; i++)
{
if (strncmp (modestr, mode[i], 3) == 0)
{
foundmode = 1;
sf = sf + modeshift[i];
minor = modeminor[i];
modeindex = i;
};
};
if (foundmode)
{
s = moveon;
};
}
else
{
strcpy (modestr, &word[j]);
lcase (modestr);
for (i = 0; i < 10; i++)
{
if (strncmp (modestr, mode[i], 3) == 0)
{
foundmode = 1;
sf = sf + modeshift[i];
minor = modeminor[i];
modeindex = i;
};
};
if (!foundmode)
{
sprintf (msg, "Unknown mode '%s'", &word[j]);
event_error (msg);
modeindex = 0;
};
};
};
if (gotkey)
{
if (sf > 7)
{
event_warning ("Unusual key representation");
sf = sf - 12;
};
if (sf < -7)
{
event_warning ("Unusual key representation");
sf = sf + 12;
};
};
/* look for key signature modifiers
* For example K:D _B
* which will include a Bb in the D major key signature
* */
if ((word[0] == '^') || (word[0] == '_') || (word[0] == '='))
{
modnotes = 1;
if ((strlen (word) == 2) && (word[1] >= 'a') && (word[1] <= 'g'))
{
j = (int) word[1] - 'a';
modmap[j] = word[0];
modmul[j] = 1;
parsed = 1;
}
else
{ /*double sharp or double flat */
if ((strlen (word) == 3) && (word[0] != '=')
&& (word[0] == word[1]) && (word[2] >= 'a')
&& (word[2] <= 'g'))
{
j = (int) word[2] - 'a';
modmap[j] = word[0];
modmul[j] = 2;
parsed = 1;
};
};
};
/* if (explict) for compatibility with abcm2ps 2010-05-08 2010-05-20 */
if ((word[0] == '^') || (word[0] == '_') || (word[0] == '='))
{
modnotes = 1;
if ((strlen (word) == 2) && (word[1] >= 'A') && (word[1] <= 'G'))
{
j = (int) word[1] - 'A';
modmap[j] = word[0];
modmul[j] = 1;
parsed = 1;
}
else if /*double sharp or double flat */
((strlen (word) == 3) && (word[0] != '=') && (word[0] == word[1])
&& (word[2] >= 'A') && (word[2] <= 'G'))
{
j = (int) word[2] - 'A';
modmap[j] = word[0];
modmul[j] = 2;
parsed = 1;
};
}
/* microtone? */
/* shortcuts such as ^/4G instead of ^1/4G not allowed here */
/* parsed =0; [SS] 2020-09-30 */
process_microtones (&parsed, word,
modmap, modmul, modmicrotone);
}
if ((parsed == 0) && (strlen (word) > 0))
{
sprintf (msg, "Ignoring string '%s' in K: field", word);
event_warning (msg);
};
if (cgotoctave)
{
gotoctave = 1;
octave = coctave;
}
if (modnotes & !gotkey)
{ /*[SS] 2010-07-29 for explicit key signature */
sf = 0;
/*gotkey = 1; [SS] 2010-07-29 */
explict = 1; /* [SS] 2010-07-29 */
}
event_key (sf, str, modeindex, modmap, modmul, modmicrotone, gotkey,
gotclef, clefstr, &newclef, octave, transpose, gotoctave, gottranspose,
explict);
return (gotkey);
}
static void set_voice_from_master(int voice_num)
{
voice_context_t *current_voice;
current_voice = &voicecode[voice_num - 1];
copy_timesig(¤t_voice->timesig, &master_timesig);
copy_clef(¤t_voice->clef, &master_clef);
current_voice->unitlen = master_unitlen;
}
void
parsevoice (s)
char *s;
{
int num; /* voice number */
struct voice_params vparams;
char word[64]; /* 2017-10-11 */
int parsed;
int coctave, cgotoctave;
int is_new = 0;
vparams.transpose = 0;
vparams.gottranspose = 0;
vparams.octave = 0;
vparams.gotoctave = 0;
vparams.gotclef = 0;
cgotoctave = 0;
coctave = 0;
vparams.gotname = 0;
vparams.gotsname = 0;
vparams.gotmiddle = 0;
vparams.gotother = 0; /* [SS] 2011-04-18 */
vparams.other[0] = '\0'; /* [SS] 2011-04-18 */
skipspace (&s);
num = 0;
if (isdigit(*s)) { /* [JA] 2020-10-12 */
num = readnump (&s);
}
num = interpret_voice_label (s, num, &is_new);
if (is_new) {
/* declaring voice for the first time.
* initialize with values set in the tune header */
set_voice_from_master(num);
}
has_voice_fields = 1;
skiptospace (&s);
voicenum = num;
skipspace (&s);
while (*s != '\0') {
parsed =
parseclef (&s, word, &vparams.gotclef, vparams.clefname,
&vparams.new_clef, &cgotoctave, &coctave);
if (vparams.gotclef) {
/* make clef an attribute of current voice */
copy_clef (&voicecode[num - 1].clef, &vparams.new_clef);
}
if (!parsed)
parsed =
parsetranspose (&s, word, &vparams.gottranspose,
&vparams.transpose);
if (!parsed)
parsed = parseoctave (&s, word, &vparams.gotoctave, &vparams.octave);
if (!parsed)
parsed =
parsename (&s, word, &vparams.gotname, vparams.namestring,
V_STRLEN);
if (!parsed)
parsed =
parsesname (&s, word, &vparams.gotsname, vparams.snamestring,
V_STRLEN);
if (!parsed)
parsed =
parsemiddle (&s, word, &vparams.gotmiddle, vparams.middlestring,
V_STRLEN);
if (!parsed)
parsed = parseother (&s, word, &vparams.gotother, vparams.other, V_STRLEN); /* [SS] 2011-04-18 */
}
/* [SS] 2015-05-13 allow octave= to change the clef= octave setting */
/* cgotoctave may be set to 1 by a clef=. vparams.gotoctave is set */
/* by octave= */
if (cgotoctave && vparams.gotoctave == 0)
{
vparams.gotoctave = 1;
vparams.octave = coctave;
}
event_voice (num, s, &vparams);
/*
if (gottranspose) printf("transpose = %d\n", vparams.transpose);
if (gotoctave) printf("octave= %d\n", vparams.octave);
if (gotclef) printf("clef= %s\n", vparams.clefstr);
if (gotname) printf("parsevoice: name= %s\n", vparams.namestring);
if(gotmiddle) printf("parsevoice: middle= %s\n", vparams.middlestring);
}
void
parsenote (s)
char **s;
/* parse abc note and advance character pointer */
{
int decorators[DECSIZE];
int i, t;
int mult;
char accidental, note;
int octave, n, m;
char msg[80];
mult = 1;
microtone = 0;
accidental = ' ';
note = ' ';
for (i = 0; i < DECSIZE; i++)
{
decorators[i] = decorators_passback[i];
if (!inchordflag)
decorators_passback[i] = 0; /* [SS] 2012-03-30 */
}
while (strchr (decorations, **s) != NULL)
{
t = strchr (decorations, **s) - decorations;
decorators[t] = 1;
*s = *s + 1;
};
/*check for decorated chord */
if (**s == '[')
{
lineposition = *s - linestart; /* [SS] 2011-07-18 */
if (fileprogram == YAPS)
event_warning ("decorations applied to chord");
for (i = 0; i < DECSIZE; i++)
chorddecorators[i] = decorators[i];
event_chordon (chorddecorators);
if (fileprogram == ABC2ABC)
for (i = 0; i < DECSIZE; i++)
decorators[i] = 0;
parserinchord = 1;
*s = *s + 1;
skipspace (s);
};
if (parserinchord)
{
/* inherit decorators */
if (fileprogram != ABC2ABC)
for (i = 0; i < DECSIZE; i++)
{
decorators[i] = decorators[i] | chorddecorators[i];
};
};
/* [SS] 2011-12-08 to catch fermata H followed by a rest */
if (**s == 'z')
{
*s = *s + 1;
readlen (&n, &m, s);
event_rest (decorators, n, m, 0);
return;
}
if (**s == 'x')
{
*s = *s + 1;
readlen (&n, &m, s);
event_rest (decorators, n, m, 1);
return;
}
/* read accidental */
switch (**s)
{
case '_':
accidental = **s;
*s = *s + 1;
if (**s == '_')
{
*s = *s + 1;
mult = 2;
};
microtone = ismicrotone (s, -1);
if (microtone)
{
if (mult == 2)
mult = 1;
else
accidental = ' ';
}
break;
case '^':
accidental = **s;
*s = *s + 1;
if (**s == '^')
{
*s = *s + 1;
mult = 2;
};
microtone = ismicrotone (s, 1);
if (microtone)
{
if (mult == 2)
mult = 1;
else
accidental = ' ';
}
break;
case '=':
accidental = **s;
*s = *s + 1;
/* if ((**s == '^') || (**s == '_')) {
accidental = **s;
}; */
if (**s == '^')
{
accidental = **s;
*s = *s + 1;
microtone = ismicrotone (s, 1);
if (microtone == 0)
accidental = '^';
}
else if (**s == '_')
{
accidental = **s;
*s = *s + 1;
microtone = ismicrotone (s, -1);
if (microtone == 0)
accidental = '_';
}
break;
default:
microtone = ismicrotone (s, 1); /* [SS] 2014-01-19 */
break;
};
if ((**s >= 'a') && (**s <= 'g'))
{
note = **s;
octave = 1;
*s = *s + 1;
while ((**s == '\'') || (**s == ','))
{
if (**s == '\'')
{
octave = octave + 1;
*s = *s + 1;
};
if (**s == ',')
{
sprintf (msg, "Bad pitch specifier , after note %c", note);
event_error (msg);
octave = octave - 1;
*s = *s + 1;
};
};
}
else
{
octave = 0;
if ((**s >= 'A') && (**s <= 'G'))
{
note = **s + 'a' - 'A';
*s = *s + 1;
while ((**s == '\'') || (**s == ','))
{
if (**s == ',')
{
octave = octave - 1;
*s = *s + 1;
};
if (**s == '\'')
{
sprintf (msg, "Bad pitch specifier ' after note %c",
note + 'A' - 'a');
event_error (msg);
octave = octave + 1;
*s = *s + 1;
};
};
};
};
if (note == ' ')
{
event_error ("Malformed note : expecting a-g or A-G");
}
else
{
readlen (&n, &m, s);
event_note (decorators, &voicecode[voicenum - 1].clef, accidental, mult, note, octave, n, m);
if (!microtone)
event_normal_tone (); /* [SS] 2014-01-09 */
};
}
char *
getrep (p, out)
char *p;
char *out;
/* look for number or list following [ | or :| */
{
char *q;
int digits;
int done;
int count;
q = p;
count = 0;
done = 0;
digits = 0;
while (!done)
{
if (isdigit (*q))
{
out[count] = *q;
count = count + 1;
q = q + 1;
digits = digits + 1;
/* [SS] 2013-04-21 */
if (count > 50)
{
event_error ("malformed repeat");
break;
}
}
else
{
if (((*q == '-') || (*q == ',')) && (digits > 0)
&& (isdigit (*(q + 1))))
{
out[count] = *q;
count = count + 1;
q = q + 1;
digits = 0;
/* [SS] 2013-04-21 */
if (count > 50)
{
event_error ("malformed repeat");
break;
}
}
else
{
done = 1;
};
};
};
out[count] = '\0';
return (q);
}
int
checkend (s)
char *s;
/* returns 1 if we are at the end of the line 0 otherwise */
/* used when we encounter '\' '*' or other special line end characters */
{
char *p;
int atend;
p = s;
skipspace (&p);
if (*p == '\0')
{
atend = 1;
}
else
{
atend = 0;
};
return (atend);
}
void
readstr (out, in, limit)
char out[];
char **in;
int limit;
/* copy across alpha string */
{
int i;
i = 0;
while ((isalpha (**in)) && (i < limit - 1))
{
out[i] = **in;
i = i + 1;
*in = *in + 1;
};
out[i] = '\0';
}
/* [SS] 2015-06-01 required for parse_mididef() in store.c */
/* Just like readstr but also allows anything except white space */
int readaln (out, in, limit)
char out[];
char **in;
int limit;
/* copy across alphanumeric string */
{
int i;
i = 0;
while ((!isspace (**in)) && (**in) != 0 && (i < limit - 1))
{
out[i] = **in;
i = i + 1;
*in = *in + 1;
};
out[i] = '\0';
return i;
}
void
parse_precomment (s)
char *s;
/* handles a comment field */
{
char package[40];
char *p;
int success;
success = sscanf (s, "%%%%abc-version %3s", abcversion); /* [SS] 2014-08-11 */
if (*s == '%')
{
p = s + 1;
readstr (package, &p, 40);
event_specific (package, p);
}
else
{
event_comment (s);
};
}
/* [SS] 2017-12-10 */
FILE * parse_abc_include (s)
char *s;
{
char includefilename[80];
FILE *includehandle;
int success;
success = sscanf (s, "%%%%abc-include %79s", includefilename); /* [SS] 2014-08-11 */
if (success == 1) {
/* printf("opening include file %s\n",includefilename); */
includehandle = fopen(includefilename,"r");
if (includehandle == NULL)
{
printf ("Failed to open include file %s\n", includefilename);
};
return includehandle;
}
return NULL;
}
void
parse_tempo (place)
char *place;
/* parse tempo descriptor i.e. Q: field */
{
char *p;
int a, b;
int n;
int relative;
char *pre_string;
char *post_string;
relative = 0;
p = place;
pre_string = NULL;
if (*p == '"')
{
p = p + 1;
pre_string = p;
while ((*p != '"') && (*p != '\0'))
{
p = p + 1;
};
if (*p == '\0')
{
event_error ("Missing closing double quote");
}
else
{
*p = '\0';
p = p + 1;
place = p;
};
};
while ((*p != '\0') && (*p != '='))
p = p + 1;
if (*p == '=')
{
p = place;
skipspace (&p);
if (((*p >= 'A') && (*p <= 'G')) || ((*p >= 'a') && (*p <= 'g')))
{
relative = 1;
p = p + 1;
};
readlen (&a, &b, &p);
skipspace (&p);
if (*p != '=')
{
event_error ("Expecting = in tempo");
};
p = p + 1;
}
else
{
a = 1; /* [SS] 2013-01-27 */
/*a = 0; [SS] 2013-01-27 */
b = 4;
p = place;
};
skipspace (&p);
n = readnump (&p);
post_string = NULL;
if (*p == '"')
{
p = p + 1;
post_string = p;
while ((*p != '"') && (*p != '\0'))
{
p = p + 1;
};
if (*p == '\0')
{
event_error ("Missing closing double quote");
}
else
{
*p = '\0';
p = p + 1;
};
};
event_tempo (n, a, b, relative, pre_string, post_string);
}
void appendfield(char *); /* links with store.c and yapstree.c */
void append_fieldcmd (key, s) /* [SS] 2014-08-15 */
char key;
char *s;
{
appendfield(s);
}
void
preparse_words (s)
char *s;
/* takes a line of lyrics (w: field) and strips off */
/* any continuation character */
{
int continuation;
int l;
/* printf("Parsing %s\n", s); */
/* strip off any trailing spaces */
l = strlen (s) - 1;
while ((l >= 0) && (*(s + l) == ' '))
{
*(s + l) = '\0';
l = l - 1;
};
if (*(s + l) != '\\')
{
continuation = 0;
}
else
{
/* [SS] 2014-08-14 */
event_warning ("\\n continuation no longer supported in w: line");
continuation = 1;
/* remove continuation character */
*(s + l) = '\0';
l = l - 1;
while ((l >= 0) && (*(s + l) == ' '))
{
*(s + l) = '\0';
l = l - 1;
};
};
event_words (s, continuation);
}
void
init_abbreviations ()
/* initialize mapping of H-Z to strings */
{
int i;
/* for (i = 0; i < 'Z' - 'H'; i++) [SS] 2016-09-25 */
for (i = 0; i < 'z' - 'A'; i++) /* [SS] 2016-09-25 */
{
abbreviation[i] = NULL;
};
}
void
record_abbreviation (char symbol, char *string)
/* update record of abbreviations when a U: field is encountered */
{
int index;
/* if ((symbol < 'H') || (symbol > 'Z')) [SS] 2016-09-20 */
if ((symbol < 'A') || (symbol > 'z'))
{
return;
};
index = symbol - 'A';
if (abbreviation[index] != NULL)
{
free (abbreviation[index]);
};
abbreviation[index] = addstring (string);
}
char *
lookup_abbreviation (char symbol)
/* return string which s abbreviates */
{
/* if ((symbol < 'H') || (symbol > 'Z')) [SS] 2016-09-25 */
if ((symbol < 'A') || (symbol > 'z'))
{
return (NULL);
}
else
{
return (abbreviation[symbol - 'A']); /* [SS] 2016-09-20 */
};
}
void
free_abbreviations ()
/* free up any space taken by abbreviations */
{
int i;
for (i = 0; i < SIZE_ABBREVIATIONS; i++)
{
if (abbreviation[i] != NULL)
{
free (abbreviation[i]);
};
};
}
/* function to resolve unit note length when the
* L: field is missing in the header [JA] 2020-12-10
*
* From the abc standard 2.2:
* If there is no L: field defined, a unit note length is set by default,
* based on the meter field M:. This default is calculated by computing
* the meter as a decimal: if it is less than 0.75 the default unit note
* length is a sixteenth note; if it is 0.75 or greater, it is an eighth
* note. For example, 2/4 = 0.5, so, the default unit note length is a
* sixteenth note, while for 4/4 = 1.0, or 6/8 = 0.75, or 3/4= 0.75,
* it is an eighth note. For M:C (4/4), M:C| (2/2) and M:none (free meter),
* the default unit note length is 1/8.
*
*/
static void resolve_unitlen()
{
if (master_unitlen == -1)
{
if (has_timesig == 0)
{
event_default_length(8);
master_unitlen = 8;
}
else
{
if (((4 * master_timesig.num)/master_timesig.denom) >= 3)
{
event_default_length(8);
master_unitlen = 8;
}
else
{
event_default_length(16);
master_unitlen = 16;
}
}
}
}
void
parsefield (key, field)
char key;
char *field;
/* top-level routine handling all lines containing a field */
{
char *comment;
char *place;
char *xplace;
int iscomment;
int foundkey;
if (key == 'X')
{
int x;
xplace = field;
skipspace (&xplace);
x = readnumf (xplace);
if (inhead)
{
event_error ("second X: field in header");
};
if (inbody)
{
/* [JA] 2020-10-14 */
event_error ("Missing blank line before new tune");
}
event_refno (x);
ignore_line =0; /* [SS] 2017-04-12 */
reset_parser_status(); /* [JA] 2020-10-12 */
inhead = 1;
inbody = 0;
parserinchord = 0;
return;
};
if (parsing == 0)
return;
/*if ((inbody) && (strchr ("EIKLMPQTVdswW", key) == NULL)) [SS] 2014-08-15 */
if ((inbody) && (strchr ("EIKLMPQTVdrswW+", key) == NULL)) /* [SS] 2015-05-11 */
{
event_error ("Field not allowed in tune body");
};
comment = field;
iscomment = 0;
while ((*comment != '\0') && (*comment != '%'))
{
comment = comment + 1;
};
if (*comment == '%')
{
iscomment = 1;
*comment = '\0';
comment = comment + 1;
};
place = field;
skipspace (&place);
switch (key)
{
case 'K':
if (inhead)
{
/* First K: is the end of the header and the start of the body.
* make sure we set up default for unit length
* if L: fields was missing in the header.
*/
resolve_unitlen();
/* set voice parameters using values from header */
set_voice_from_master(1);
}
foundkey = parsekey (place);
if (inhead || inbody) {
if (foundkey)
{
inbody = 1;
inhead = 0;
}
else
{
if (inhead)
{
event_error ("First K: field must specify key signature");
};
};
}
else
{
event_error ("No X: field preceding K:");
};
break;
case 'M':
{
timesig_details_t timesig;
/* strncpy (timesigstring, place, 16); [SS] 2011-08-19 */
#ifdef NO_SNPRINT
sprintf(timesigstring,"%s",place); /* [SEG] 2020-06-07 */
#else
snprintf(timesigstring,sizeof(timesigstring),"%s",place); /* [SEG] 2020-06-07 */
#endif
readsig (&place, ×ig);
if ((*place == 's') || (*place == 'l')) {
event_error ("s and l in M: field not supported");
};
if ((timesig.num == 0) || (timesig.denom == 0)) {
event_warning ("Invalid time signature ignored");
} else {
if (inhead) {
/* copy timesig into master_timesig */
copy_timesig(&master_timesig, ×ig);
}
if (inbody) {
/* update timesig in current voice */
voice_context_t *current_voice;
current_voice = &voicecode[voicenum - 1];
copy_timesig(¤t_voice->timesig, ×ig);
}
event_timesig (×ig);
has_timesig = 1;
}
}
break;
case 'L':
{
int num, denom;
read_L_unitlen(&num, &denom, &place);
if (num != 1)
{
event_error ("Default length must be 1/X");
}
else
{
if (denom > 0)
{
event_length (denom);
if (inhead)
{
master_unitlen = denom;
}
if (inbody) {
voice_context_t *current_voice;
current_voice = &voicecode[voicenum - 1];
current_voice->unitlen = denom;
}
}
else
{
event_error ("invalid denominator");
};
};
break;
};
case 'P':
event_part (place);
break;
case 'I':
event_info (place);
break;
case 'V':
parsevoice (place);
break;
case 'Q':
parse_tempo (place);
break;
case 'U':
{
char symbol;
char container;
char *expansion;
skipspace (&place);
/* if ((*place >= 'H') && (*place <= 'Z')) [SS] 2016-09-20 */
if ((*place >= 'A') && (*place <= 'z')) /* [SS] 2016-09-20 */
{
symbol = *place;
place = place + 1;
skipspace (&place);
if (*place == '=')
{
place = place + 1;
skipspace (&place);
if (*place == '!')
{
place = place + 1;
container = '!';
expansion = place;
while ((!iscntrl (*place)) && (*place != '!'))
{
place = place + 1;
};
if (*place != '!')
{
event_error ("No closing ! in U: field");
};
*place = '\0';
}
else
{
container = ' ';
expansion = place;
while (isalnum (*place))
{
place = place + 1;
};
*place = '\0';
};
if (strlen (expansion) > 0)
{
record_abbreviation (symbol, expansion);
event_abbreviation (symbol, expansion, container);
}
else
{
event_error ("Missing term in U: field");
};
}
else
{
event_error ("Missing '=' U: field ignored");
};
}
else
{
event_warning ("only 'H' - 'Z' supported in U: field");
};
};
break;
case 'w':
preparse_words (place);
break;
case 'd':
/* decoration line in abcm2ps */
event_field (key, place); /* [SS] 2010-02-23 */
break;
case 's':
event_field (key, place); /* [SS] 2010-02-23 */
break;
case '+':
if (lastfieldcmd == 'w')
append_fieldcmd (key, place); /*[SS] 2014-08-15 */
break; /* [SS] 2014-09-07 */
default:
event_field (key, place);
};
if (iscomment)
{
parse_precomment (comment);
};
if (key == 'w') lastfieldcmd = 'w'; /* [SS] 2014-08-15 */
else lastfieldcmd = ' '; /* [SS[ 2014-08-15 */
}
char *
parseinlinefield (p)
char *p;
/* parse field within abc line e.g. [K:G] */
{
char *q;
event_startinline ();
q = p;
while ((*q != ']') && (*q != '\0'))
{
q = q + 1;
};
if (*q == ']')
{
*q = '\0';
parsefield (*p, p + 2);
q = q + 1;
}
else
{
event_error ("missing closing ]");
parsefield (*p, p + 2);
};
event_closeinline ();
return (q);
}
/* this function is used by toabc.c [SS] 2011-06-10 */
void
print_inputline_nolinefeed ()
{
if (inputline[sizeof inputline - 1] != '\0')
{
/*
* We are called exclusively by toabc.c,
* and when we are called, event_error is muted,
* so, event_error("input line truncated") does nothing.
* Simulate it with a plain printf. [PHDM 2012-12-01]
*/
printf ("%%Error : input line truncated\n");
}
printf ("%s", inputline);
}
/* this function is used by toabc.c [SS] 2011-06-07 */
void
print_inputline ()
{
print_inputline_nolinefeed ();
printf ("\n");
}
/* [JA] 2020-10-12 */
/* Look for problems in the use of repeats */
static void check_bar_repeats (int bar_type, char *replist)
{
voice_context_t *cv = &voicecode[voicenum];
switch (bar_type) {
case SINGLE_BAR:
break;
case DOUBLE_BAR:
break;
case THIN_THICK:
break;
case THICK_THIN:
break;
case BAR_REP:
if (cv->expect_repeat) {
event_warning ("Expecting repeat, found |:");
};
cv->expect_repeat = 1;
cv->repeat_count = cv->repeat_count + 1;
break;
case REP_BAR:
if (!cv->expect_repeat) {
char error_message[80];
if (cv->repeat_count == 0)
{
#ifdef NO_SNPRINT
sprintf(error_message, "Missing repeat at start ? Unexpected :|%s found", replist);
#else
snprintf(error_message, 80, "Missing repeat at start ? Unexpected :|%s found", replist);
#endif
event_warning (error_message);
}
else
{
#ifdef NO_SNPRINT
sprintf(error_message, "Unexpected :|%s found", replist);
#else
snprintf(error_message, 80, "Unexpected :|%s found", replist);
#endif
event_warning (error_message);
}
};
cv->expect_repeat = 0;
cv->repeat_count = cv->repeat_count + 1;
break;
case BAR1:
if (!cv->expect_repeat) {
if (cv->repeat_count == 0)
{
event_warning ("Missing repeat at start ? found |1");
}
else
{
event_warning ("found |1 in non-repeat section");
}
};
break;
case REP_BAR2:
if (!cv->expect_repeat) {
if (cv->repeat_count == 0)
{
event_warning ("Missing repeat at start ? found :|2");
}
else
{
event_warning ("No repeat expected, found :|2");
}
};
cv->expect_repeat = 0;
cv->repeat_count = cv->repeat_count + 1;
break;
case DOUBLE_REP:
if (!cv->expect_repeat) {
if (cv->repeat_count == 0)
{
event_warning ("Missing repeat at start ? found ::");
}
else
{
event_warning ("No repeat expected, found ::");
}
};
cv->expect_repeat = 1;
cv->repeat_count = cv->repeat_count + 1;
break;
};
}
/* [JA] 2020-10-12 */
static void check_and_call_bar(int bar_type, char *replist)
{
if (repcheck)
{
check_bar_repeats (bar_type, replist);
}
event_bar (bar_type, replist);
}
void
parsemusic (field)
char *field;
/* parse a line of abc notes */
{
char *p;
char c; /* [SS] 2017-04-19 */
char *comment;
char endchar;
int iscomment;
int starcount;
int i;
char playonrep_list[80];
int decorators[DECSIZE];
for (i = 0; i < DECSIZE; i++)
decorators[i] = 0; /* [SS] 2012-03-30 */
event_startmusicline ();
endchar = ' ';
comment = field;
iscomment = 0;
while ((*comment != '\0') && (*comment != '%'))
{
comment = comment + 1;
};
if (*comment == '%')
{
iscomment = 1;
*comment = '\0';
comment = comment + 1;
};
p = field;
skipspace (&p);
while (*p != '\0')
{
lineposition = p - linestart; /* [SS] 2011-07-18 */
if (*p == '.' && *(p+1) == '(') { /* [SS] 2015-04-28 dotted slur */
p = p+1;
event_sluron (1);
p = p+1;
}
if (((*p >= 'a') && (*p <= 'g')) || ((*p >= 'A') && (*p <= 'G')) ||
(strchr ("_^=", *p) != NULL) || (strchr (decorations, *p) != NULL))
{
parsenote (&p);
}
else
{
c = *p; /* [SS] 2017-04-19 */
switch (*p)
{
case '"':
{
struct vstring gchord;
p = p + 1;
initvstring (&gchord);
while ((*p != '"') && (*p != '\0'))
{
addch (*p, &gchord);
p = p + 1;
};
if (*p == '\0')
{
event_error ("Guitar chord name not properly closed");
}
else
{
p = p + 1;
};
event_gchord (gchord.st);
freevstring (&gchord);
break;
};
case '|':
p = p + 1;
switch (*p)
{
case ':':
check_and_call_bar (BAR_REP, "");
p = p + 1;
break;
case '|':
check_and_call_bar (DOUBLE_BAR, "");
p = p + 1;
break;
case ']':
check_and_call_bar (THIN_THICK, "");
p = p + 1;
break;
default:
p = getrep (p, playonrep_list);
check_and_call_bar (SINGLE_BAR, playonrep_list);
};
break;
case ':':
p = p + 1;
switch (*p)
{
case ':':
check_and_call_bar (DOUBLE_REP, "");
p = p + 1;
break;
case '|':
p = p + 1;
p = getrep (p, playonrep_list);
check_and_call_bar (REP_BAR, playonrep_list);
if (*p == ']')
p = p + 1; /* [SS] 2013-10-31 */
break;
/* [JM] 2018-02-22 dotted bar line ... this is legal */
default:
/* [SS] 2018-02-08 introducing DOTTED_BAR */
check_and_call_bar (DOTTED_BAR,"");
};
break;
case ' ':
event_space ();
skipspace (&p);
break;
case TAB:
event_space ();
skipspace (&p);
break;
case '(':
p = p + 1;
{
int t, q, r;
t = 0;
q = 0;
r = 0;
t = readnump (&p);
if ((t != 0) && (*p == ':'))
{
p = p + 1;
q = readnump (&p);
if (*p == ':')
{
p = p + 1;
r = readnump (&p);
};
};
if (t == 0)
{
if (*p == '&') {
p = p+1;
event_start_extended_overlay(); /* [SS] 2015-03-23 */
}
else
event_sluron (1);
}
else /* t != 0 */
{
event_tuple (t, q, r);
};
};
break;
case ')':
p = p + 1;
event_sluroff (0);
break;
case '{':
p = p + 1;
event_graceon ();
ingrace = 1;
break;
case '}':
p = p + 1;
event_graceoff ();
ingrace = 0;
break;
case '[':
p = p + 1;
switch (*p)
{
case '|':
p = p + 1;
check_and_call_bar (THICK_THIN, "");
if (*p == ':') { /* [SS] 2015-04-13 [SDG] 2020-06-04 */
check_and_call_bar (BAR_REP, "");
p = p + 1;
}
break;
default:
if (isdigit (*p))
{
p = getrep (p, playonrep_list);
event_playonrep (playonrep_list);
}
else
{
if (isalpha (*p) && (*(p + 1) == ':'))
{
p = parseinlinefield (p);
}
else
{
lineposition = p - linestart; /* [SS] 2011-07-18 */
/* [SS] 2012-03-30 */
for (i = 0; i < DECSIZE; i++)
chorddecorators[i] =
decorators[i] | decorators_passback[i];
event_chordon (chorddecorators);
parserinchord = 1;
};
};
break;
};
break;
case ']':
p = p + 1;
/*readlen (&chord_n, &chord_m, &p); [SS] 2019-06-06 */
/*if (!inchordflag && *p == '|') { [SS] 2018-12-21 not THICK_THIN bar line*/
if (!parserinchord && *p == '|') { /* [SS] 2019-06-06 not THICK_THIN bar line*/
p = p + 1; /* skip over | */
check_and_call_bar (THIN_THICK, "");}
else {
readlen (&chord_n, &chord_m, &p); /* [SS] 2019-06-06 */
event_chordoff (chord_n, chord_m);
parserinchord = 0;
}
for (i = 0; i < DECSIZE; i++)
{
chorddecorators[i] = 0;
decorators_passback[i] = 0; /* [SS] 2012-03-30 */
}
break;
case '