diff options
Diffstat (limited to 'Build/source/texk/gregorio/gregorio-src/src/gregoriotex/gregoriotex-write.c')
-rw-r--r-- | Build/source/texk/gregorio/gregorio-src/src/gregoriotex/gregoriotex-write.c | 1411 |
1 files changed, 715 insertions, 696 deletions
diff --git a/Build/source/texk/gregorio/gregorio-src/src/gregoriotex/gregoriotex-write.c b/Build/source/texk/gregorio/gregorio-src/src/gregoriotex/gregoriotex-write.c index 524c61b286d..853121708b7 100644 --- a/Build/source/texk/gregorio/gregorio-src/src/gregoriotex/gregoriotex-write.c +++ b/Build/source/texk/gregorio/gregorio-src/src/gregoriotex/gregoriotex-write.c @@ -5,18 +5,18 @@ * Copyright (C) 2008-2015 The Gregorio Project (see CONTRIBUTORS.md) * * This file is part of Gregorio. - * + * * 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 3 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 + * + * You should have received a copy of the GNU General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. */ /** @@ -76,9 +76,11 @@ SHAPE(Flat); SHAPE(Flexus); SHAPE(FlexusLongqueue); SHAPE(FlexusNobar); +SHAPE(FlexusOpenqueue); SHAPE(FlexusOriscus); SHAPE(FlexusOriscusScapus); SHAPE(FlexusOriscusScapusLongqueue); +SHAPE(FlexusOriscusScapusOpenqueue); SHAPE(Linea); SHAPE(LineaPunctum); SHAPE(LineaPunctumCavum); @@ -93,16 +95,22 @@ SHAPE(OriscusReversus); SHAPE(OriscusReversusLineTL); SHAPE(OriscusScapus); SHAPE(OriscusScapusLongqueue); +SHAPE(OriscusScapusOpenqueue); SHAPE(Pes); SHAPE(PesQuadratum); SHAPE(PesQuadratumLongqueue); +SHAPE(PesQuadratumOpenqueue); SHAPE(PesQuassus); SHAPE(PesQuassusLongqueue); +SHAPE(PesQuassusOpenqueue); SHAPE(PesQuilisma); SHAPE(PesQuilismaQuadratum); SHAPE(PesQuilismaQuadratumLongqueue); +SHAPE(PesQuilismaQuadratumOpenqueue); SHAPE(Porrectus); +SHAPE(PorrectusLongqueue); SHAPE(PorrectusFlexus); +SHAPE(PorrectusFlexusLongqueue); SHAPE(PorrectusFlexusNobar); SHAPE(PorrectusNobar); SHAPE(Punctum); @@ -134,12 +142,16 @@ SHAPE(TorculusResupinus); SHAPE(TorculusResupinusQuilisma); SHAPE(Virga); SHAPE(VirgaLongqueue); +SHAPE(VirgaOpenqueue); SHAPE(VirgaReversa); SHAPE(VirgaReversaAscendens); SHAPE(VirgaReversaDescendens); SHAPE(VirgaReversaLongqueue); SHAPE(VirgaReversaLongqueueAscendens); SHAPE(VirgaReversaLongqueueDescendens); +SHAPE(VirgaReversaOpenqueue); +SHAPE(VirgaReversaOpenqueueAscendens); +SHAPE(VirgaReversaOpenqueueDescendens); SHAPE(VirgaStrata); #define LIQ(NAME) static const char *const LIQ_##NAME = #NAME @@ -161,21 +173,6 @@ FUSE(Up); /* the value indicating to GregorioTeX that there is no flat */ #define NO_KEY_FLAT LOWEST_PITCH -/* a helper macro for the following function */ -#define WHILEGLYPH(prevornext) \ - while(glyph) {\ - if (glyph->type == GRE_GLYPH) {\ - note = glyph->u.notes.first_note;\ - while (note) {\ - if (note->u.note.pitch <= LOW_LEDGER_LINE_PITCH) {\ - return true;\ - }\ - note = note->next;\ - }\ - }\ - glyph = glyph->prevornext;\ - } - static __inline signed char pitch_value(const signed char height) { /* right now height == pitch, but this function allows us to change * the offset easily */ @@ -186,73 +183,40 @@ static __inline int bool_to_int(bool value) { return value? 1 : 0; } -/* a function that determines if we must use a long queue or not (less easy - * that it might seem) */ +typedef enum queuetype { + Q_ON_SPACE_BELOW_BOTTOM_LINE, + Q_ON_SPACE_ABOVE_BOTTOM_LINE, + Q_ON_BOTTOM_LINE, + Q_ON_LINE_ABOVE_BOTTOM_LINE +} queuetype; -static bool is_longqueue(const signed char pitch, - const gregorio_glyph *const current_glyph, - const gregorio_element *const current_element) +static queuetype adjusted_queuetype_of(const gregorio_note *const note, + const signed char adjustment) { - gregorio_note *note; - gregorio_glyph *glyph = current_glyph->next; - gregorio_element *element = current_element->next; - switch (pitch - LOWEST_PITCH) { + switch (note->u.note.pitch + adjustment - LOWEST_PITCH) { + case 0: + return Q_ON_SPACE_BELOW_BOTTOM_LINE; case 1: + return Q_ON_BOTTOM_LINE; + case 2: + return note->supposed_low_ledger_line? Q_ON_SPACE_ABOVE_BOTTOM_LINE + : Q_ON_SPACE_BELOW_BOTTOM_LINE; + case 3: + return note->supposed_low_ledger_line? Q_ON_LINE_ABOVE_BOTTOM_LINE + : Q_ON_BOTTOM_LINE; case 5: case 7: case 9: case 11: case 13: - return true; - case 3: - /* we first look forward to see if there is a note underneath c */ - WHILEGLYPH(next); - if (element && element->type == GRE_SPACE - && (element->u.misc.unpitched.info.space == SP_NEUMATIC_CUT - || element->u.misc.unpitched.info.space == SP_LARGER_SPACE - || element->u.misc.unpitched.info.space - == SP_NEUMATIC_CUT_NB - || element->u.misc.unpitched.info.space - == SP_LARGER_SPACE_NB)) { - element = element->next; - } - if (element && element->type == GRE_ELEMENT) { - glyph = element->u.first_glyph; - WHILEGLYPH(next); - } - /* and now something completely different */ - glyph = current_glyph->previous; - element = current_element->previous; - WHILEGLYPH(previous); - if (element && element->type == GRE_SPACE - && (element->u.misc.unpitched.info.space == SP_NEUMATIC_CUT - || element->u.misc.unpitched.info.space == SP_LARGER_SPACE - || element->u.misc.unpitched.info.space - == SP_NEUMATIC_CUT_NB - || element->u.misc.unpitched.info.space - == SP_LARGER_SPACE_NB)) { - element = element->previous; - } - if (element && element->type == GRE_ELEMENT) { - glyph = element->u.first_glyph; - WHILEGLYPH(next); - } - return false; + return Q_ON_LINE_ABOVE_BOTTOM_LINE; default: - return false; + return Q_ON_SPACE_ABOVE_BOTTOM_LINE; } } -/* inline functions that we will use to determine if we need a short bar or - * not */ - -/* we define d to be short instead of long... may induce errors, but fixes - * some too */ -static __inline bool is_shortqueue(const signed char pitch, - const gregorio_glyph *const glyph, - const gregorio_element *const element) -{ - return !is_longqueue(pitch, glyph, element); +static queuetype queuetype_of(const gregorio_note *const note) { + return adjusted_queuetype_of(note, 0); } static grestyle_style gregoriotex_ignore_style = ST_NO_STYLE; @@ -268,7 +232,7 @@ static grestyle_style gregoriotex_next_ignore_style = ST_NO_STYLE; * 'InitioDebilisDeminutus' * 'InitioDebilisAscendens' * 'InitioDebilisDescendens' - * + * * They also are and must be the same as in squarize.py. */ @@ -333,11 +297,8 @@ static __inline int compute_ambitus(const gregorio_note *const current_note) } else { ambitus = first - second; } - if (ambitus < 1 || ambitus > MAX_AMBITUS) { - gregorio_messagef("compute_ambitus", VERBOSITY_ERROR, 0, - _("unsupported ambitus: %d"), ambitus); - return 0; - } + gregorio_assert2(ambitus >= 1 && ambitus <= MAX_AMBITUS, compute_ambitus, + "unsupported ambitus: %d", ambitus, return 0); return ambitus; } @@ -357,30 +318,22 @@ static const char *compute_glyph_name(const gregorio_glyph *const glyph, ? previous->u.notes.fuse_to_next_glyph : 0; /* then we start making our formula */ - if (!glyph) { - gregorio_message(_("called with NULL pointer"), - "compute_glyph_name", VERBOSITY_ERROR, 0); - return ""; - } - if (!glyph->u.notes.first_note) { - gregorio_message(_("called with a glyph that have no note"), - "compute_glyph_name", VERBOSITY_ERROR, 0); - return ""; - } + gregorio_assert(glyph, compute_glyph_name, "called with NULL pointer", + return ""); + gregorio_assert(glyph->u.notes.first_note, compute_glyph_name, + "called with a glyph that have no note", return ""); fuse_to_next_note = glyph->u.notes.fuse_to_next_glyph; switch (glyph->u.notes.glyph_type) { case G_PODATUS: - if (!is_tail_liquescentia(glyph->u.notes.liquescentia) - && fuse_from_previous_note < 0) { - /* a normal pes cannot be fused from above */ - break; - } + gregorio_assert(is_tail_liquescentia(glyph->u.notes.liquescentia) + || fuse_from_previous_note >= 0, compute_glyph_name, + "unexpected fusible podatus", break); /* else fall through */ case G_PUNCTUM: case G_FLEXA: - /* directionally head-fusable */ + /* directionally head-fusible */ if (fuse_from_previous_note < -1 && glyph->u.notes.first_note->u.note.shape != S_QUILISMA && glyph->u.notes.first_note->u.note.shape @@ -404,20 +357,20 @@ static const char *compute_glyph_name(const gregorio_glyph *const glyph, break; default: - /* not directionally head-fusable */ + /* not directionally head-fusible */ break; } switch (glyph->u.notes.glyph_type) { case G_FLEXA: if (fuse_to_next_note <= 0) { - /* a flexa is only fusable up */ + /* a flexa is only fusible up */ break; } /* else fall through */ case G_VIRGA_REVERSA: case G_PUNCTUM: - /* tail-fusable */ + /* tail-fusible */ if (fuse_to_next_note < 0) { fuse_tail = FUSE_Down; fuse_ambitus = -fuse_to_next_note; @@ -432,13 +385,14 @@ static const char *compute_glyph_name(const gregorio_glyph *const glyph, break; default: - /* not tail-fusable */ + /* not tail-fusible */ break; } if ((*fuse_tail && shape == SHAPE_OriscusReversus) || ((shape == SHAPE_OriscusReversus || shape == SHAPE_OriscusScapus - || shape == SHAPE_OriscusScapusLongqueue) + || shape == SHAPE_OriscusScapusLongqueue + || shape == SHAPE_OriscusScapusOpenqueue) && is_fused(glyph->u.notes.liquescentia))) { shape = SHAPE_Oriscus; } @@ -469,16 +423,14 @@ static const char *compute_glyph_name(const gregorio_glyph *const glyph, tex_ambitus[fuse_ambitus], liquescentia, fuse_tail); return buf; } - if (!current_note->next) { - gregorio_message(_("called with a multi-note glyph that has only " - "one note"), "compute_glyph_name", VERBOSITY_ERROR, 0); - return ""; - } - if (!(ambitus1 = compute_ambitus(current_note))) { - return ""; - } + gregorio_assert(current_note->next, compute_glyph_name, + "called with a multi-note glyph that has only one note", return ""); + gregorio_assert((ambitus1 = compute_ambitus(current_note)), + compute_glyph_name, "unexpected unison on multi-note glyph", + return ""); if (is_fused(glyph->u.notes.liquescentia)) { - if (shape == SHAPE_Flexus || shape == SHAPE_FlexusLongqueue) { + if (shape == SHAPE_Flexus || shape == SHAPE_FlexusLongqueue + || shape == SHAPE_FlexusOpenqueue) { if (fuse_to_next_note) { fuse_head = ""; } @@ -487,11 +439,13 @@ static const char *compute_glyph_name(const gregorio_glyph *const glyph, } else { shape = SHAPE_FlexusNobar; } - } else if (shape == SHAPE_Porrectus) { + } else if (shape == SHAPE_Porrectus + || shape == SHAPE_PorrectusLongqueue) { shape = SHAPE_PorrectusNobar; } } else { - if (fuse_to_next_note && shape == SHAPE_FlexusLongqueue) { + if (fuse_to_next_note && (shape == SHAPE_FlexusLongqueue + || shape == SHAPE_FlexusOpenqueue)) { /* a porrectus-like flexus has no longqueue variant */ shape = SHAPE_Flexus; } @@ -508,9 +462,9 @@ static const char *compute_glyph_name(const gregorio_glyph *const glyph, liquescentia, fuse_tail); return buf; } - if (!(ambitus2 = compute_ambitus(current_note))) { - return ""; - } + gregorio_assert((ambitus2 = compute_ambitus(current_note)), + compute_glyph_name, "unexpected unison on multi-note glyph", + return ""); current_note = current_note->next; if (!current_note->next) { gregorio_snprintf(buf, BUFSIZE, "%s%s%s%s%s%s%s", fuse_head, shape, @@ -518,26 +472,63 @@ static const char *compute_glyph_name(const gregorio_glyph *const glyph, tex_ambitus[fuse_ambitus], liquescentia, fuse_tail); return buf; } - if (!(ambitus3 = compute_ambitus(current_note))) { - return ""; - } + gregorio_assert((ambitus3 = compute_ambitus(current_note)), + compute_glyph_name, "unexpected unison on multi-note glyph", + return ""); gregorio_snprintf(buf, BUFSIZE, "%s%s%s%s%s%s%s%s", fuse_head, shape, tex_ambitus[ambitus1], tex_ambitus[ambitus2], tex_ambitus[ambitus3], tex_ambitus[fuse_ambitus], liquescentia, fuse_tail); return buf; } +static const char *fusible_queued_shape(const gregorio_note *const note, + const gregorio_glyph *const glyph, + const char *const base_shape, const char *const longqueue_shape, + const char *const openqueue_shape) +{ + const char *name = ""; + if (glyph->u.notes.fuse_to_next_glyph < 0) { + /* queue size depends on the following note if fused down */ + if (glyph->u.notes.fuse_to_next_glyph == -1) { + switch (adjusted_queuetype_of(note, + glyph->u.notes.fuse_to_next_glyph)) { + case Q_ON_SPACE_BELOW_BOTTOM_LINE: + case Q_ON_BOTTOM_LINE: + name = openqueue_shape; + break; + case Q_ON_SPACE_ABOVE_BOTTOM_LINE: + name = base_shape; + break; + case Q_ON_LINE_ABOVE_BOTTOM_LINE: + name = longqueue_shape; + break; + } + } else { + name = base_shape; + } + } else { + switch (queuetype_of(note)) { + case Q_ON_SPACE_BELOW_BOTTOM_LINE: + case Q_ON_SPACE_ABOVE_BOTTOM_LINE: + name = base_shape; + break; + case Q_ON_BOTTOM_LINE: + case Q_ON_LINE_ABOVE_BOTTOM_LINE: + name = longqueue_shape; + break; + } + } + return compute_glyph_name(glyph, name, LG_NONE, true); +} + static const char *gregoriotex_determine_note_glyph_name(gregorio_note *note, - gregorio_glyph *glyph, gregorio_element *element, gtex_alignment *type) + gregorio_glyph *glyph, gtex_alignment *type) { static char buf[128]; - const char *name; + const char *name = ""; - if (!note) { - gregorio_message(_("called with NULL pointer"), - "gregoriotex_determine_note_glyph_name", VERBOSITY_ERROR, 0); - return ""; - } + gregorio_assert(note, gregoriotex_determine_note_glyph_name, + "called with NULL pointer", return ""); *type = AT_ONE_NOTE; switch (note->u.note.shape) { @@ -565,17 +556,29 @@ static const char *gregoriotex_determine_note_glyph_name(gregorio_note *note, case S_LINEA_PUNCTUM_CAVUM: return SHAPE_LineaPunctumCavum; case S_VIRGA: - if (is_shortqueue(note->u.note.pitch, glyph, element)) { + switch (queuetype_of(note)) { + case Q_ON_SPACE_ABOVE_BOTTOM_LINE: return SHAPE_Virga; - } - return SHAPE_VirgaLongqueue; + case Q_ON_SPACE_BELOW_BOTTOM_LINE: + case Q_ON_BOTTOM_LINE: + return SHAPE_VirgaOpenqueue; + case Q_ON_LINE_ABOVE_BOTTOM_LINE: + return SHAPE_VirgaLongqueue; + } /* all cases return, so this line is not hit; LCOV_EXCL_LINE */ case S_VIRGA_REVERSA: switch (note->u.note.liquescentia) { case L_AUCTUS_ASCENDENS: - if (is_shortqueue(note->u.note.pitch, glyph, element)) { + switch (queuetype_of(note)) { + case Q_ON_SPACE_BELOW_BOTTOM_LINE: + case Q_ON_SPACE_ABOVE_BOTTOM_LINE: name = SHAPE_VirgaReversaAscendens; - } else { + break; + case Q_ON_BOTTOM_LINE: + name = SHAPE_VirgaReversaOpenqueueAscendens; + break; + case Q_ON_LINE_ABOVE_BOTTOM_LINE: name = SHAPE_VirgaReversaLongqueueAscendens; + break; } if (note->u.note.pitch - LOWEST_PITCH == 3) { /* if we're on the 'd' line, the queue could be long or short */ @@ -585,17 +588,18 @@ static const char *gregoriotex_determine_note_glyph_name(gregorio_note *note, } return name; case L_AUCTUS_DESCENDENS: - if (is_shortqueue(note->u.note.pitch, glyph, element)) { + switch (queuetype_of(note)) { + case Q_ON_SPACE_BELOW_BOTTOM_LINE: + case Q_ON_SPACE_ABOVE_BOTTOM_LINE: return SHAPE_VirgaReversaDescendens; - } - return SHAPE_VirgaReversaLongqueueDescendens; + case Q_ON_BOTTOM_LINE: + return SHAPE_VirgaReversaOpenqueueDescendens; + case Q_ON_LINE_ABOVE_BOTTOM_LINE: + return SHAPE_VirgaReversaLongqueueDescendens; + } /* all cases return, so this line is not hit; LCOV_EXCL_LINE */ default: - if (is_shortqueue(note->u.note.pitch, glyph, element)) { - return compute_glyph_name(glyph, SHAPE_VirgaReversa, LG_NONE, - true); - } - return compute_glyph_name(glyph, SHAPE_VirgaReversaLongqueue, - LG_NONE, true); + return fusible_queued_shape(note, glyph, SHAPE_VirgaReversa, + SHAPE_VirgaReversaLongqueue, SHAPE_VirgaReversaOpenqueue); } case S_ORISCUS_ASCENDENS: *type = AT_ORISCUS; @@ -610,12 +614,8 @@ static const char *gregoriotex_determine_note_glyph_name(gregorio_note *note, *type = AT_QUILISMA; return compute_glyph_name(glyph, SHAPE_Quilisma, LG_NONE, true); case S_ORISCUS_SCAPUS: - if (is_shortqueue(note->u.note.pitch, glyph, element)) { - return compute_glyph_name(glyph, SHAPE_OriscusScapus, LG_NONE, - true); - } - return compute_glyph_name(glyph, SHAPE_OriscusScapusLongqueue, LG_NONE, - true); + return fusible_queued_shape(note, glyph, SHAPE_OriscusScapus, + SHAPE_OriscusScapusLongqueue, SHAPE_OriscusScapusOpenqueue); case S_STROPHA: *type = AT_STROPHA; if (!(note->u.note.liquescentia & @@ -625,10 +625,14 @@ static const char *gregoriotex_determine_note_glyph_name(gregorio_note *note, /* else fall through to next case */ case S_STROPHA_AUCTA: *type = AT_STROPHA; - if (is_shortqueue(note->u.note.pitch, glyph, element)) { + switch (queuetype_of(note)) { + case Q_ON_SPACE_BELOW_BOTTOM_LINE: + case Q_ON_SPACE_ABOVE_BOTTOM_LINE: return SHAPE_StrophaAucta; - } - return SHAPE_StrophaAuctaLongtail; + case Q_ON_BOTTOM_LINE: + case Q_ON_LINE_ABOVE_BOTTOM_LINE: + return SHAPE_StrophaAuctaLongtail; + } /* all cases return, so this line is not hit; LCOV_EXCL_LINE */ case S_PUNCTUM_CAVUM_INCLINATUM: *type = AT_PUNCTUM_INCLINATUM; return SHAPE_PunctumCavumInclinatum; @@ -651,38 +655,160 @@ static const char *gregoriotex_determine_note_glyph_name(gregorio_note *note, case S_NATURAL: return SHAPE_Natural; default: - gregorio_messagef("gregoriotex_determine_note_glyph_name", - VERBOSITY_ERROR, 0, _("called with unknown shape: %s"), + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_fail2(gregoriotex_determine_note_glyph_name, + "called with unknown shape: %s", gregorio_shape_to_string(note->u.note.shape)); return ""; + /* LCOV_EXCL_STOP */ } } +static __inline gregorio_note *first_note_of( + const gregorio_glyph *const glyph) { + assert(glyph->type == GRE_GLYPH); + assert(glyph->u.notes.first_note); + assert(glyph->u.notes.first_note->type == GRE_NOTE); + return glyph->u.notes.first_note; +} + +static __inline signed char first_pitch_of(const gregorio_glyph *const glyph) { + return first_note_of(glyph)->u.note.pitch; +} + +static __inline gregorio_note *second_note_of( + const gregorio_glyph *const glyph) { + assert(glyph->type == GRE_GLYPH); + assert(glyph->u.notes.first_note); + assert(glyph->u.notes.first_note->type == GRE_NOTE); + assert(glyph->u.notes.first_note->next); + assert(glyph->u.notes.first_note->next->type == GRE_NOTE); + return glyph->u.notes.first_note->next; +} + +static __inline signed char second_pitch_of(const gregorio_glyph *const glyph) { + return second_note_of(glyph)->u.note.pitch; +} + +static __inline gregorio_note *third_note_of( + const gregorio_glyph *const glyph) { + assert(glyph->type == GRE_GLYPH); + assert(glyph->u.notes.first_note); + assert(glyph->u.notes.first_note->type == GRE_NOTE); + assert(glyph->u.notes.first_note->next); + assert(glyph->u.notes.first_note->next->type == GRE_NOTE); + assert(glyph->u.notes.first_note->next->next); + assert(glyph->u.notes.first_note->next->next->type == GRE_NOTE); + return glyph->u.notes.first_note->next->next; +} + +static __inline signed char third_pitch_of(const gregorio_glyph *const glyph) { + return third_note_of(glyph)->u.note.pitch; +} + +static __inline const char *porrectus_shape(const gregorio_glyph *const glyph, + const char *base_shape, const char *longqueue_shape) { + const gregorio_note *const first_note = first_note_of(glyph); + if ((glyph->u.notes.glyph_type == G_PORRECTUS + && (glyph->u.notes.liquescentia & L_DEMINUTUS)) + || first_note->u.note.pitch - second_pitch_of(glyph) == 1) { + switch (queuetype_of(first_note)) { + case Q_ON_SPACE_BELOW_BOTTOM_LINE: + case Q_ON_SPACE_ABOVE_BOTTOM_LINE: + case Q_ON_BOTTOM_LINE: + return base_shape; + case Q_ON_LINE_ABOVE_BOTTOM_LINE: + return longqueue_shape; + } + } /* must be optimized out; LCOV_EXCL_LINE */ + return base_shape; +} + +static __inline const char *flexus_shape(const gregorio_glyph *const glyph, + const signed char ambitus, const char *base_shape, + const char *longqueue_shape, const char *openqueue_shape) { + const bool ambitus_one = (ambitus == 1); + switch (queuetype_of(second_note_of(glyph))) { + case Q_ON_SPACE_BELOW_BOTTOM_LINE: + if (ambitus_one) { + return openqueue_shape; + } + /* else fall through */ + case Q_ON_SPACE_ABOVE_BOTTOM_LINE: + /* at ambitus one, long and short are swapped becuase the queue where + * the second note is on a space is longer than on a line */ + return ambitus_one? longqueue_shape : base_shape; + case Q_ON_BOTTOM_LINE: + if (ambitus_one) { + return openqueue_shape; + } + /* else fall through */ + case Q_ON_LINE_ABOVE_BOTTOM_LINE: + /* at ambitus one, long and short are swapped becuase the queue where + * the second note is on a line is shorter than on a space */ + return ambitus_one? base_shape : longqueue_shape; + } + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_fail(flexus_shape, "unexpected queue length"); + return base_shape; + /* LCOV_EXCL_STOP */ +} + +static __inline const char *quadratum_shape(const gregorio_glyph *const glyph, + const char *base_shape, const char *longqueue_shape, + const char *openqueue_shape) { + const bool ambitus_one = + (second_pitch_of(glyph) - first_pitch_of(glyph) == 1); + if (!is_tail_liquescentia(glyph->u.notes.liquescentia)) { + switch (queuetype_of(first_note_of(glyph))) { + case Q_ON_SPACE_BELOW_BOTTOM_LINE: + if (ambitus_one) { + return openqueue_shape; + } + /* else fall through */ + case Q_ON_SPACE_ABOVE_BOTTOM_LINE: + /* at ambitus one, long and short are swapped becuase the queue + * where the first note is on a space is longer than on a line */ + return ambitus_one? longqueue_shape : base_shape; + case Q_ON_BOTTOM_LINE: + if (ambitus_one) { + return openqueue_shape; + } + /* else fall through */ + case Q_ON_LINE_ABOVE_BOTTOM_LINE: + /* at ambitus one, long and short are swapped becuase the queue + * where the first note is on a line is shorter than on a space */ + return ambitus_one? base_shape : longqueue_shape; + } + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_fail(quadratum_shape, "unexpected queue length"); + /* fall out of the conditional */ + } + /* LCOV_EXCL_STOP */ + return base_shape; +} + /* the function that calculates the number of the glyph. It also * calculates the type, used for determining the position of signs. Type is * very basic, it is only the global dimensions : torculus, one_note, etc. */ const char *gregoriotex_determine_glyph_name(const gregorio_glyph *const glyph, - const gregorio_element *const element, gtex_alignment *const type, - gtex_type *const gtype) + gtex_alignment *const type, gtex_type *const gtype) { const char *shape = NULL; gtex_glyph_liquescentia ltype; - char pitch = 0; - if (!glyph) { - gregorio_message(_("called with NULL pointer"), - "gregoriotex_determine_glyph_name", VERBOSITY_ERROR, 0); - return ""; - } - if (!glyph->u.notes.first_note) { - gregorio_message(_("called with a glyph that has no note"), - "gregorio_tex_determine_glyph_name", VERBOSITY_ERROR, 0); - return ""; - } + signed char ambitus; + gregorio_note *second_note; + gregorio_assert(glyph, gregoriotex_determine_glyph_name, + "called with NULL pointer", return ""); + gregorio_assert(glyph->u.notes.first_note, gregoriotex_determine_glyph_name, + "called with a glyph that has no note", return ""); *gtype = T_ONE_NOTE; switch (glyph->u.notes.glyph_type) { case G_PODATUS: - pitch = glyph->u.notes.first_note->next->u.note.pitch; switch (glyph->u.notes.first_note->u.note.shape) { case S_QUILISMA: *type = AT_QUILISMA; @@ -702,15 +828,9 @@ const char *gregoriotex_determine_glyph_name(const gregorio_glyph *const glyph, case S_ORISCUS_DESCENDENS: case S_ORISCUS_SCAPUS: *type = AT_ORISCUS; - /* TODO: we could factorize this code */ - if (!is_tail_liquescentia(glyph->u.notes.liquescentia) - && is_longqueue(pitch, glyph, element)) { - *gtype = T_PESQUASSUS_LONGQUEUE; - shape = SHAPE_PesQuassusLongqueue; - } else { - *gtype = T_PESQUASSUS; - shape = SHAPE_PesQuassus; - } + *gtype = T_PESQUASSUS; + shape = quadratum_shape(glyph, SHAPE_PesQuassus, + SHAPE_PesQuassusLongqueue, SHAPE_PesQuassusOpenqueue); ltype = LG_NO_INITIO; break; default: @@ -728,60 +848,36 @@ const char *gregoriotex_determine_glyph_name(const gregorio_glyph *const glyph, } break; case G_PES_QUADRATUM: - pitch = glyph->u.notes.first_note->next->u.note.pitch; switch (glyph->u.notes.first_note->u.note.shape) { case S_QUILISMA: *type = AT_QUILISMA; - if (!is_tail_liquescentia(glyph->u.notes.liquescentia) - && is_longqueue(pitch, glyph, element)) { - *gtype = T_PESQUILISMAQUADRATUM_LONGQUEUE; - shape = SHAPE_PesQuilismaQuadratumLongqueue; - } else { - *gtype = T_PESQUILISMAQUADRATUM; - shape = SHAPE_PesQuilismaQuadratum; - } - ltype = LG_NO_INITIO; - break; - case S_ORISCUS_ASCENDENS: - case S_ORISCUS_DESCENDENS: - *type = AT_ORISCUS; - if (!is_tail_liquescentia(glyph->u.notes.liquescentia) - && is_longqueue(pitch, glyph, element)) { - *gtype = T_PESQUASSUS_LONGQUEUE; - shape = SHAPE_PesQuadratumLongqueue; - } else { - *gtype = T_PESQUASSUS; - shape = SHAPE_PesQuassus; - } + *gtype = T_PESQUILISMAQUADRATUM; + shape = quadratum_shape(glyph, SHAPE_PesQuilismaQuadratum, + SHAPE_PesQuilismaQuadratumLongqueue, + SHAPE_PesQuilismaQuadratumOpenqueue); ltype = LG_NO_INITIO; break; default: *type = AT_ONE_NOTE; - if (!is_tail_liquescentia(glyph->u.notes.liquescentia) - && is_longqueue(pitch, glyph, element)) { - *gtype = T_PESQUADRATUM_LONGQUEUE; - shape = SHAPE_PesQuadratumLongqueue; - } else { - *gtype = T_PESQUADRATUM; - shape = SHAPE_PesQuadratum; - } + *gtype = T_PESQUADRATUM; + shape = quadratum_shape(glyph, SHAPE_PesQuadratum, + SHAPE_PesQuadratumLongqueue, SHAPE_PesQuadratumOpenqueue); ltype = LG_ALL; break; } break; case G_VIRGA_STRATA: - pitch = glyph->u.notes.first_note->next->u.note.pitch; *type = AT_ONE_NOTE; *gtype = T_VIRGA_STRATA; shape = SHAPE_VirgaStrata; ltype = LG_ALL; break; case G_FLEXA: - pitch = glyph->u.notes.first_note->u.note.pitch; + ambitus = first_pitch_of(glyph) - second_pitch_of(glyph); if (glyph->u.notes.liquescentia & L_DEMINUTUS) { *type = AT_FLEXUS_DEMINUTUS; } else { - if (pitch - glyph->u.notes.first_note->next->u.note.pitch == 1) { + if (ambitus == 1) { *type = AT_FLEXUS_1; } else { *type = AT_FLEXUS; @@ -796,26 +892,17 @@ const char *gregoriotex_determine_glyph_name(const gregorio_glyph *const glyph, break; case S_ORISCUS_SCAPUS: - if (is_shortqueue(pitch, glyph, element)) { - *gtype = T_FLEXUS_ORISCUS_SCAPUS; - shape = SHAPE_FlexusOriscusScapus; - } else { - *gtype = T_FLEXUS_ORISCUS_SCAPUS_LONGQUEUE; - shape = SHAPE_FlexusOriscusScapusLongqueue; - } + *gtype = T_FLEXUS_ORISCUS_SCAPUS; + shape = flexus_shape(glyph, ambitus, SHAPE_FlexusOriscusScapus, + SHAPE_FlexusOriscusScapusLongqueue, + SHAPE_FlexusOriscusScapusOpenqueue); ltype = LG_NO_INITIO; break; default: - if (is_shortqueue(pitch, glyph, element)) { - *gtype = glyph->u.notes.fuse_to_next_glyph? T_PORRECTUS - : T_FLEXUS; - shape = SHAPE_Flexus; - } else { - *gtype = glyph->u.notes.fuse_to_next_glyph ? T_PORRECTUS - : T_FLEXUS_LONGQUEUE; - shape = SHAPE_FlexusLongqueue; - } + *gtype = glyph->u.notes.fuse_to_next_glyph? T_PORRECTUS : T_FLEXUS; + shape = flexus_shape(glyph, ambitus, SHAPE_Flexus, + SHAPE_FlexusLongqueue, SHAPE_FlexusOpenqueue); ltype = LG_NO_INITIO; break; } @@ -850,7 +937,8 @@ const char *gregoriotex_determine_glyph_name(const gregorio_glyph *const glyph, case G_PORRECTUS: *type = AT_PORRECTUS; *gtype = T_PORRECTUS; - shape = SHAPE_Porrectus; + shape = porrectus_shape(glyph, SHAPE_Porrectus, + SHAPE_PorrectusLongqueue); ltype = LG_NO_INITIO; break; case G_TORCULUS_RESUPINUS: @@ -867,7 +955,8 @@ const char *gregoriotex_determine_glyph_name(const gregorio_glyph *const glyph, case G_PORRECTUS_FLEXUS: *type = AT_PORRECTUS; *gtype = T_PORRECTUS_FLEXUS; - shape = SHAPE_PorrectusFlexus; + shape = porrectus_shape(glyph, SHAPE_PorrectusFlexus, + SHAPE_PorrectusFlexusLongqueue); ltype = LG_NO_INITIO; break; case G_PORRECTUS_NO_BAR: @@ -883,24 +972,21 @@ const char *gregoriotex_determine_glyph_name(const gregorio_glyph *const glyph, ltype = LG_NO_INITIO; break; case G_ANCUS: - if (glyph->u.notes.liquescentia & L_DEMINUTUS) { - if (pitch - glyph->u.notes.first_note->next->u.note.pitch == 1) { - *type = AT_FLEXUS_1; - } else { - *type = AT_FLEXUS; - } - if (is_shortqueue(pitch, glyph, element)) { - *gtype = T_ANCUS; - shape = SHAPE_Ancus; - } else { - *gtype = T_ANCUS_LONGQUEUE; - shape = SHAPE_AncusLongqueue; - } - ltype = LG_ONLY_DEMINUTUS; + *type = AT_ONE_NOTE; + gregorio_assert(glyph->u.notes.liquescentia & L_DEMINUTUS, + gregoriotex_determine_glyph_name, + "encountered non-deminutus ancus", break); + second_note = second_note_of(glyph); + if (first_pitch_of(glyph) - second_note->u.note.pitch == 1) { + *type = AT_FLEXUS_1; } else { - /* TODO... */ - *type = AT_ONE_NOTE; + *type = AT_FLEXUS; } + *gtype = T_ANCUS; + ambitus = first_pitch_of(glyph) - second_pitch_of(glyph); + shape = flexus_shape(glyph, ambitus, SHAPE_Ancus, + SHAPE_AncusLongqueue, SHAPE_Ancus); + ltype = LG_ONLY_DEMINUTUS; break; case G_SCANDICUS: *type = AT_ONE_NOTE; @@ -910,14 +996,10 @@ const char *gregoriotex_determine_glyph_name(const gregorio_glyph *const glyph, break; case G_SALICUS: *type = AT_ONE_NOTE; - pitch = glyph->u.notes.first_note->next->next->u.note.pitch; - if (is_longqueue(pitch, glyph, element)) { - *gtype = T_SALICUS_LONGQUEUE; - shape = SHAPE_SalicusLongqueue; - } else { - *gtype = T_SALICUS; - shape = SHAPE_Salicus; - } + *gtype = T_SALICUS; + ambitus = third_pitch_of(glyph) - second_pitch_of(glyph); + shape = flexus_shape(glyph, ambitus, SHAPE_Salicus, + SHAPE_SalicusLongqueue, SHAPE_Salicus); ltype = LG_NO_INITIO; break; case G_SALICUS_FLEXUS: @@ -926,9 +1008,7 @@ const char *gregoriotex_determine_glyph_name(const gregorio_glyph *const glyph, shape = SHAPE_SalicusFlexus; ltype = LG_NO_INITIO; break; - case G_ONE_NOTE: case G_PUNCTUM_INCLINATUM: - case G_TRIGONUS: case G_PUNCTA_INCLINATA: case G_2_PUNCTA_INCLINATA_DESCENDENS: case G_3_PUNCTA_INCLINATA_DESCENDENS: @@ -953,10 +1033,13 @@ const char *gregoriotex_determine_glyph_name(const gregorio_glyph *const glyph, *type = AT_ONE_NOTE; break; default: - gregorio_messagef("gregoriotex_determine_glyph_name", VERBOSITY_ERROR, - 0, _("called with unknown glyph: %s"), + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_fail2(gregoriotex_determine_glyph_name, + "called with unknown glyph: %s", gregorio_glyph_type_to_string(glyph->u.notes.glyph_type)); break; + /* LCOV_EXCL_STOP */ } if (shape) { shape = compute_glyph_name(glyph, shape, ltype, false); @@ -977,9 +1060,8 @@ const char *gregoriotex_determine_glyph_name(const gregorio_glyph *const glyph, */ static void gregoriotex_write_voice_info(FILE *f, gregorio_voice_info *voice_info) { - if (!f || !voice_info) { - return; - } + gregorio_assert(f && voice_info, gregoriotex_write_voice_info, + "file or voice_info passed as NULL", return); } /* this function indicates if the syllable is the last of the line. If it's the @@ -1014,50 +1096,7 @@ static bool gregoriotex_is_last_of_line(gregorio_syllable *syllable) } /* - * A small helper for the following function - */ - -static __inline bool is_clef(gregorio_type x) -{ - return x == GRE_CLEF; -} - -/* - * This function is used in write_syllable, it detects if the syllable is like - * (c4), (::c4), (z0c4) or (z0::c4). It returns the gregorio_element of the - * clef change. - */ -static gregorio_element *gregoriotex_syllable_is_clef_change(gregorio_syllable - *syllable) -{ - gregorio_element *element; - if (!syllable || !syllable->elements || !syllable->elements[0]) { - return NULL; - } - element = syllable->elements[0]; - /* we just detect the foud cases */ - if (element->type == GRE_CUSTOS && element->next - && (is_clef(element->next->type)) && !element->next->next) { - return element->next; - } - if (element->type == GRE_BAR && element->next - && (is_clef(element->next->type)) && !element->next->next) { - return element->next; - } - if ((is_clef(element->type)) && !element->next) { - return element; - } - if (element->type == GRE_CUSTOS && element->next - && element->next->type == GRE_BAR && element->next->next - && (is_clef(element->next->next->type)) - && !element->next->next->next) { - return element->next->next; - } - return NULL; -} - -/* - * ! @brief Prints the beginning of each text style + * ! @brief Prints the beginning of each text style */ static void gtex_write_begin(FILE *f, grestyle_style style) { @@ -1087,6 +1126,9 @@ static void gtex_write_begin(FILE *f, grestyle_style style) case ST_COLORED: fprintf(f, "\\GreColored{"); break; + case ST_ELISION: + fprintf(f, "\\GreElision{"); + break; case ST_FIRST_WORD: fprintf(f, "\\GreFirstWord{"); break; @@ -1123,101 +1165,103 @@ static void gtex_write_end(FILE *f, grestyle_style style) } } +static __inline void tex_escape_text(FILE *const f, const char *text) +{ + /* We escape these characters into \string\ddd (where ddd is the decimal + * ASCII value of the character) for most escapes, and into \string\n for + * newlines. We do it this way to get the "raw" string values through TeX + * and into Lua, where the sequences become \ddd and \n respectively and + * are translated into their byte values. Lua can then decide whether the + * full strings should be evaluated by TeX as TeX or as strings */ + for (; *text; ++text) { + switch (*text) { + case '\\': + case '{': + case '}': + case '~': + case '%': /* currently, we'll never get %, but handle it anyway */ + case '#': + case '"': + /* these characters have special meaning to TeX */ + fprintf(f, "\\string\\%03d", *text); + break; + case '\n': + fprintf(f, "\\string\\n"); + break; + case '\r': + /* ignore */ + break; + default: + /* UTF-8 multibyte sequences will fall into here, which is fine */ + fputc(*text, f); + break; + } + } +} + +static __inline void tex_escape_wtext(FILE *const f, const grewchar *text) +{ + /* We escape these characters into \string\ddd (where ddd is the decimal + * ASCII value of the character) for most escapes, and into \string\n for + * newlines. We do it this way to get the "raw" string values through TeX + * and into Lua, where the sequences become \ddd and \n respectively and + * are translated into their byte values. Lua can then decide whether the + * full strings should be evaluated by TeX as TeX or as strings */ + for (; *text; ++text) { + switch (*text) { + case L'\\': + case L'{': + case L'}': + case L'~': + case L'%': /* currently, we'll never get %, but handle it anyway */ + case L'#': + case L'"': + /* these characters have special meaning to TeX */ + fprintf(f, "\\string\\%03d", *text); + break; + case L'\n': + fprintf(f, "\\string\\n"); + break; + case L'\r': + /* ignore */ + break; + default: + gregorio_print_unichar(f, *text); + break; + } + } +} + /* * ! @brief Writes GregorioTeX special characters. This function takes the - * special characters as input (i.e. from gabc representation), and writes them - * * in GregorioTeX form. + * special characters as input (i.e. from gabc representation), and writes them + * * in GregorioTeX form. */ -static void gtex_write_special_char(FILE *f, grewchar *special_char) +static void gtex_write_special_char(FILE *f, const grewchar *special_char) { - if (!gregorio_wcsbufcmp(special_char, "A/")) { - fprintf(f, "\\Abar{}"); - return; - } - if (!gregorio_wcsbufcmp(special_char, "%")) { - fprintf(f, "\\%%{}"); - return; - } - if (!gregorio_wcsbufcmp(special_char, "R/")) { - fprintf(f, "\\Rbar{}"); - return; - } - if (!gregorio_wcsbufcmp(special_char, "V/")) { - fprintf(f, "\\Vbar{}"); - return; - } - if (!gregorio_wcsbufcmp(special_char, ACCENTED_AE)) { - fprintf(f, "\\'\\ae{}"); - return; - } - if (!gregorio_wcsbufcmp(special_char, "'ae")) { - fprintf(f, "\\'\\ae{}"); - return; - } - if (!gregorio_wcsbufcmp(special_char, ACCENTED_OE)) { - fprintf(f, "\\'\\oe{}"); - return; - } - if (!gregorio_wcsbufcmp(special_char, "'oe")) { - fprintf(f, "\\'\\oe{}"); - return; - } - if (!gregorio_wcsbufcmp(special_char, "ae")) { - fprintf(f, "\\ae{}"); - return; - } - if (!gregorio_wcsbufcmp(special_char, "oe")) { - fprintf(f, "\\oe{}"); - return; - } - if (!gregorio_wcsbufcmp(special_char, "*")) { - fprintf(f, "\\GreStar{}"); - return; - } - if (!gregorio_wcsbufcmp(special_char, "+")) { - fprintf(f, "\\GreDagger{}"); - return; - } - if (!gregorio_wcsbufcmp(special_char, "-")) { - fprintf(f, "\\GreZeroHyph{}"); - return; - } - if (!gregorio_wcsbufcmp(special_char, "\\")) { - fprintf(f, "\\textbackslash{}"); - return; - } - if (!gregorio_wcsbufcmp(special_char, "&")) { - fprintf(f, "\\&{}"); - return; - } - if (!gregorio_wcsbufcmp(special_char, "#")) { - fprintf(f, "\\#{}"); - return; - } - if (!gregorio_wcsbufcmp(special_char, "_")) { - fprintf(f, "\\_{}"); - return; - } - if (!gregorio_wcsbufcmp(special_char, "~")) { - fprintf(f, "\\GreTilde{}"); - return; - } + fprintf(f, "\\GreSpecial{"); + tex_escape_wtext(f, special_char); + fprintf(f, "}"); } -static void gtex_write_verb(FILE *f, grewchar *first_char) +static void gtex_write_verb(FILE *f, const grewchar *first_char) { gregorio_print_unistring(f, first_char); } -static void gtex_print_char(FILE *f, grewchar to_print) +static void gtex_print_char(FILE *f, const grewchar to_print) { switch (to_print) { case L'*': fprintf(f, "\\GreStar{}"); break; case L'%': + /* there's currently no way to get a % into gabc, so this wont be hit, + * but we leave it here for safety and possible future use */ + /* LCOV_EXCL_START */ fprintf(f, "\\%%{}"); break; + /* LCOV_EXCL_STOP */ case L'\\': fprintf(f, "\\textbackslash{}"); break; @@ -1245,31 +1289,29 @@ static void gtex_print_char(FILE *f, grewchar to_print) /* a function to map the internal ST_* styles to gregoriotex styles as defined * in gregoriotex-syllables.tex */ -static unsigned char gregoriotex_internal_style_to_gregoriotex(grestyle_style - style) +static unsigned char gregoriotex_internal_style_to_gregoriotex( + const grestyle_style style) { switch (style) { case ST_ITALIC: return 1; - break; case ST_BOLD: return 2; - break; case ST_SMALL_CAPS: return 3; - break; case ST_TT: return 4; - break; case ST_UNDERLINED: return 5; - break; case ST_COLORED: return 6; - break; default: + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_fail2(gregoriotex_internal_style_to_gregoriotex, + "unrecognized style: %s", grestyle_style_to_string(style)); return 0; - break; + /* LCOV_EXCL_STOP */ } } @@ -1278,78 +1320,105 @@ static unsigned char gregoriotex_internal_style_to_gregoriotex(grestyle_style * when this style is on all the parts, then we return this style. * */ -static grestyle_style gregoriotex_fix_style(gregorio_character *first_character) +static grestyle_style gregoriotex_fix_style( + const gregorio_character *current_char) { grestyle_style possible_fixed_style = ST_NO_STYLE; - unsigned char state = 0; - /* - * states are: - 0: we didn't meet any style yet, which means that if we - * encounter: * a character -> we can return, nothing to do * a style -> we - * go in state 1 * center or initial: stay in state 0 - 1: we encountered a - * style, if we encounter * another style : we can return * something that - * makes us change syllable part (like center or initial) -> go in state 2 * - * a character : stay in state 1 - 2: if we encounter: * another style, then - * return * a character, then return * the same style: go in state 1 - */ - gregorio_character *current_char = first_character; + grestyle_style in_fixed_style = ST_NO_STYLE; while (current_char) { - switch (state) { - case 0: - if (current_char->is_character) - return 0; - if (current_char->cos.s.style != ST_CENTER - && current_char->cos.s.style != ST_FORCED_CENTER - && current_char->cos.s.style != ST_FIRST_WORD - && current_char->cos.s.style != ST_FIRST_SYLLABLE - && current_char->cos.s.style != ST_FIRST_SYLLABLE_INITIAL - && current_char->cos.s.style != ST_SPECIAL_CHAR - && current_char->cos.s.style != ST_VERBATIM - && current_char->cos.s.style != ST_INITIAL) { - possible_fixed_style = current_char->cos.s.style; - state = 1; + if (!in_fixed_style) { + /* before the first style */ + if (current_char->is_character) { + /* got some character, so no future style can apply to the + * entire syllable */ + return ST_NO_STYLE; + } + if (current_char->cos.s.type == ST_T_BEGIN) { + switch (current_char->cos.s.style) { + case ST_VERBATIM: + case ST_SPECIAL_CHAR: + /* these are pseudo-characters, and if they appear + * before the first style, then the style does not + * apply to the entire syllable */ + return ST_NO_STYLE; + + case ST_ITALIC: + case ST_BOLD: + case ST_TT: + case ST_SMALL_CAPS: + case ST_UNDERLINED: + case ST_COLORED: + if (possible_fixed_style) { + if (current_char->cos.s.style != possible_fixed_style) { + /* found a differing style */ + return ST_NO_STYLE; + } else { + /* same style which may apply to the entire + * syllable */ + in_fixed_style = possible_fixed_style; + } + } else { + /* we found a candidate fixed style */ + in_fixed_style = possible_fixed_style = + current_char->cos.s.style; + } + break; + + default: + /* anything else is "transparent" before the first real + * or pseudo-character */ + break; + } } - break; - case 1: + /* else it's an end of a style, which we treat as "transparent" + * noise */ + } else { + /* we have a possible style */ if (!current_char->is_character) { - if (!current_char->is_character - && current_char->cos.s.style != ST_CENTER - && current_char->cos.s.style != ST_FORCED_CENTER - && current_char->cos.s.style != ST_FIRST_WORD - && current_char->cos.s.style != ST_FIRST_SYLLABLE - && current_char->cos.s.style != ST_FIRST_SYLLABLE_INITIAL - && current_char->cos.s.style != ST_INITIAL) { - state = 2; - } else if (current_char->cos.s.style != possible_fixed_style - && current_char->cos.s.style != ST_SPECIAL_CHAR - && current_char->cos.s.style != ST_VERBATIM) - return 0; - } - break; - case 2: - if (current_char->is_character) - return 0; - if (current_char->cos.s.style != ST_CENTER - && current_char->cos.s.style != ST_FORCED_CENTER - && current_char->cos.s.style != ST_FIRST_WORD - && current_char->cos.s.style != ST_FIRST_SYLLABLE - && current_char->cos.s.style != ST_FIRST_SYLLABLE_INITIAL - && current_char->cos.s.style != ST_SPECIAL_CHAR - && current_char->cos.s.style != ST_VERBATIM - && current_char->cos.s.style != ST_INITIAL) { - if (current_char->cos.s.style != possible_fixed_style) { - return 0; - } else { - state = 1; + if (current_char->cos.s.type == ST_T_BEGIN) { + switch (current_char->cos.s.style) { + case ST_ITALIC: + case ST_BOLD: + case ST_TT: + case ST_SMALL_CAPS: + case ST_UNDERLINED: + case ST_COLORED: + if (current_char->cos.s.style != possible_fixed_style) { + /* found a differing style */ + return ST_NO_STYLE; + } + /* else it's a (nested) open of the same style, which + * doesn't change the style; however, nested styles + * (should) have been eliminated by now */ + /* LCOV_EXCL_START */ + gregorio_fail(gregoriotex_fix_style, "encountered a " + "nested style which should have been removed " + "by now"); + break; + /* LCOV_EXCL_STOP */ + + default: + /* anything else is a pseudo-character or a + * "transparent" style, which don't affect the fixed + * style */ + break; + } + } else if (current_char->cos.s.type == ST_T_END) { + if (current_char->cos.s.style == possible_fixed_style) { + /* we closed the possible fixed style; we don't return + * yet to give it the chance to re-open */ + in_fixed_style = ST_NO_STYLE; + } + /* else it's a close of something we don't care about or a + * style that wasn't opened, so for the purposes of this + * function, these are "transparent" and don't affect the + * fixed style */ } } - break; - default: - break; + /* else it's a character, and that doesn't affect the fixed style */ } current_char = current_char->next_character; } - /* if we reached here, this means that we there is only one style applied - * to all the characters */ return possible_fixed_style; } @@ -1360,12 +1429,10 @@ static grestyle_style gregoriotex_fix_style(gregorio_character *first_character) * entire string of text under the normal text line, without considering any * special centering or linebreaks. */ -static void gregoriotex_write_translation(FILE *f, +static __inline void write_translation(FILE *f, gregorio_character *translation) { - if (translation == NULL) { - return; - } + gregorio_not_null(translation, write_translation, return); gregorio_write_text(WTP_NORMAL, translation, f, >ex_write_verb, >ex_print_char, >ex_write_begin, >ex_write_end, >ex_write_special_char); @@ -1401,9 +1468,11 @@ static char clef_flat_height(gregorio_clef clef, signed char line, bool flatted) offset = 10; break; default: - gregorio_messagef("clef_flat_height", VERBOSITY_ERROR, - 0, _("unknown line number: %d"), line); + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_fail2(clef_flat_height, "unknown line number: %d", line); break; + /* LCOV_EXCL_STOP */ } break; case CLEF_F: @@ -1424,15 +1493,19 @@ static char clef_flat_height(gregorio_clef clef, signed char line, bool flatted) offset = 7; break; default: - gregorio_messagef("clef_flat_height", VERBOSITY_ERROR, - 0, _("unknown line number: %d"), line); + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_fail2(clef_flat_height, "unknown line number: %d", line); break; + /* LCOV_EXCL_STOP */ } break; default: - gregorio_messagef("clef_flat_height", VERBOSITY_ERROR, 0, - _("unknown clef type: %d"), clef); + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_fail2(clef_flat_height, "unknown clef type: %d", clef); break; + /* LCOV_EXCL_STOP */ } return pitch_value(LOWEST_PITCH + offset); @@ -1495,9 +1568,11 @@ static void write_bar(FILE *f, gregorio_bar type, fprintf(f, "Dominica{8}"); break; default: - gregorio_messagef("write_bar", VERBOSITY_ERROR, 0, - _("unknown bar type: %d"), type); + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_fail2(write_bar, "unknown bar type: %d", type); break; + /* LCOV_EXCL_STOP */ } fprintf(f, "{%c}", has_text? '1' : '0'); switch (signs) { @@ -1534,7 +1609,7 @@ static __inline char *suppose_low_ledger_line(const gregorio_note *const note) * we consider that the two puncta of the augumentum duplex must correspond to * the last note and the previous note. If we are adding to a single note * glyph, which would be weird but sure why not, we just typeset two puncta - * spaced of 2. + * spaced of 2. */ static void gregoriotex_write_auctum_duplex(FILE *f, gregorio_note *current_note) @@ -1669,7 +1744,6 @@ static void gregoriotex_write_punctum_mora(FILE *f, gregorio_glyph *glyph, } /* we enter here in any case */ switch (glyph->u.notes.glyph_type) { - case G_TRIGONUS: case G_PUNCTA_INCLINATA: case G_2_PUNCTA_INCLINATA_DESCENDENS: case G_3_PUNCTA_INCLINATA_DESCENDENS: @@ -1683,11 +1757,13 @@ static void gregoriotex_write_punctum_mora(FILE *f, gregorio_glyph *glyph, default: break; } - if (current_note->u.note.shape == S_PUNCTUM_INCLINATUM) { - punctum_inclinatum = 1; - } - if (current_note->u.note.shape == S_PUNCTUM_INCLINATUM_DEMINUTUS) { + switch (current_note->u.note.shape) { + case S_PUNCTUM_INCLINATUM: + case S_PUNCTUM_INCLINATUM_DEMINUTUS: punctum_inclinatum = 1; + break; + default: + break; } /* when the punctum mora is on a note on a line, and the prior note is on * the space immediately above, the dot is placed on the space below the @@ -1832,7 +1908,7 @@ static __inline int get_punctum_inclinatum_space_case( } } } - } + } /* LCOV_EXCL_LINE */ break; case S_PUNCTUM_INCLINATUM_AUCTUS: case S_PUNCTUM_CAVUM_INCLINATUM_AUCTUS: @@ -1976,9 +2052,8 @@ static void gregoriotex_write_hepisema(FILE *const f, { int porrectus_long_episema_index = -1; - if (!note) { - return; - } + gregorio_assert(note, gregoriotex_write_hepisema, + "called with NULL pointer", return); switch (type) { case T_PORRECTUS: @@ -2008,20 +2083,18 @@ static void write_additional_line(FILE *f, int i, gtex_type type, bool bottom, gregorio_note *current_note, const gregorio_score *const score) { char ambitus = 0; - if (!current_note) { - gregorio_message(_("called with no note"), "write_additional_line", - VERBOSITY_ERROR, 0); - return; - } + gregorio_assert(current_note, write_additional_line, "called with no note", + return); /* patch to get a line under the full glyph in the case of dbc (for * example) */ switch (type) { case T_PORRECTUS: case T_PORRECTUS_FLEXUS: - if (i == 1) { + switch (i) { + case 1: i = HEPISEMA_FIRST_TWO; - } - if (i == 2) { + break; + case 2: if (current_note->previous->u.note.pitch > LOW_LEDGER_LINE_PITCH && current_note->previous->u.note.pitch < score->high_ledger_line_pitch) { @@ -2031,21 +2104,23 @@ static void write_additional_line(FILE *f, int i, gtex_type type, bool bottom, } else { return; } - } - if (i == 3) { + break; + case 3: if (bottom || current_note->previous->u.note.pitch >= score->high_ledger_line_pitch) { /* we don't need to add twice the same line */ return; } + break; } break; case T_TORCULUS_RESUPINUS: case T_TORCULUS_RESUPINUS_FLEXUS: - if (i == 2) { + switch (i) { + case 2: i = HEPISEMA_FIRST_TWO; - } - if (i == 3) { + break; + case 3: if (current_note->previous->u.note.pitch > LOW_LEDGER_LINE_PITCH && current_note->previous->u.note.pitch < score->high_ledger_line_pitch) { @@ -2055,13 +2130,14 @@ static void write_additional_line(FILE *f, int i, gtex_type type, bool bottom, } else { return; } - } - if (i == 4) { + break; + case 4: if (bottom || current_note->previous->u.note.pitch >= score->high_ledger_line_pitch) { /* we don't need to add twice the same line */ return; } + break; } break; default: @@ -2071,25 +2147,21 @@ static void write_additional_line(FILE *f, int i, gtex_type type, bool bottom, if (i == HEPISEMA_FIRST_TWO) { /* here we must compare the first note of the big bar with the second * one, but it may be tricky sometimes, because of the previous patch */ - if (current_note->previous && - current_note->previous->u.note.pitch > - current_note->u.note.pitch) { - ambitus = current_note->previous->u.note.pitch - - current_note->u.note.pitch; - } else { - ambitus = current_note->u.note.pitch - - current_note->next->u.note.pitch; - } + gregorio_assert_only(!current_note->previous || + current_note->u.note.pitch + > current_note->previous->u.note.pitch, + write_additional_line, "HEPISEMA_FIRST_TWO on the wrong note"); + ambitus = current_note->u.note.pitch - current_note->next->u.note.pitch; } fprintf(f, "\\GreAdditionalLine{\\GreOCase%s}{%d}{%d}%%\n", current_note->gtex_offset_case, ambitus, bottom ? 3 : 2); } /* - * - * a function that writes the good value of \vepisema in GregorioTeX. i is the + * + * a function that writes the good value of \vepisema in GregorioTeX. i is the * position of the note in the glyph - * + * */ static void gregoriotex_write_vepisema(FILE *f, gregorio_note *note) @@ -2134,7 +2206,11 @@ static void gregoriotex_write_rare(FILE *f, gregorio_note *current_note, /* the cases of the bar signs are dealt in another function * (write_bar) */ default: + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + assert(false); break; + /* LCOV_EXCL_STOP */ } } @@ -2143,39 +2219,54 @@ static void gregoriotex_write_rare(FILE *f, gregorio_note *current_note, */ static void gregoriotex_write_note(FILE *f, gregorio_note *note, - gregorio_glyph *glyph, gregorio_element *element, char next_note_pitch) + gregorio_glyph *glyph, char next_note_pitch) { unsigned int initial_shape = note->u.note.shape; const char *shape; int space_case; /* type in the sense of GregorioTeX alignment type */ gtex_alignment type = AT_ONE_NOTE; - if (!note) { - gregorio_message(_ - ("called with NULL pointer"), - "gregoriotex_write_note", VERBOSITY_ERROR, 0); - return; - } + gregorio_assert(note, gregoriotex_write_note, "called with NULL pointer", + return); if (note->u.note.shape == S_PUNCTUM) { switch (note->u.note.liquescentia) { case L_AUCTUS_ASCENDENS: + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_fail(gregoriotex_write_note, + "encounted S_PUNCTUM with L_AUCTUS_ASCENDENS"); + /* should have been changed by this point */ note->u.note.shape = S_PUNCTUM_AUCTUS_ASCENDENS; break; + /* LCOV_EXCL_STOP */ case L_AUCTUS_DESCENDENS: + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_fail(gregoriotex_write_note, + "encounted S_PUNCTUM with L_AUCTUS_DESCENDENS"); + /* should have been changed by this point */ note->u.note.shape = S_PUNCTUM_AUCTUS_DESCENDENS; break; + /* LCOV_EXCL_STOP */ case L_INITIO_DEBILIS: if (glyph->u.notes.fuse_to_next_glyph > 0) { break; } /* else fall through to next case */ case L_DEMINUTUS: + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_fail(gregoriotex_write_note, + "encounted S_PUNCTUM with L_DEMINUTUS"); + /* should have been changed by this point */ note->u.note.shape = S_PUNCTUM_DEMINUTUS; + break; + /* LCOV_EXCL_STOP */ default: break; } } - shape = gregoriotex_determine_note_glyph_name(note, glyph, element, &type); + shape = gregoriotex_determine_note_glyph_name(note, glyph, &type); note->u.note.shape = initial_shape; /* special things for puncta inclinata */ space_case = get_punctum_inclinatum_space_case(note); @@ -2250,13 +2341,10 @@ static int gregoriotex_syllable_first_type(gregorio_syllable *syllable) int alteration = 0; gregorio_glyph *glyph; gregorio_element *element; - if (!syllable || !syllable->elements) { - gregorio_message(_("called with a NULL argument"), - "gregoriotex_syllable_first_type", VERBOSITY_ERROR, 0); - return 0; - } - element = syllable->elements[0]; - while (element) { + gregorio_assert(syllable && syllable->elements, + gregoriotex_syllable_first_type, "called with a NULL argument", + return 0); + for (element = syllable->elements[0]; element; element = element->next) { if (element->type == GRE_BAR) { switch (element->u.misc.unpitched.info.bar) { case B_NO_BAR: @@ -2280,8 +2368,12 @@ static int gregoriotex_syllable_first_type(gregorio_syllable *syllable) result = 12; break; default: + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_fail(gregoriotex_syllable_first_type, "invalid bar"); result = 0; break; + /* LCOV_EXCL_STOP */ } return result; } @@ -2302,12 +2394,15 @@ static int gregoriotex_syllable_first_type(gregorio_syllable *syllable) alteration = 60; break; default: - /* do nothing */ + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_fail(gregoriotex_syllable_first_type, + "invalid alteration"); break; + /* LCOV_EXCL_STOP */ } } continue; - case G_TRIGONUS: case G_PUNCTA_INCLINATA: case G_2_PUNCTA_INCLINATA_DESCENDENS: case G_3_PUNCTA_INCLINATA_DESCENDENS: @@ -2329,20 +2424,16 @@ static int gregoriotex_syllable_first_type(gregorio_syllable *syllable) case G_BIVIRGA: case G_TRIVIRGA: gregoriotex_determine_note_glyph_name - (glyph->u.notes.first_note, glyph, element, - &type); + (glyph->u.notes.first_note, glyph, &type); break; default: - gregoriotex_determine_glyph_name(glyph, element, &type, - >ype); + gregoriotex_determine_glyph_name(glyph, &type, >ype); break; } return type + alteration; } - glyph = glyph->next; } } - element = element->next; } return 0; } @@ -2410,14 +2501,14 @@ static void gregoriotex_write_choral_sign(FILE *f, gregorio_glyph *glyph, } /* - * + * * A function that write the signs of a glyph, which has the type type (T_*, * not G_*, which is in the glyph->glyph_type), and (important), we start only * at the note current_note. It is due to the way we call it : if type is * T_ONE_NOTE, we just do the signs on current_note, not all. This is the case * for example for the first note of the torculus resupinus, or the * G_*_PUNCTA_INCLINATA. - * + * */ /* small helper */ @@ -2489,8 +2580,6 @@ static void gregoriotex_write_signs(FILE *f, gtex_type type, /* i is the number of the note for which we are typesetting the sign. */ int i; gregorio_note *current_note; - /* a dumb char */ - char block_hepisema = 0; signed char high_pitch = UNDETERMINED_HEIGHT; signed char low_pitch = UNDETERMINED_HEIGHT; bool found = false; @@ -2555,15 +2644,6 @@ static void gregoriotex_write_signs(FILE *f, gtex_type type, /* do nothing */ break; } - /* why is this if there?... */ - if (!current_note->special_sign) { - if (block_hepisema == 2) { - block_hepisema = 0; - } - if (block_hepisema == 1) { - block_hepisema = 2; - } - } if (type == T_ONE_NOTE) { break; } @@ -2642,7 +2722,7 @@ static void write_glyph(FILE *f, gregorio_syllable *syllable, gtex_alignment type = 0; /* the type of the glyph, in the sense of the shape (T_PES, etc.) */ gtex_type gtype = 0; - char next_note_pitch = 0; + signed char next_note_pitch = 0; gregorio_note *current_note; const char *leading_shape, *shape; const gregorio_glyph *prev_glyph = @@ -2650,16 +2730,9 @@ static void write_glyph(FILE *f, gregorio_syllable *syllable, int fuse_to_next_note, fuse_from_previous_note = (prev_glyph && prev_glyph->type == GRE_GLYPH) ? prev_glyph->u.notes.fuse_to_next_glyph : 0; - if (!glyph) { - gregorio_message(_("called with NULL pointer"), "write_glyph", - VERBOSITY_ERROR, 0); - return; - } - if (glyph->type != GRE_GLYPH || !glyph->u.notes.first_note) { - gregorio_message(_("called with glyph without note"), "write_glyph", - VERBOSITY_ERROR, 0); - return; - } + gregorio_assert(glyph, write_glyph, "called with NULL pointer", return); + gregorio_assert(glyph->type == GRE_GLYPH && glyph->u.notes.first_note, + write_glyph, "called with glyph without note", return); fuse_to_next_note = glyph->u.notes.fuse_to_next_glyph; if (fuse_from_previous_note) { fprintf(f, "\\GreFuse"); @@ -2671,7 +2744,6 @@ static void write_glyph(FILE *f, gregorio_syllable *syllable, * in general, and torculus resupinus and torculus resupinus flexus, so * we first divide the glyph into real gregoriotex glyphs */ switch (glyph->u.notes.glyph_type) { - case G_TRIGONUS: case G_PUNCTA_INCLINATA: case G_2_PUNCTA_INCLINATA_DESCENDENS: case G_3_PUNCTA_INCLINATA_DESCENDENS: @@ -2682,8 +2754,7 @@ static void write_glyph(FILE *f, gregorio_syllable *syllable, case G_4_PUNCTA_INCLINATA_ASCENDENS: case G_5_PUNCTA_INCLINATA_ASCENDENS: while (current_note) { - gregoriotex_write_note(f, current_note, glyph, element, - next_note_pitch); + gregoriotex_write_note(f, current_note, glyph, next_note_pitch); gregoriotex_write_signs(f, T_ONE_NOTE, glyph, current_note, current_note->next ? 0 : fuse_to_next_note, status, score); current_note = current_note->next; @@ -2691,46 +2762,26 @@ static void write_glyph(FILE *f, gregorio_syllable *syllable, /* TODO: handle fusion to next note */ break; case G_SCANDICUS: - if ((glyph->u.notes.liquescentia & L_DEMINUTUS) + gregorio_assert((glyph->u.notes.liquescentia & L_DEMINUTUS) || glyph->u.notes.liquescentia == L_NO_LIQUESCENTIA - || glyph->u.notes.liquescentia == L_FUSED) { - shape = gregoriotex_determine_glyph_name(glyph, element, &type, - >ype); - fprintf(f, "\\GreGlyph{\\GreCP%s}{%d}{%d}{%d}", shape, - pitch_value(glyph->u.notes.first_note->u.note.pitch), - pitch_value(next_note_pitch), type); - gregoriotex_write_signs(f, gtype, glyph, glyph->u.notes.first_note, - fuse_to_next_note, status, score); - } else { - while (current_note) { - gregoriotex_write_note(f, current_note, glyph, element, - next_note_pitch); - gregoriotex_write_signs(f, T_ONE_NOTE, glyph, current_note, - current_note->next ? 0 : fuse_to_next_note, status, - score); - current_note = current_note->next; - } - } + || glyph->u.notes.liquescentia == L_FUSED, write_glyph, + "encountered an invalid scandicus", break); + shape = gregoriotex_determine_glyph_name(glyph, &type, >ype); + fprintf(f, "\\GreGlyph{\\GreCP%s}{%d}{%d}{%d}", shape, + pitch_value(glyph->u.notes.first_note->u.note.pitch), + pitch_value(next_note_pitch), type); + gregoriotex_write_signs(f, gtype, glyph, glyph->u.notes.first_note, + fuse_to_next_note, status, score); break; case G_ANCUS: - if (glyph->u.notes.liquescentia & L_DEMINUTUS) { - shape = gregoriotex_determine_glyph_name(glyph, element, &type, - >ype); - fprintf(f, "\\GreGlyph{\\GreCP%s}{%d}{%d}{%d}", shape, - pitch_value(glyph->u.notes.first_note->u.note.pitch), - pitch_value(next_note_pitch), type); - gregoriotex_write_signs(f, gtype, glyph, glyph->u.notes.first_note, - fuse_to_next_note, status, score); - } else { - while (current_note) { - gregoriotex_write_note(f, current_note, glyph, element, - next_note_pitch); - gregoriotex_write_signs(f, T_ONE_NOTE, glyph, current_note, - current_note->next ? 0 : fuse_to_next_note, status, - score); - current_note = current_note->next; - } - } + gregorio_assert(glyph->u.notes.liquescentia & L_DEMINUTUS, + write_glyph, "encountered a non-deminutus ancus", break); + shape = gregoriotex_determine_glyph_name(glyph, &type, >ype); + fprintf(f, "\\GreGlyph{\\GreCP%s}{%d}{%d}{%d}", shape, + pitch_value(glyph->u.notes.first_note->u.note.pitch), + pitch_value(next_note_pitch), type); + gregoriotex_write_signs(f, gtype, glyph, glyph->u.notes.first_note, + fuse_to_next_note, status, score); break; case G_TORCULUS_RESUPINUS_FLEXUS: /* we retain this "old-style" fusion as it does look marginally better @@ -2739,7 +2790,7 @@ static void write_glyph(FILE *f, gregorio_syllable *syllable, /* trick to have the good position for these glyphs */ glyph->u.notes.glyph_type = G_PORRECTUS_FLEXUS_NO_BAR; glyph->u.notes.first_note = current_note->next; - shape = gregoriotex_determine_glyph_name(glyph, element, &type, >ype); + shape = gregoriotex_determine_glyph_name(glyph, &type, >ype); fprintf(f, "\\GreGlyph{\\GreFuseTwo{\\GreCP%s}{\\GreCP%s}}{%d}{%d}{%d}", leading_shape, shape, pitch_value(glyph->u.notes.first_note->u.note.pitch), @@ -2752,8 +2803,7 @@ static void write_glyph(FILE *f, gregorio_syllable *syllable, case G_BIVIRGA: case G_TRIVIRGA: while (current_note) { - gregoriotex_write_note(f, current_note, glyph, element, - next_note_pitch); + gregoriotex_write_note(f, current_note, glyph, next_note_pitch); gregoriotex_write_signs(f, T_ONE_NOTE, glyph, current_note, current_note->next ? 0 : fuse_to_next_note, status, score); current_note = current_note->next; @@ -2767,8 +2817,7 @@ static void write_glyph(FILE *f, gregorio_syllable *syllable, case G_DISTROPHA: case G_DISTROPHA_AUCTA: while (current_note) { - gregoriotex_write_note(f, current_note, glyph, element, - next_note_pitch); + gregoriotex_write_note(f, current_note, glyph, next_note_pitch); gregoriotex_write_signs(f, T_ONE_NOTE, glyph, current_note, current_note->next ? 0 : fuse_to_next_note, status, score); current_note = current_note->next; @@ -2820,7 +2869,7 @@ static void write_glyph(FILE *f, gregorio_syllable *syllable, case G_STROPHA: case G_STROPHA_AUCTA: case G_ALTERATION: - gregoriotex_write_note(f, glyph->u.notes.first_note, glyph, element, + gregoriotex_write_note(f, glyph->u.notes.first_note, glyph, next_note_pitch); gregoriotex_write_signs(f, T_ONE_NOTE, glyph, current_note, fuse_to_next_note, status, score); @@ -2837,8 +2886,7 @@ static void write_glyph(FILE *f, gregorio_syllable *syllable, /* trick to have the good position for these glyphs */ glyph->u.notes.glyph_type = G_PORRECTUS_NO_BAR; glyph->u.notes.first_note = current_note->next; - shape = gregoriotex_determine_glyph_name(glyph, element, &type, - >ype); + shape = gregoriotex_determine_glyph_name(glyph, &type, >ype); fprintf(f, "\\GreGlyph{\\GreFuseTwo{\\GreCP%s}{\\GreCP%s}}" "{%d}{%d}{%d}", leading_shape, shape, pitch_value(glyph->u.notes.first_note->u.note.pitch), @@ -2849,8 +2897,7 @@ static void write_glyph(FILE *f, gregorio_syllable *syllable, fuse_to_next_note, status, score); break; } else { - shape = gregoriotex_determine_glyph_name(glyph, element, &type, - >ype); + shape = gregoriotex_determine_glyph_name(glyph, &type, >ype); fprintf(f, "\\GreGlyph{\\GreCP%s}{%d}{%d}{%d}", shape, pitch_value(glyph->u.notes.first_note->u.note.pitch), pitch_value(next_note_pitch), type); @@ -2880,11 +2927,16 @@ static void write_element(FILE *f, gregorio_syllable *syllable, case SP_HALF_SPACE: fprintf(f, "\\GreEndOfGlyph{22}%%\n"); break; + case SP_INTERGLYPH_SPACE: + fprintf(f, "\\GreEndOfGlyph{0}%%\n"); + break; default: - gregorio_message( - _("encountered an unexpected glyph-level space"), - "write_element", VERBOSITY_ERROR, 0); + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_fail(write_element, + "encountered an unexpected glyph-level space"); break; + /* LCOV_EXCL_STOP */ } break; @@ -2996,9 +3048,7 @@ static void gregoriotex_print_change_line_clef(FILE *f, static __inline bool is_manual_custos(const gregorio_element *element) { - return element->type == GRE_ELEMENT - && element->u.first_glyph - && element->u.first_glyph->type == GRE_MANUAL_CUSTOS; + return element->type == GRE_CUSTOS && element->u.misc.pitched.force_pitch; } static __inline bool next_is_bar(const gregorio_syllable *syllable, @@ -3030,19 +3080,15 @@ static __inline bool next_is_bar(const gregorio_syllable *syllable, if (!syllable) { return false; } - if (syllable->type == GRE_BAR) { - return true; - } - if (syllable->type != GRE_SYLLABLE) { - return false; - } - /* the next syllable is a GRE_SYLLABLE; so look at the element */ element = syllable->elements[0]; } - assert(false); /* should never reach here */ - return false; /* avoid gcc 5.1 warning */ + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_fail(next_is_bar, "unexpected end of syllables/elements"); + return false; + /* LCOV_EXCL_STOP */ } static void finish_syllable(FILE *f, gregorio_syllable *syllable) { @@ -3097,7 +3143,7 @@ static __inline void write_syllable_point_and_click(FILE *const f, } } -static void write_syllable_text(FILE *f, const char *const syllable_type, +static void write_syllable_text(FILE *f, const char *const syllable_type, const gregorio_character *text, bool ignored __attribute__((unused))) { if (syllable_type != NULL) { @@ -3107,11 +3153,12 @@ static void write_syllable_text(FILE *f, const char *const syllable_type, } } -static void write_first_syllable_text(FILE *f, const char *const syllable_type, +static void write_first_syllable_text(FILE *f, const char *const syllable_type, const gregorio_character *const text, bool end_of_word) { - if (syllable_type == NULL || text == NULL) { - fprintf(f, "}{}{\\GreSetNoFirstSyllableText}"); + gregorio_not_null(syllable_type, write_first_syllable_text, return); + if (text == NULL) { + fprintf(f, "}{%s}{\\GreSetNoFirstSyllableText}", syllable_type); } else { gregorio_character *text_with_initial = gregorio_clone_characters(text), *text_without_initial = gregorio_clone_characters(text); @@ -3234,9 +3281,9 @@ static __inline void anticipate_event(FILE *f, gregorio_syllable *syllable) { /* * Arguments are relatively obvious. The most obscure is certainly first_of_disc * which is 0 all the time, except in the case of a "clef change syllable". In - * this case we make a \GreDiscretionary with two arguments: + * this case we make a \GreDiscretionary with two arguments: * 1.what should be printed if the syllable is the last of its line (which - * basically means everything but clefs and custos), and + * basically means everything but clefs and custos), and * 2. what should be printed if it's in a middle of a line (which means * everything) * So the first_of_disc argument is: @@ -3253,14 +3300,11 @@ static void write_syllable(FILE *f, gregorio_syllable *syllable, gregorio_element *clef_change_element = NULL, *element; const char *syllable_type = NULL; bool event_anticipated = false; - bool end_of_word = syllable->position == WORD_END + bool end_of_word; + gregorio_not_null(syllable, write_syllable, return); + end_of_word = syllable->position == WORD_END || syllable->position == WORD_ONE_SYLLABLE || !syllable->text - || !syllable->next_syllable - || syllable->next_syllable->type == GRE_END_OF_LINE; - if (!syllable) { - write_this_syllable_text(f, NULL, NULL, end_of_word); - return; - } + || !syllable->next_syllable; /* Very first: before anything, if the syllable is the beginning of a * no-linebreak area: */ if (syllable->no_linebreak_area == NLBA_BEGINNING) { @@ -3273,10 +3317,13 @@ static void write_syllable(FILE *f, gregorio_syllable *syllable, * first we check if the syllable is only a end of line. If it is the case, * we don't print anything but a comment (to be able to read it if we read * GregorioTeX). The end of lines are treated separately in GregorioTeX, it - * is buit inside the TeX structure. + * is buit inside the TeX structure. */ if (syllable->elements && *(syllable->elements)) { if ((syllable->elements)[0]->type == GRE_END_OF_LINE) { + gregorio_assert(syllable != score->first_syllable, write_syllable, + "line break is not supported on the first syllable", + return); if ((syllable->elements)[0]->u.misc.unpitched.info.eol_ragged) { fprintf(f, "%%\n%%\n\\GreNewParLine %%\n%%\n%%\n"); } else { @@ -3289,13 +3336,17 @@ static void write_syllable(FILE *f, gregorio_syllable *syllable, * This case is not simple: if the syllable contains a clef change, * whether it is (c4) or (::c4) or (z0::c4), we put it in a * discretionary. Warning: only these three cases will have the expected - * effect. So first we detect it: + * effect. So first we detect it: */ if (first_of_disc == 0) { /* to avoid infinite loops */ - clef_change_element = gregoriotex_syllable_is_clef_change(syllable); + clef_change_element = gregorio_get_clef_change(syllable); if (clef_change_element) { + gregorio_assert(syllable != score->first_syllable, + write_syllable, + "clef change is not supported on the first syllable", + return); /* - * In this case, the first thing to do is to change the line clef + * In this case, the first thing to do is to change the line clef */ gregoriotex_print_change_line_clef(f, clef_change_element); fprintf(f, "\\GreDiscretionary{0}{%%\n"); @@ -3312,22 +3363,27 @@ static void write_syllable(FILE *f, gregorio_syllable *syllable, write_fixed_text_styles(f, syllable->text, syllable->next_syllable? syllable->next_syllable->text : NULL); if ((syllable->elements)[0]->type == GRE_BAR) { - if (!syllable->next_syllable && !syllable->text - && (syllable->elements)[0]->u.misc.unpitched.info.bar == - B_DIVISIO_FINALIS) { - handle_final_bar(f, "DivisioFinalis", syllable); - write_this_syllable_text(f, NULL, syllable->text, end_of_word); - return; - } - if (!syllable->next_syllable && !syllable->text - && (syllable->elements)[0]->u.misc.unpitched.info.bar == - B_DIVISIO_MAIOR) { - handle_final_bar(f, "DivisioMaior", syllable); - write_this_syllable_text(f, NULL, syllable->text, end_of_word); - return; - } else { - syllable_type = "\\GreBarSyllable"; + if (syllable != score->first_syllable) { + if (!syllable->next_syllable && !syllable->text + && (syllable->elements)[0]->u.misc.unpitched.info.bar + == B_DIVISIO_FINALIS) { + handle_final_bar(f, "DivisioFinalis", syllable); + write_this_syllable_text(f, NULL, syllable->text, + end_of_word); + return; + } + if (!syllable->next_syllable && !syllable->text + && (syllable->elements)[0]->u.misc.unpitched.info.bar + == B_DIVISIO_MAIOR) { + handle_final_bar(f, "DivisioMaior", syllable); + write_this_syllable_text(f, NULL, syllable->text, + end_of_word); + return; + } } + /* the special case of first syllable will be treated as a normal + * bar syllable */ + syllable_type = "\\GreBarSyllable"; } else { syllable_type = "\\GreSyllable"; } @@ -3361,7 +3417,7 @@ static void write_syllable(FILE *f, gregorio_syllable *syllable, } else { fprintf(f, "%%\n\\GreWriteTranslation{"); } - gregoriotex_write_translation(f, syllable->translation); + write_translation(f, syllable->translation); fprintf(f, "}%%\n"); } if (syllable->translation_type) { @@ -3395,9 +3451,6 @@ static void write_syllable(FILE *f, gregorio_syllable *syllable, switch (element->type) { case GRE_SPACE: switch (element->u.misc.unpitched.info.space) { - case SP_ZERO_WIDTH: - fprintf(f, "\\GreEndOfElement{3}{1}%%\n"); - break; case SP_LARGER_SPACE: fprintf(f, "\\GreEndOfElement{1}{0}%%\n"); break; @@ -3425,10 +3478,12 @@ static void write_syllable(FILE *f, gregorio_syllable *syllable, element->u.misc.unpitched.info.ad_hoc_space_factor); break; default: - gregorio_message( - _("encountered an unexpected element-level space"), - "write_syllable", VERBOSITY_ERROR, 0); + /* not reachable unless there's a programming error */ + /* LCOV_EXCL_START */ + gregorio_fail(write_syllable, + "encountered an unexpected element-level space"); break; + /* LCOV_EXCL_STOP */ } break; @@ -3479,7 +3534,7 @@ static void write_syllable(FILE *f, gregorio_syllable *syllable, case GRE_CUSTOS: if (first_of_disc != 1) { /* - * We don't print custos before a bar at the end of a line + * We don't print custos before a bar at the end of a line */ /* we also print an unbreakable larger space before the custo */ fprintf(f, "\\GreEndOfElement{1}{1}%%\n\\GreCustos{%d}" @@ -3617,50 +3672,14 @@ static void initialize_score(gregoriotex_status *const status, status->point_and_click = point_and_click; } -static __inline void write_escapable_header_text(FILE *const f, - const char *text) -{ - /* We escape these characters into \string\ddd (where ddd is the decimal - * ASCII value of the character) for most escapes, and into \string\n for - * newlines. We do it this way to get the "raw" string values through TeX - * and into Lua, where the sequences become \ddd and \n respectively and - * are translated into their byte values. Lua can then decide whether the - * full strings should be evaluated by TeX as TeX or as strings */ - for (; *text; ++text) { - switch (*text) { - case '\\': - case '{': - case '}': - case '~': - case '%': /* currently, we'll never get %, but handle it anyway */ - case '#': - case '"': - /* these characters have special meaning to TeX */ - fprintf(f, "\\string\\%03d", *text); - break; - case '\n': - /* currently, we'll never get \n, but handle it anyway */ - fprintf(f, "\\string\\n"); - break; - case '\r': - /* ignore */ - break; - default: - /* UTF-8 multibyte sequences will fall into here, which is fine */ - fputc(*text, f); - break; - } - } -} - static void write_header(FILE *const f, const char *const name, const char *const value) { if (value) { fprintf(f, "\\GreHeader{"); - write_escapable_header_text(f, name); + tex_escape_text(f, name); fprintf(f, "}{"); - write_escapable_header_text(f, value); + tex_escape_text(f, value); fprintf(f, "}%%\n"); } } @@ -3700,16 +3719,10 @@ void gregoriotex_write_score(FILE *const f, gregorio_score *const score, initialize_score(&status, score, point_and_click_filename != NULL); - if (!f) { - gregorio_message(_("call with NULL file"), "gregoriotex_write_score", - VERBOSITY_ERROR, 0); - return; - } + gregorio_assert(f, gregoriotex_write_score, "call with NULL file", return); - if (score->number_of_voices != 1) { - gregorio_message(_("gregoriotex only works in monophony (for the " - "moment)"), "gregoriotex_write_score", VERBOSITY_ERROR, 0); - } + gregorio_assert_only(score->number_of_voices == 1, gregoriotex_write_score, + "gregoriotex only works in monophony (for the moment)"); fprintf(f, "%% File generated by gregorio %s\n", GREGORIO_VERSION); fprintf(f, "\\GregorioTeXAPIVersion{%s}%%\n", VERSION); @@ -3768,9 +3781,12 @@ void gregoriotex_write_score(FILE *const f, gregorio_score *const score, fprintf(f, "}%%\n"); } + /* DEPRECATED by 4.1 */ + /* LCOV_EXCL_START */ if (score->initial_style != INITIAL_NOT_SPECIFIED) { /* DEPRECATED by 4.1 */ fprintf(f, "\\GreSetInitialStyle{%d}%%\n", score->initial_style); /* DEPRECATED by 4.1 */ } + /* LCOV_EXCL_STOP */ fprintf(f, "\\GreScoreOpening{%%\n"); /* GreScoreOpening#1 */ if (score->first_voice_info) { @@ -3780,7 +3796,7 @@ void gregoriotex_write_score(FILE *const f, gregorio_score *const score, if (score->first_voice_info) { clef = score->first_voice_info->initial_clef; } - fprintf(f, "\\GreSetInitialClef{%c}{%d}{%d}{%c}{%d}{%d}%%\n", + fprintf(f, "\\GreSetInitialClef{%c}{%d}{%d}{%c}{%d}{%d}%%\n", gregorio_clef_to_char(clef.clef), clef.line, clef_flat_height(clef.clef, clef.line, clef.flatted), gregorio_clef_to_char(clef.secondary_clef), clef.secondary_line, @@ -3792,6 +3808,9 @@ void gregoriotex_write_score(FILE *const f, gregorio_score *const score, write_syllable(f, current_syllable, 0, &status, score, write_first_syllable_text); current_syllable = current_syllable->next_syllable; + } else { + /* edge case: a score with no syllables */ + fprintf(f, "}{}{\\GreSetNoFirstSyllableText}%%\n"); } while (current_syllable) { write_syllable(f, current_syllable, 0, &status, score, |