chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / charinfo.c
CommitLineData
69695f33
MW
1
2/* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 by Arkkra Enterprises */
3/* All rights reserved */
4
5/* functions related to characters: information about the size and shape of
6 * characters to be printed, to initialize the internal tables
7 * to tell how big each character is, etc.
8 */
9
10#include <string.h>
11#include "defines.h"
12#include "structs.h"
13#include "globals.h"
14
15
16/* code for invalid music character */
17#define BAD_CHAR '\377'
18/* code for invalid number in user input string */
19#define BAD_NUMBER -30000
20
21/* machine-generated sorted list for translating
22 * music character names to internal code numbers. */
23/* The +1 is because there is an "end-of-list" entry with charname == 0 */
24extern struct SPECCHAR Mus_char_table[NUM_MFONTS][CHARS_IN_FONT+1];
25#ifdef EXTCHAR
26extern struct SPECCHAR Ext_char_table[];
27#endif
28
29
30/* save information about characters in string as we go, in order to be
31 * able to backspace back over them */
32struct BACKSPACEINFO {
33 char code;
34 char font;
35};
36
37
38#ifndef __STDC__
39extern char *bsearch(); /* binary search library function */
40#endif
41extern long strtol();
42
43/* static functions */
44static char *get_font P((char *string, int *font_p, int prev_font, char *fname,
45 int lineno));
46static char *get_num P((char *string, int *num_p));
47static int sc_compare P((const void *item1_p, const void *item2_p));
48#ifdef EXTCHAR
49static unsigned char ext_name2num P((char *name));
50#endif
51static int starts_piled P((char *string, int *font_p, int *size_p,
52 char **pile_start_p_p));
53static int str_cmd P((char *str, int *size_p, int *in_pile_p));
54static int get_accidental P((unsigned char *str, char *accidental_p,
55 int *acc_size_p, int trans_natural, int *escaped_p));
56static int add_accidental P((char *buff, int acc_character, int acc_size,
57 int escaped));
58static int dim_tri P((unsigned char *str, char *replacement,
59 int size, int is_chord));
60static int smallsize P((int size));
61static int accsize P((int size));
62\f
63
64/* return the height (in inches) of a character of specified font and size */
65
66double
67height(font, size, ch)
68
69int font;
70int size;
71int ch; /* which character */
72
73{
74 int chval;
75
76 chval = ch & 0xff;
77
78 /* control characters have no height */
79 if (chval < FIRST_CHAR) {
80 return(0.0);
81 }
82
83 return((Fontinfo[ font_index(font) ].ch_height[ CHAR_INDEX(chval) ]
84 / FONTFACTOR) * ((float)size / (float)DFLT_SIZE) );
85}
86\f
87
88/* return the width (in inches) of a character of specified font and size */
89
90double
91width(font, size, ch)
92
93int font;
94int size;
95int ch; /* which character */
96
97{
98 int chval;
99
100 chval = ch & 0xff;
101
102 /* control characters have no width */
103 if (chval < FIRST_CHAR) {
104 return(0.0);
105 }
106
107 return((Fontinfo[ font_index(font) ].ch_width[ CHAR_INDEX(chval) ]
108 / FONTFACTOR) * ((float)size / (float)DFLT_SIZE) );
109}
110\f
111
112/* return the ascent (in inches) of a character of specified font and size */
113
114double
115ascent(font, size, ch)
116
117int font;
118int size;
119int ch; /* which character */
120
121{
122 int chval;
123
124 chval = ch & 0xff;
125
126 /* control characters have no ascent */
127 if (chval < FIRST_CHAR) {
128 return(0.0);
129 }
130
131 return((Fontinfo[ font_index(font) ].ch_ascent[ CHAR_INDEX(chval) ]
132 / FONTFACTOR) * ((float) size / (float)DFLT_SIZE) );
133}
134\f
135
136/* return the descent (in inches) of a character of specified font and size */
137
138double
139descent(font, size, ch)
140
141int font;
142int size;
143int ch; /* which character */
144
145{
146 return ( height(font, size, ch) - ascent(font, size, ch) );
147}
148\f
149
150/* given a user input string, normalize it. This means:
151 * Put the default font in [0] and default size in [1] of the string.
152 * Change backslashed things to internal format. Each starts with a
153 * hyper-ASCII code byte and is followed by one or more data bytes.
154 * Note that in all cases in internal format is no longer than the
155 * incoming format.
156 * Change any \f(XX) to STR_FONT font_number
157 * Change any \s(NN) to STR_SIZE actual_size
158 * Note that NN might have a sign to indicate relative size change.
159 * Change any \v(NN) to STR_VERTICAL vertical_offset
160 * Note that NN might have a sign to indicate direction,
161 * negative means downward.
162 * Change any \/ to STR_SLASH
163 * Change any \| to STR_L_ALIGN (piled mode only)
164 * Change any \^ to STR_C_ALIGN (piled mode only)
165 * Change any backslashed space to space when in piled mode
166 * Change any space to newline while in piled mode
167 * Change \(xxxx) to STR_MUS_CHAR size mus_char_code
168 * Change \% to STR_PAGENUM %
169 * Change \# to STR_NUMPAGES #
170 * Change \n to newline
171 * Change \b to STR_BACKSPACE n
172 * where n is how much to back up for the
173 * default size, in BACKSP_FACTORths of an inch
174 * Change backslashed backslash or double quote to just be themselves.
175 * Reject any other control characters or illegal backslash escapes.
176 * The string is null-terminated.
177 *
178 * The normalized string is put back into the original string buffer
179 * that was passed in, and a pointer to it is returned.
180 *
181 * Note that some functions in lyrics.c, and prntdata.c
182 * also have knowledge of the escape conventions,
183 * so if these change, check there too. But it is intended
184 * that all the rest of the code gets at strings indirectly
185 * via functions in this file, so the details can be hidden here.
186 */
187
188char *
189fix_string(string, font, size, fname, lineno)
190
191char *string; /* original string */
192int font; /* default font for string */
193int size; /* default size for string */
194char *fname; /* file name, for error messages */
195int lineno; /* input line number, for error messages */
196
197{
198 char *tmpbuff; /* for normalized string */
199 int leng; /* strlen(string) + 1 */
200 char *inp_p, *out_p; /* walk thru orig & normalized string */
201 int nsize; /* new size */
202 int prevsize; /* previous size */
203 int msize; /* size for music character */
204 int vert; /* argument to \v without sign */
205 int vertval = 0; /* signed argument to \v */
206 int has_vertical = NO; /* YES if \v or pile found */
207 int has_newline = NO; /* YES if \n somewhere in string */
208 int pile_mode = NO;
209 int align_points = 0; /* how many aligments points found */
210 int error; /* YES if have found an error */
211 char spec_name[100], *sc_p; /* name of special music character, or
212 * extended character set character */
213 unsigned char extchar; /* value for extended character */
214 unsigned char muschar; /* value for music character */
215 int now_font; /* current font */
216 int newfont; /* proposed new font */
217 int prevfont; /* previous font */
218 int mfont; /* music font */
219 struct BACKSPACEINFO *backspaceinfo; /* font/size for backspacing */
220 int backspaceindex = 0; /* index into backspaceinfo */
221 float backup; /* backspace distance in inches
222 * for default size */
223 int backupval; /* value to store for backspace
224 * distance */
225
226
227 /* fill in default font and size */
228 string[0] = (char) font;
229 if (rangecheck(size, MINSIZE, MAXSIZE, "size") == NO) {
230 size = MAXSIZE;
231 }
232 string[1] = (char) size;
233 now_font = prevfont = font;
234 prevsize = size;
235
236 leng = strlen(string) + 1;
237 MALLOCA(char, tmpbuff, leng);
238 MALLOC(BACKSPACEINFO, backspaceinfo, leng);
239 /* walk through incoming string, creating normalized string */
240 for (error = NO, out_p = tmpbuff + 2, inp_p = string + 2;
241 (error == NO) && (*inp_p != '\0');
242 inp_p++, out_p++) {
243
244 /* handle backslashed stuff */
245 if (*inp_p == '\\') {
246
247 /* skip past the backslash */
248 inp_p++;
249
250 switch( *inp_p) {
251
252 case '\n':
253 /* ignore the backslashed newline */
254 out_p--;
255 break;
256
257 case '\r':
258 if (*(inp_p) == '\n') {
259 inp_p++;
260 }
261 out_p--;
262 break;
263
264 case 'f':
265 /* font change */
266 inp_p = get_font(++inp_p, &newfont, prevfont,
267 fname, lineno);
268 if (newfont == FONT_UNKNOWN) {
269 error = YES;
270 }
271 else {
272 *out_p++ = (char) STR_FONT;
273 *out_p = (char) newfont;
274 prevfont = now_font;
275 now_font = newfont;
276 }
277 break;
278
279 case 's':
280 /* size change */
281 if (*++inp_p == '(') {
282 switch (*++inp_p) {
283 case '+':
284 inp_p = get_num(++inp_p, &nsize);
285 if (nsize > 0) {
286 nsize += size;
287 }
288 break;
289 case '-':
290 inp_p = get_num(++inp_p, &nsize);
291 if (nsize > 0) {
292 nsize = size - nsize;
293 }
294 break;
295 case 'P':
296 if (strncmp(inp_p, "PV)", 3) == 0) {
297 nsize = prevsize;
298 inp_p += 2;
299 }
300 else {
301 nsize = BAD_NUMBER;
302 }
303 break;
304 case 'p':
305 if (strncmp(inp_p, "previous)", 9) == 0) {
306 nsize = prevsize;
307 inp_p += 8;
308 }
309 else {
310 nsize = BAD_NUMBER;
311 }
312 break;
313 default:
314 inp_p = get_num(inp_p, &nsize);
315 break;
316 }
317 }
318 else {
319 nsize = BAD_NUMBER;
320 }
321
322 /* if got valid size, store it */
323 if (nsize == BAD_NUMBER) {
324 l_yyerror(fname, lineno,
325 "Invalid format for size value");
326 error = YES;
327 }
328 else if (rangecheck(nsize, MINSIZE,
329 MAXSIZE, "size") == YES) {
330 *out_p++ = (char) STR_SIZE;
331 *out_p = (char) nsize;
332 /* save new size */
333 prevsize = size;
334 size = nsize;
335 }
336 else {
337 error = YES;
338 }
339
340 break;
341
342 case 'v':
343 /* vertical motion */
344 if (*++inp_p == '(') {
345 switch (*++inp_p) {
346 case '-':
347 inp_p = get_num(++inp_p, &vert);
348 if (vert >= 0) {
349 vertval = -vert;
350 }
351 break;
352
353 case '+':
354 ++inp_p;
355 /* fall through */
356 default:
357 inp_p = get_num(inp_p, &vert);
358 if (vert >= 0) {
359 vertval = vert;
360 }
361 break;
362 }
363 }
364 else {
365 vert = BAD_NUMBER;
366 }
367
368 if (vert == BAD_NUMBER) {
369 l_yyerror(fname, lineno,
370 "Invalid format for vertical motion value");
371 error = YES;
372 }
373 else if (rangecheck(vertval, -100, 100,
374 "vertical") == YES) {
375 /* if motion is zero, don't even bother
376 * to save it, else do */
377 if (vertval != 0) {
378 /* convert percentage to
379 * STR_VERTICAL units */
380 if (vertval > 0) {
381 vertval = vertval *
382 MAXVERTICAL/100;
383 }
384 else {
385 vertval = -vertval *
386 MINVERTICAL/100;
387 }
388 *out_p++ = (char) STR_VERTICAL;
389 *out_p = (char) ENCODE_VERT(
390 vertval );
391 }
392 }
393 else {
394 error = YES;
395 }
396
397 /* we don't allow backspacing to something
398 * before a vertical motion--this is almost
399 * like a newline. */
400 backspaceindex = 0;
401
402 has_vertical = YES;
403
404 break;
405
406 case ':':
407 /* If this begins a pile, and the next thing
408 * in input ends the pile, just ignore them
409 * both to keep things simpler later. */
410 if (pile_mode == NO && *(inp_p+1) == '\\'
411 && *(inp_p+2) == ':') {
412 inp_p += 2;
413 /* no output character */
414 out_p--;
415 }
416 else {
417 *out_p = (char) STR_PILE;
418 has_vertical = YES;
419 pile_mode = (pile_mode == YES ? NO : YES);
420 }
421 align_points = 0;
422 break;
423
424 case '|':
425 case '^':
426 if (pile_mode == NO) {
427 l_yyerror(fname, lineno,
428 "alignment point only allowed in piled mode");
429 *out_p = *inp_p;
430 }
431
432 else if (++align_points > 1) {
433 l_yyerror(fname, lineno,
434 "only one alignment point allowed per line");
435 *out_p = *inp_p;
436 }
437
438 else if (*inp_p == '^') {
439 int next_ch;
440 *out_p = (char) STR_C_ALIGN;
441 next_ch = *(inp_p+1) & 0xff;
442 /* it's too much trouble to handle
443 * things like font changes between
444 * the \^ and the character that
445 * will be allowed, so disallow them,
446 * since user can easily put them
447 * before the \^ anyway. */
448 if ( (IS_STR_COMMAND(next_ch)
449 && next_ch != STR_MUS_CHAR)
450 || *(inp_p+1) == ' '
451 || iscntrl(*(inp_p+1)) ) {
452 l_yyerror(fname, lineno,
453 "\\^ must be followed by normal character");
454 }
455 }
456 else {
457 *out_p = (char) STR_L_ALIGN;
458 }
459 has_vertical = YES;
460 break;
461
462 case ' ':
463 if (pile_mode == NO) {
464 l_yyerror(fname, lineno,
465 "backslashed space only allowed in piled mode");
466 }
467 *out_p = ' ';
468 break;
469
470 case '/':
471 /* This is only allowed after one
472 * or more digits */
473 if ( inp_p - string < 4 ||
474 ! isdigit( *(inp_p - 2)) ) {
475 l_yyerror(fname, lineno,
476 "slash can only be used after digit(s)");
477 }
478 *out_p = (char) STR_SLASH;
479 break;
480 case '\\':
481 case '"':
482 /* real backslash or embedded quote, copy it */
483 backspaceinfo[backspaceindex].code = *inp_p;
484 backspaceinfo[backspaceindex++].font
485 = (char) now_font;
486 *out_p = *inp_p;
487 break;
488
489 case '(':
490 /* special music character or extended
491 * character set character */
492 /* make copy of name */
493 for ( sc_p = spec_name, inp_p++;
494 *inp_p != ')' && *inp_p != '\0';
495 sc_p++, inp_p++) {
496 *sc_p = *inp_p;
497 }
498 *sc_p = '\0';
499
500#ifdef EXTCHAR
501 /* first see if it is a character in the
502 * extended character set */
503 if ((extchar = ext_name2num(spec_name))
504 != (unsigned char) BAD_CHAR) {
505 /* temporarily change to the extended
506 * character set font that corresponds
507 * to the current normal ASCII font,
508 * and output the extended character
509 * set code for the desired character.
510 * Then go back to original font */
511 *out_p++ = (char) STR_FONT;
512 *out_p++ = (char)
513 (now_font + EXT_FONT_OFFSET);
514 *out_p++ = extchar;
515 *out_p++ = (char) STR_FONT;
516 *out_p = (char) now_font;
517 backspaceinfo[backspaceindex].code
518 = extchar;
519 backspaceinfo[backspaceindex++].font
520 = now_font + EXT_FONT_OFFSET;
521
522 /* mark that this extended character
523 * set font has been used */
524 Font_used[now_font + EXT_FONT_OFFSET] = YES;
525
526 break;
527 }
528#endif
529 /* look up music character with this name */
530 msize = size;
531 if ((muschar = mc_name2num(spec_name, fname,
532 lineno, &msize, &mfont))
533 != (unsigned char) BAD_CHAR) {
534 *out_p++ = (char) mfont2str(mfont);
535 *out_p++ = (char) msize;
536 *out_p = muschar;
537 backspaceinfo[backspaceindex].code
538 = muschar;
539 backspaceinfo[backspaceindex++].font
540 = FONT_MUSIC;
541 }
542 break;
543
544 case '[':
545 /* start of boxed text. We only allow this at
546 * the beginning of a string */
547 if (inp_p != string + 3) {
548 l_yyerror(fname, lineno,
549 "\\[ only allowed at beginning of string");
550 error = YES;
551 }
552 else {
553 *out_p = (char) STR_BOX;
554 }
555 break;
556
557 case ']':
558 /* end of boxed text. Only allowed at end of
559 * string, and only if the string began
560 * with a box start. */
561 if (*(inp_p + 1) != '\0') {
562 l_yyerror(fname, lineno,
563 "\\] only allowed at end of string");
564 error = YES;
565 }
566 else if (IS_BOXED(tmpbuff) == NO) {
567 l_yyerror(fname, lineno,
568 "no matching \\[ for \\]");
569 error = YES;
570 }
571 else {
572 *out_p = (char) STR_BOX_END;
573 }
574 break;
575
576 case '{':
577 /* start of circled text. We only allow this at
578 * the beginning of a string */
579 if (inp_p != string + 3) {
580 l_yyerror(fname, lineno,
581 "\\{ only allowed at beginning of string");
582 error = YES;
583 }
584 else {
585 *out_p = (char) STR_CIR;
586 }
587 break;
588
589 case '}':
590 /* end of circled text. Only allowed at end of
591 * string, and only if the string began
592 * with a circle start. */
593 if (*(inp_p + 1) != '\0') {
594 l_yyerror(fname, lineno,
595 "\\} only allowed at end of string");
596 error = YES;
597 }
598 else if (IS_CIRCLED(tmpbuff) == NO) {
599 l_yyerror(fname, lineno,
600 "no matching \\{ for \\}");
601 error = YES;
602 }
603 else {
604 *out_p = (char) STR_CIR_END;
605 }
606 break;
607
608 case '%':
609 /* too hard to deal with inside a pile... */
610 if (pile_mode == YES) {
611 l_yyerror(fname, lineno,
612 "\\%c not allowed inside a pile\n", '%');
613 }
614
615 /* page number -- change to STR_PAGENUM-% */
616 *out_p++ = (char) STR_PAGENUM;
617 *out_p = '%';
618 /* we really don't know at this point how far
619 * to backspace over pagenum because we don't
620 * know yet how many digits it is, etc, so we
621 * punt and just use the % character
622 * for width */
623 backspaceinfo[backspaceindex].code = '%';
624 backspaceinfo[backspaceindex++].font
625 = (char) now_font;
626 break;
627
628 case '#':
629 /* code basically the same as for % */
630 if (pile_mode == YES) {
631 l_yyerror(fname, lineno,
632 "\\# not allowed inside a pile\n");
633 }
634
635 /* number of pages -- change to STR_NUMPAGES-# */
636 *out_p++ = (char) STR_NUMPAGES;
637 *out_p = '#';
638 /* We really don't know at this point how far
639 * to backspace, because we don't know yet
640 * how many digits it is, etc, so we punt
641 * and just use the # character for width. */
642 backspaceinfo[backspaceindex].code = '#';
643 backspaceinfo[backspaceindex++].font
644 = (char) now_font;
645 break;
646
647 case 'n':
648 /* newline */
649 *out_p = '\n';
650 /* can't back up to previous line */
651 backspaceindex = 0;
652 has_newline = YES;
653 break;
654
655 case 'b':
656 /* can't back up past beginning of string */
657 if (backspaceindex == 0) {
658 if (has_newline == YES || has_vertical == YES) {
659 l_yyerror(fname, lineno,
660 "can't backspace before newline or vertical motion");
661 }
662 else {
663 l_yyerror(fname, lineno,
664 "can't backspace before beginning of line");
665 }
666 error = YES;
667 }
668 else {
669 backspaceindex--;
670 backup = width(backspaceinfo
671 [backspaceindex].font,
672 DFLT_SIZE, backspaceinfo
673 [backspaceindex].code);
674 *out_p++ = (char) STR_BACKSPACE;
675 /* calculate backup value to store */
676 backupval = (int) (backup * BACKSP_FACTOR);
677 if (backupval < 1) {
678 backupval = 1;
679 }
680 else if (backupval > 127) {
681 backupval = 127;
682 }
683 *out_p = (char) backupval;
684 }
685 break;
686
687 default:
688 yyerror("illegal \\ escape");
689 error = YES;
690 break;
691 }
692 }
693
694 else if (iscntrl(*inp_p) ) {
695 if (*inp_p == '\n') {
696 backspaceindex = 0;
697 has_newline = YES;
698 *out_p = *inp_p;
699 }
700 else if (*inp_p == '\r' && *(inp_p+1) == '\n') {
701 /* ignore DOS's extraneous \r */
702 out_p--;
703 }
704 else {
705 /* We don't support any other control
706 * characters, but just convert others to
707 * space and continue. That way user at least
708 * gets something. Tab is something user may
709 * expect to work, so we give a more clear
710 * and specific error for that.
711 */
712 l_warning(fname, lineno,
713 "unsupported control character '\\0%o' %sin string replaced with space",
714 *inp_p, *inp_p =='\t' ? "(tab) ": "");
715 *out_p = ' ';
716 }
717 }
718 else if (pile_mode == YES && *inp_p == ' ') {
719 /* in piled mode, space means move down for next
720 * item in pile. */
721 *out_p = '\n';
722
723 align_points = 0;
724 backspaceindex = 0;
725 }
726 else {
727 /* normal character -- copy as is */
728 *out_p = *inp_p;
729 backspaceinfo[backspaceindex].code = *inp_p;
730 backspaceinfo[backspaceindex++].font = (char) now_font;
731 }
732 }
733 /* If we got an error, we would not have put anything into the
734 * final output position before incrementing out_p in the 'for' loop,
735 * so compensate, so we don't leave a garbage character. */
736 if (error == YES) {
737 out_p--;
738 }
739 *out_p = '\0';
740
741 if (error == NO && IS_BOXED(tmpbuff) == YES &&
742 (*(out_p - 1) & 0xff) != (STR_BOX_END & 0xff)) {
743 l_yyerror(fname, lineno, "no matching \\] for \\[");
744 }
745
746 if (error == NO && IS_CIRCLED(tmpbuff) == YES &&
747 (*(out_p - 1) & 0xff) != (STR_CIR_END & 0xff)) {
748 l_yyerror(fname, lineno, "no matching \\} for \\{");
749 }
750
751 /* to keep things simple, we don't allow
752 * mixing newlines with vertical motion */
753 if (has_vertical == YES && has_newline == YES) {
754 l_yyerror(fname, lineno,
755 "can't have newline in same string with vertical motion or alignment");
756 }
757
758 /* now copy normalized string back onto original */
759 (void) strcpy(string + 2, tmpbuff + 2);
760 FREE(tmpbuff);
761 FREE(backspaceinfo);
762 return(string);
763}
764\f
765
766/* given pointer into a string, read a font name exclosed in parentheses.
767 * Return the corresponding font number, or
768 * FONT_UNKNOWN if name is invalid. Return pointer to last character
769 * processed in string */
770
771static char *
772get_font(string, font_p, prev_font, fname, lineno)
773
774char *string; /* get font from this string */
775int *font_p; /* return new font via this pointer */
776int prev_font; /* previous font */
777char *fname; /* file name for errors */
778int lineno; /* line number, for error messages */
779
780{
781 char fontname[BUFSIZ];
782 int font = FONT_UNKNOWN;
783 char *endparen; /* where ')' is in string */
784 int length; /* of font name */
785
786
787 if (*string == '(') {
788 string++;
789 if ((endparen = strchr(string, ')')) != (char *) 0) {
790 length = endparen - string;
791 (void) strncpy(fontname, string, (unsigned) length);
792 fontname[length] = '\0';
793 string += length;
794 if (strcmp(fontname, "PV") == 0
795 || strcmp(fontname, "previous") == 0) {
796 /* special case of "previous" font */
797 font = prev_font;
798 }
799 else {
800 font = lookup_font(fontname);
801 }
802 }
803 }
804
805 *font_p = font;
806 if (font == FONT_UNKNOWN) {
807 l_yyerror(fname, lineno, "unknown font specified");
808 }
809 return(string);
810}
811\f
812
813/* given a pointer into a string, get a number followed by close parenthesis.
814 * Return the number via pointer, or BAD_NUMBER on error.
815 * Return pointer to the last character processed
816 * in the incoming string */
817
818static char *
819get_num(string, num_p)
820
821char *string; /* get number from this string */
822int *num_p; /* return number via this pointer, or -1 on error */
823
824{
825 if (isdigit(*string)) {
826 *num_p = strtol(string, &string, 10);
827 if (*string != ')') {
828 *num_p = BAD_NUMBER;
829 }
830 }
831 else {
832 *num_p = BAD_NUMBER;
833 }
834 return(string);
835}
836\f
837
838/* compare the charname fields of 2 SPECCHAR structs and return
839 * their proper order, for comparison by bsearch() */
840
841static int
842sc_compare(item1_p, item2_p)
843
844#ifdef __STDC__
845const void *item1_p; /* there are really struct SPECCHAR *, but bsearch
846 * passes them as char * and we have to
847 * cast appropriately */
848const void *item2_p;
849#else
850char *item1_p; /* there are really struct SPECCHAR *, but bsearch passes them
851 * as char * and we have to cast appropriately */
852char *item2_p;
853#endif
854
855{
856 return(strcmp( ((struct SPECCHAR *)(item1_p))->charname,
857 ((struct SPECCHAR *)(item2_p))->charname));
858}
859\f
860
861/* given the name of a music character, return its code number.
862 * If the name is not a valid name, return BAD_CHAR.
863 * Just do a binary search in the name-to-code translation table.
864 */
865
866unsigned char
867mc_name2num(name, fname, lineno, size_p, font_p)
868
869char *name; /* name for a music character */
870char *fname; /* file name for error messages */
871int lineno; /* input line number for error messages */
872int *size_p; /* points to current size, proper size for music character
873 * is returned through here */
874int *font_p; /* FONT_MUSIC* is returned here */
875
876{
877 struct SPECCHAR *info_p;/* translation entry for the given name */
878 struct SPECCHAR key; /* what to look for */
879 int f; /* font index */
880 static unsigned int numch[NUM_MFONTS]; /* how many items in font */
881
882
883 /* first time through, find size of name-to-code table */
884 if (numch[0] == 0) {
885 for (f = 0; f < NUM_MFONTS; f++) {
886 for ( ; Mus_char_table[f][numch[f]].charname != (char *)0;
887 (numch[f])++) {
888 ;
889 }
890 }
891 }
892
893 /* check for "small" characters */
894 if (name[0] == 's' && name[1] == 'm') {
895 key.charname = name + 2;
896 *size_p = smallsize(*size_p);
897 }
898 else {
899 key.charname = name;
900 }
901
902 /* do binary search for code */
903 for (f = 0; f < NUM_MFONTS; f++) {
904 if ((info_p = (struct SPECCHAR *) bsearch((char *) &key, Mus_char_table[f],
905 numch[f], sizeof(struct SPECCHAR), sc_compare))
906 != (struct SPECCHAR *) 0) {
907 *font_p = FONT_MUSIC + f;
908 return( (unsigned char) info_p->code);
909 }
910 }
911
912 l_yyerror(fname, lineno, "unknown music character '%s'", name);
913 *font_p = FONT_MUSIC;
914 return( (unsigned char) BAD_CHAR);
915}
916#ifdef EXTCHAR
917\f
918
919/* given the name of an extended character set character,
920 * return its code number.
921 * If the name is not a valid name, return BAD_CHAR.
922 * Just do a binary search in the name-to-code translation table.
923 */
924
925static unsigned char
926ext_name2num(name)
927
928char *name; /* name for an extended character set character */
929
930{
931 struct SPECCHAR *info_p;/* translation entry for the given name */
932 struct SPECCHAR key; /* what to look for */
933 static unsigned int numch = 0; /* how many items in xlation table */
934 char shortcut[12]; /* full name of shortcutted character */
935
936
937 /* find size of name-to-code table */
938 if (numch == 0) {
939 for ( ; Ext_char_table[numch].charname != (char *) 0;
940 numch++) {
941 ;
942 }
943 }
944
945 key.charname = name;
946
947 /* allow some shortcuts for common diacritical marks. A letter
948 * followed by one of '`^~:/,vo represents acute, grave, circumflex,
949 * tilde, dieresis, slash, cedilla, caron, and ring.
950 * And as a special case, ss represents germandbls */
951 if (strlen(name) == 2 && isalpha(name[0])) {
952 switch (name[1]) {
953 case '\'':
954 (void) sprintf(shortcut, "%cacute", name[0]);
955 key.charname = shortcut;
956 break;
957 case '`':
958 (void) sprintf(shortcut, "%cgrave", name[0]);
959 key.charname = shortcut;
960 break;
961 case '^':
962 (void) sprintf(shortcut, "%ccircumflex", name[0]);
963 key.charname = shortcut;
964 break;
965 case '~':
966 (void) sprintf(shortcut, "%ctilde", name[0]);
967 key.charname = shortcut;
968 break;
969 case ':':
970 (void) sprintf(shortcut, "%cdieresis", name[0]);
971 key.charname = shortcut;
972 break;
973 case '/':
974 (void) sprintf(shortcut, "%cslash", name[0]);
975 key.charname = shortcut;
976 break;
977 case ',':
978 (void) sprintf(shortcut, "%ccedilla", name[0]);
979 key.charname = shortcut;
980 break;
981 case 'v':
982 (void) sprintf(shortcut, "%ccaron", name[0]);
983 key.charname = shortcut;
984 break;
985 case 'o':
986 (void) sprintf(shortcut, "%cring", name[0]);
987 key.charname = shortcut;
988 break;
989 case 's':
990 if (name[0] == 's') {
991 (void) sprintf(shortcut, "germandbls");
992 key.charname = shortcut;
993 }
994 break;
995 default:
996 /* not a special shortcut, leave as is */
997 break;
998 }
999 }
1000 /* Some more special case shortcuts: `` and '' are shortcuts for
1001 * quotedblleft and quotedblright, and << and >> for guillemots */
1002 if (strcmp(name, "``") == 0) {
1003 key.charname = "quotedblleft";
1004 }
1005 else if (strcmp(name, "''") == 0) {
1006 key.charname = "quotedblright";
1007 }
1008 else if (strcmp(name, "<<") == 0) {
1009 key.charname = "guillemotleft";
1010 }
1011 else if (strcmp(name, ">>") == 0) {
1012 key.charname = "guillemotright";
1013 }
1014
1015 /* do binary search for code */
1016 if ((info_p = (struct SPECCHAR *) bsearch((char *) &key, Ext_char_table,
1017 numch, sizeof(struct SPECCHAR), sc_compare))
1018 != (struct SPECCHAR *) 0) {
1019 return( (unsigned char) info_p->code);
1020 }
1021
1022 else {
1023 /* don't do error message here, because it could just be a
1024 * music character rather than an extended character set char */
1025 return( (unsigned char) BAD_CHAR);
1026 }
1027}
1028#endif
1029\f
1030
1031/* given the C_XXX code value for a music character, return the
1032 * user name for the character. The first time this function gets
1033 * called it sets up a translation array. Then it can just look up
1034 * the name by using the code as an index into the array */
1035
1036char *
1037mc_num2name(code, font)
1038
1039int code; /* the code for the music character */
1040int font; /* FONT_MUSIC* */
1041
1042{
1043 static int xlate_tbl[NUM_MFONTS][CHARS_IN_FONT + FIRST_CHAR];
1044 /* translate music char #define
1045 * values to offset in Mus_char_table
1046 * array */
1047 int f; /* font index */
1048 static int called = NO; /* boolean, YES if this function
1049 * has been called before */
1050 register int numch; /* how many music characters to do */
1051
1052
1053 if (called == NO) {
1054 called = YES;
1055 /* first time. need to build table */
1056
1057 /* For each item in the Mus_char_table, fill in the
1058 * element of the xlate_tbl array with its offset,
1059 * or fill in -1 if no valid character with that code. */
1060 for (f = 0; f < NUM_MFONTS; f++) {
1061 for ( numch = 0; numch < CHARS_IN_FONT + FIRST_CHAR; numch++) {
1062 xlate_tbl[f][numch] = -1;
1063 }
1064 }
1065 for (f = 0; f < NUM_MFONTS; f++) {
1066 for ( numch = 0; numch < CHARS_IN_FONT + FIRST_CHAR; numch++) {
1067 if (Mus_char_table[f][numch].charname != 0) {
1068 xlate_tbl [f] [ Mus_char_table[f][numch].code & 0xff ] =
1069 numch;
1070 }
1071 else {
1072 break;
1073 }
1074 }
1075 }
1076 }
1077
1078 /* now we just look up the name */
1079 if ((numch = xlate_tbl[font - FONT_MUSIC][code & 0xff]) < 0) {
1080 pfatal("bad music character [%d][%d] in mc_num2name",
1081 font - FONT_MUSIC, code & 0xff);
1082 }
1083
1084 return( Mus_char_table[font - FONT_MUSIC][numch].charname );
1085}
1086#ifdef EXTCHAR
1087\f
1088
1089/* given the C_XXX code value for an extended character set char, return the
1090 * user name for the character. The first time this function gets
1091 * called it sets up a translation array. Then it can just look up
1092 * the name by using the code as an index into the array */
1093
1094char *
1095ext_num2name(code)
1096
1097int code; /* the code for the extended character set character */
1098
1099{
1100 static int xlate_tbl[CHARS_IN_FONT + FIRST_CHAR];
1101 /* translate extended char
1102 * #define values to offset in
1103 * Ext_char_table array */
1104 static int called = NO; /* boolean, YES if this function
1105 * has been called before */
1106 register int numch; /* how many extended characters to do */
1107
1108
1109 if (called == NO) {
1110 called = YES;
1111 /* first time. need to build table */
1112
1113 /* initialize table to have nothing set */
1114 for ( numch = 0; numch < CHARS_IN_FONT + FIRST_CHAR; numch++) {
1115 xlate_tbl[numch] = -1;
1116 }
1117
1118 /* for each item in the Ext_char_table, fill in the
1119 * element of the xlate_tbl array with its offset */
1120 for (numch = 0; Ext_char_table[numch].charname != (char *) 0;
1121 numch++) {
1122 xlate_tbl [ Ext_char_table[numch].code & 0xff ] =
1123 numch;
1124 }
1125 }
1126
1127 /* now we just look up the name */
1128 if ((numch = xlate_tbl[code & 0xff]) < 0) {
1129 pfatal("bad extended character set character (%d) in ext_num2name", code & 0xff);
1130 }
1131
1132 return( Ext_char_table[numch].charname );
1133}
1134#endif
1135\f
1136
1137/* return YES if string passed in consists solely of a music symbol, otherwise
1138 * return NO */
1139
1140int
1141is_music_symbol(str)
1142
1143char *str; /* which string to check */
1144
1145{
1146 char *string;
1147 int font;
1148 int size;
1149
1150
1151 if (str == (char *) 0) {
1152 return(NO);
1153 }
1154
1155 font = str[0];
1156 size = str[1];
1157 string = str + 2;
1158
1159 /* has to be music char followed by null to be YES */
1160 if (next_str_char(&string, &font, &size) == '\0') {
1161 return(NO);
1162 }
1163 if ( ! IS_MUSIC_FONT(font)) {
1164 return(NO);
1165 }
1166 if (next_str_char(&string, &font, &size) == '\0') {
1167 return(YES);
1168 }
1169 return(NO);
1170}
1171\f
1172
1173/* return the ascent of a string in inches. This is the largest ascent of any
1174 * character in the string */
1175
1176double
1177strascent(str)
1178
1179char *str; /* which string to process */
1180
1181{
1182 float max_ascent, a; /* tallest and current ascent */
1183 char *s; /* to walk through string */
1184 int font, size, code;
1185 int textfont;
1186 double vertical, horizontal;
1187 float baseline_offset; /* to account for vertical motion */
1188 int in_pile;
1189 int only_mus_sym; /* YES if string consists solely
1190 * of a music char */
1191
1192
1193 if (str == (char *) 0) {
1194 return(0.0);
1195 }
1196
1197 only_mus_sym = is_music_symbol(str);
1198
1199 /* first 2 bytes are font and size. */
1200 font = str[0];
1201 size = str[1];
1202
1203 /* Walk through the string. */
1204 for (max_ascent = 0.0, baseline_offset = 0.0, s = str + 2;
1205 (code = nxt_str_char(&s, &font, &size, &textfont,
1206 &vertical, &horizontal, &in_pile, NO)) > 0; ) {
1207
1208 /* A newline goes to following line, so we probably won't
1209 * get any higher ascent than we have so far, but if
1210 * user gives enough vertical motion, we might, so continue. */
1211 if (code == '\n') {
1212 baseline_offset -= fontheight(font, size);
1213 }
1214
1215 /* adjust for any vertical motion */
1216 if (vertical != 0.0) {
1217 baseline_offset += vertical;
1218 }
1219
1220 /* music characters inside strings get moved up to the baseline,
1221 * so use their height as ascent.
1222 * Regular characters use the
1223 * ascent of the character */
1224 if ((IS_MUSIC_FONT(font)) && (only_mus_sym == NO)) {
1225 a = height(font, size, code);
1226 }
1227 else {
1228 a = ascent(font, size, code);
1229 }
1230 a += baseline_offset;
1231
1232 /* if tallest seen save this height */
1233 if (a > max_ascent) {
1234 max_ascent = a;
1235 }
1236 }
1237
1238 /* if boxed, allow space for that */
1239 if (IS_BOXED(str) == YES) {
1240 max_ascent += 2.5 * STDPAD;
1241 }
1242 /* similarly, allow space for circle */
1243 if (IS_CIRCLED(str) == YES) {
1244 float ascent_adjust;
1245 max_ascent += circled_dimensions(str, (float *) 0, (float *) 0,
1246 &ascent_adjust, (float *) 0);
1247 max_ascent += ascent_adjust;
1248 }
1249 return(max_ascent);
1250}
1251\f
1252
1253/* return the descent of a string in inches. This is the largest descent of any
1254 * character in the string */
1255
1256double
1257strdescent(str)
1258
1259char *str; /* which string to process */
1260
1261{
1262 float max_descent, d; /* largest and current descent */
1263 float line_descent; /* descent caused by newlines */
1264 double vertical, horizontal;
1265 int in_pile;
1266 char *s; /* to walk through string */
1267 int font, size, code;
1268 int textfont;
1269 int only_mus_sym; /* YES if string consists solely
1270 * of a music char */
1271
1272
1273 if (str == (char *) 0) {
1274 return(0.0);
1275 }
1276
1277 only_mus_sym = is_music_symbol(str);
1278
1279 /* first 2 bytes are font and size. */
1280 font = str[0];
1281 size = str[1];
1282
1283 /* walk through the string. */
1284 for (max_descent = line_descent = 0.0, s = str + 2;
1285 (code = nxt_str_char(&s, &font, &size, &textfont,
1286 &vertical, &horizontal, &in_pile, NO)) > 0
1287 || vertical != 0.0; ) {
1288
1289 /* Adjust for vertical motion. Since line_descent is
1290 * measured downward and vertical is upward, have to
1291 * substract the vertical, then adjust max_descent
1292 * to compensate. */
1293 if (vertical != 0.0) {
1294 line_descent -= vertical;
1295 max_descent += vertical;
1296 if (code == 0) {
1297 /* motion only */
1298 continue;
1299 }
1300 }
1301
1302 if (code == '\n') {
1303 /* at newline, descent goes down to next baseline,
1304 * which will be down from current baseline
1305 * by height of font */
1306 line_descent += fontheight(font, size);
1307 max_descent = 0.0;
1308 continue;
1309 }
1310
1311 /* music characters inside strings get moved up to the
1312 * baseline, so have no descent. */
1313 if ( ! (IS_MUSIC_FONT(font)) || (only_mus_sym == YES)) {
1314 d = descent(font, size, code);
1315 }
1316 else {
1317 d = 0.0;
1318 }
1319
1320 /* if largest descent seen, save this descent */
1321 if (d > max_descent) {
1322 max_descent = d;
1323 }
1324 }
1325
1326 /* if boxed, allow space for that */
1327 if (IS_BOXED(str) == YES) {
1328 max_descent += 3.5 * STDPAD;
1329 }
1330 /* similarly, allow space for circle */
1331 if (IS_CIRCLED(str) == YES) {
1332 max_descent += circled_dimensions(str, (float *) 0, (float *) 0,
1333 (float *) 0, (float *) 0);
1334 }
1335 return(max_descent + line_descent);
1336}
1337\f
1338
1339/* return the height of a string in inches. This is the maximum ascent plus the
1340 * maximum descent */
1341
1342double
1343strheight(str)
1344
1345char *str; /* which string to process */
1346{
1347 /* Since letters may not
1348 * align because of ascent/descent, we get the tallest extent
1349 * by adding the largest ascent to the largest descent */
1350 return( strascent(str) + strdescent(str));
1351}
1352\f
1353
1354/* return the width of a string. This is the sum of the widths of the
1355 * individual characters in the string */
1356
1357double
1358strwidth(str)
1359char *str;
1360{
1361 float tot_width;
1362 float widest_line; /* for multi-line strings */
1363 float curr_width;
1364 double horizontal, vertical;
1365 int was_in_pile; /* if in pile last time through loop */
1366 int in_pile_now; /* if current character is inside a pile */
1367 char *s; /* to walk through string */
1368 int font, size, code;
1369 int textfont;
1370
1371
1372 if (str == (char *) 0) {
1373 return(0.0);
1374 }
1375
1376 /* first 2 bytes are font and size. */
1377 font = str[0];
1378 size = str[1];
1379
1380 /* walk through string */
1381 was_in_pile = NO;
1382 for (curr_width = tot_width = widest_line = 0.0, s = str + 2;
1383 (code = nxt_str_char(&s, &font, &size, &textfont,
1384 &vertical, &horizontal, &in_pile_now, NO)) > 0;
1385 was_in_pile = in_pile_now) {
1386
1387 /* Piles are handled specially. As soon as we enter a pile,
1388 * we call the function to get its entire width. Then for
1389 * the rest of the pile, we just skip past everything */
1390 if (in_pile_now == YES) {
1391 if (was_in_pile == NO) {
1392 curr_width += pile_width();
1393 if (curr_width > tot_width) {
1394 tot_width = curr_width;
1395 }
1396 }
1397 continue;
1398 }
1399
1400 /* the horizontal movement coming out of a pile doesn't count,
1401 * since it was included in the pile, otherwise it does */
1402 if (was_in_pile == NO) {
1403 curr_width += horizontal;
1404 }
1405 if (curr_width > tot_width) {
1406 tot_width = curr_width;
1407 }
1408
1409 if (code == '\n') {
1410 /* keep track of width line of multi-line string */
1411 if (tot_width > widest_line) {
1412 widest_line = tot_width;
1413 }
1414 tot_width = 0.0;
1415 curr_width = 0.0;
1416 continue;
1417 }
1418
1419 if (code == '\b') {
1420 /* backspace */
1421 tot_width -= backsp_width(size);
1422 curr_width -= backsp_width(size);
1423 continue;
1424 }
1425
1426 /* If we have the special "page number" character,
1427 * or special "total number of pages" character,
1428 * we deal with that here. */
1429 if ( (code == '%' || code == '#') && (s > str + 3)
1430 && ( (*(s-2) & 0xff) == STR_PAGENUM
1431 || (*(s-2) & 0xff) == STR_NUMPAGES) ) {
1432
1433 char pgnumbuff[8], *pgnum_p;
1434
1435 /* convert page number to a string and
1436 * add the width of each character in
1437 * that string. */
1438 (void) sprintf(pgnumbuff, "%d",
1439 code == '%' ? Pagenum : Last_pagenum);
1440
1441 for ( pgnum_p = pgnumbuff; *pgnum_p != '\0';
1442 pgnum_p++) {
1443 curr_width += width(font, size, *pgnum_p);
1444 }
1445 }
1446
1447 else {
1448 /* Oh good. This is a normal case. Just add
1449 * width of this character to width so far */
1450 curr_width += width(font, size, code);
1451 }
1452
1453 if (curr_width > tot_width) {
1454 tot_width = curr_width;
1455 }
1456 }
1457 if (tot_width < widest_line) {
1458 tot_width = widest_line;
1459 }
1460 /* if string is boxed, allow space for the box */
1461 if (IS_BOXED(str) == YES) {
1462 tot_width += 6.0 * STDPAD;
1463 }
1464 /* similarly, allow space for circled */
1465 if (IS_CIRCLED(str) == YES) {
1466 (void) circled_dimensions(str, (float *) 0, &tot_width,
1467 (float *) 0, (float *) 0);
1468 }
1469 return(tot_width);
1470}
1471\f
1472
1473/* Return the width to the "anchor" point of a string. For most strings,
1474 * this will be half the width of the first character. But for a string
1475 * that begins with things piled atop one another, it is the alignment point.
1476 * And for boxed or circled strings, the box or circle must be considered.
1477 */
1478
1479double
1480left_width(string)
1481
1482char *string;
1483
1484{
1485 int font;
1486 int size;
1487 char *pile_start_p; /* where pile begins, if any */
1488
1489 if (starts_piled(string, &font, &size, &pile_start_p) == YES) {
1490 return(align_distance(pile_start_p, font, size));
1491 }
1492 else {
1493 int ch;
1494 float extra; /* space for box or circle, if any */
1495
1496 /* For boxed or circled strings,
1497 * the space for the box or circle must be added in */
1498 if (IS_BOXED(string) == YES) {
1499 extra = 3.5 * STDPAD;
1500 }
1501 else if (IS_CIRCLED(string) == YES) {
1502 (void) circled_dimensions(string, (float *) 0,
1503 (float *) 0, (float *) 0, &extra);
1504 }
1505 else {
1506 extra = 0.0;
1507 }
1508
1509 /* Get half the width of the first character in the string */
1510 font = *string++;
1511 size = *string++;
1512 ch = next_str_char(&string, &font, &size);
1513 return(width(font, size, ch) / 2.0 + extra);
1514 }
1515}
1516\f
1517
1518/* If string begins with piled text, return YES, otherwise NO,
1519 * If YES, also return via pointers the start of the pile and the
1520 * font and size at that point. */
1521
1522static int
1523starts_piled(string, font_p, size_p, pile_start_p_p)
1524
1525char *string;
1526int *font_p;
1527int *size_p;
1528char **pile_start_p_p;
1529
1530{
1531 *font_p = *string++;
1532 *size_p = *string++;
1533
1534 /* walk through string, skipping any leading box/size/font */
1535 for ( ; *string != '\0'; string++) {
1536 if (IS_STR_COMMAND(*string)) {
1537 switch(*string & 0xff) {
1538
1539 case STR_FONT:
1540 *font_p = *(++string);
1541 break;
1542
1543 case STR_SIZE:
1544 *size_p = *(++string);
1545 break;
1546
1547 case STR_BOX:
1548 case STR_CIR:
1549 break;
1550
1551 case STR_PILE:
1552 /* The first thing we found that was not to be
1553 * ignored is the beginning of a pile */
1554 *pile_start_p_p = string;
1555 return(YES);
1556
1557 default:
1558 return(NO);
1559 }
1560 }
1561 else {
1562 break;
1563 }
1564 }
1565 return(NO);
1566}
1567\f
1568
1569/* given a string representing a chord mark, transpose it. For each letter
1570 * 'A' to 'G' optionally followed by an accidental, call function to
1571 * get transposed value. Build new string with transposed values. Free up
1572 * the old string and return the new one. Also, if the accidental was
1573 * of the form &, #, x, or && instead of \(smflat) etc, change to proper
1574 * music symbol. Also handles translation of o, o/ and ^ to dim, halfdim,
1575 * and triangle symbols, and does translation of unescaped accidentals. */
1576
1577char *
1578tranchstr(chordstring, staffno)
1579
1580char *chordstring; /* untransposed string */
1581int staffno; /* which staff it is associated with */
1582 /* A staffno of -1 means no transpose, just translate */
1583
1584{
1585 char tmpbuff[128]; /* temporary copy of transposed string */
1586 char replacement[4]; /* for dim/halfdim/triangle */
1587 short i; /* index into tmpbuff */
1588 unsigned char *str; /* walk through chordstring */
1589 char *transposed; /* new version of letter[accidental] */
1590 char tranbuff[4]; /* to point 'transposed' at if not really
1591 * transposing */
1592 char letter; /* A to G */
1593 char accidental;
1594 int escaped; /* YES is accidental was escaped */
1595 char literal_accidental; /* what would normally be translated */
1596 int nprocessed; /* how many character processed by subroutine */
1597 char *newstring; /* final copy of transposed string */
1598 int n;
1599 int size;
1600 int in_pile; /* YES if inside a pile */
1601 int acc_size; /* size for accidentals */
1602
1603
1604 /* get font/size info */
1605 tmpbuff[0] = chordstring[0];
1606 tmpbuff[1] = chordstring[1];
1607 size = chordstring[1];
1608 in_pile = NO;
1609 str = (unsigned char *) (chordstring + 2);
1610 literal_accidental = '\0'; /* avoids bogus "used before set" warning */
1611
1612 /* walk through original string */
1613 for (i = 2; *str != '\0'; str++) {
1614
1615 /* Be safe. Bail out a little before we reach end,
1616 * because some things take several bytes,
1617 * and it's easiest to just check once per loop. */
1618 if (i > sizeof(tmpbuff) - 8) {
1619 ufatal("chord string too long: '%s'", chordstring + 2);
1620 }
1621
1622 acc_size = accsize(size);
1623
1624 /* If a STR_*, deal with that */
1625 if ((n = str_cmd((char *) str, &size, &in_pile)) > 0) {
1626 strncpy(tmpbuff + i, (char *) str, (unsigned) n);
1627 i += n;
1628 str += n - 1;
1629 }
1630
1631 /* handle backslashed o and ^ */
1632 else if (*str == '\\' && ( *(str+1) == 'o' || *(str+1) == '^' ) ) {
1633 str++;
1634 tmpbuff[i++] = *str;
1635 }
1636
1637 else if (*str >= 'A' && *str <= 'G') {
1638
1639 /* Aha! Something to transpose. */
1640 letter = *str;
1641
1642 str += get_accidental( (unsigned char *) (str + 1),
1643 &accidental, &acc_size, NO, &escaped);
1644 if (escaped == YES) {
1645 /* not *really* an accidental, so save to
1646 * print later. */
1647 literal_accidental = accidental;
1648 accidental = '\0';
1649 }
1650 if (staffno == -1) {
1651 /* not to be transposed, so make a string
1652 * that would be like what tranchnote() would
1653 * return, but with no transposition. */
1654 tranbuff[0] = letter;
1655 tranbuff[1] = accidental;
1656 tranbuff[2] = '\0';
1657 transposed = tranbuff;
1658 }
1659 else {
1660 /* get the transposed value */
1661 transposed = tranchnote(letter, accidental, staffno);
1662 }
1663
1664 /* put transposed letter into output */
1665 tmpbuff[i++] = *transposed;
1666
1667 /* now add accidental if any */
1668 i += add_accidental(tmpbuff + i, (int) *++transposed,
1669 acc_size, NO);
1670
1671 /* add on any escaped pseudo-accidental */
1672 if (escaped == YES) {
1673 i += add_accidental(tmpbuff + i,
1674 (int) literal_accidental,
1675 acc_size, YES);
1676 escaped = NO;
1677 }
1678
1679 /* handle dim/halfdim/triangle transformations */
1680 if ((n = dim_tri(str + 1, replacement, size, YES)) > 0) {
1681 strcpy(tmpbuff + i, replacement);
1682 i += strlen(replacement);
1683 str += n;
1684 }
1685 }
1686 else {
1687 /* Originally we only translated things like # and &
1688 * in chords to musical accidental symbols if they
1689 * immediately followed a letter A-G. But due to
1690 * popular demand, they are now translated everywhere,
1691 * unless escaped. */
1692 nprocessed = get_accidental( (unsigned char *) str,
1693 &accidental, &acc_size, NO, &escaped);
1694 if (nprocessed > 0) {
1695 i += add_accidental(tmpbuff + i,
1696 (int) accidental,
1697 acc_size, escaped);
1698 /* the -1 is because str will get incremented
1699 * at the top of the 'for' */
1700 str += nprocessed - 1;
1701 }
1702 else {
1703 /* something boring. Just copy */
1704 tmpbuff[i++] = *str;
1705 }
1706 }
1707 }
1708
1709 /* need to make permanent copy of new string */
1710 tmpbuff[i++] = '\0';
1711 MALLOCA(char, newstring, i + 1);
1712 (void) memcpy(newstring, tmpbuff, (unsigned) i);
1713
1714 /* free original version */
1715 FREE(chordstring);
1716
1717 /* return new, transposed version */
1718 return(newstring);
1719}
1720\f
1721
1722/* If there is a STR_* command in chord/analysis/figbass, return how
1723 * many characters long it is. Also update the size if the
1724 * command was one to change size, and update pile status if necessary. */
1725
1726static int
1727str_cmd(str, size_p, in_pile_p)
1728
1729char *str; /* check string starting here */
1730int *size_p;
1731int *in_pile_p; /* YES if in pile, may be updated */
1732
1733{
1734 if (IS_STR_COMMAND(*str)) {
1735 switch(*str & 0xff) {
1736
1737 case STR_SIZE:
1738 /* update size */
1739 *size_p = *(str + 1);
1740 /* command plus 1 argument byte */
1741 return(2);
1742
1743 case STR_PAGENUM:
1744 case STR_NUMPAGES:
1745 case STR_FONT:
1746 case STR_BACKSPACE:
1747 case STR_VERTICAL:
1748 /* command plus 1 argument byte */
1749 return(2);
1750
1751 case STR_MUS_CHAR:
1752 /* command plus 2 argument bytes */
1753 return(3);
1754
1755 case STR_PILE:
1756 /* entering/leaving a pile alters the size */
1757 *size_p = pile_size(*size_p, *in_pile_p);
1758 *in_pile_p = (*in_pile_p ? NO : YES);
1759 break;
1760
1761 default:
1762 /* others have no argument bytes */
1763 return(1);
1764 }
1765 }
1766 return(0);
1767}
1768\f
1769
1770/* Check the first character of the given string to see if it is an accidental
1771 * or something that should be translated to an accidental (# & x && and
1772 * maybe n). If so, fill in the accidental. If the accidental was specified
1773 * via a STR_MUS_CHAR, also update the accidental size.
1774 * If no accidental, accidental_p will will filled in
1775 * with '\0'. In any case return how many bytes were processed.
1776 */
1777
1778static int
1779get_accidental(string, accidental_p, acc_size_p, trans_natural, escaped_p)
1780
1781unsigned char *string; /* check this for an accidental */
1782char *accidental_p; /* return the accidental here, or \0 if none */
1783int *acc_size_p; /* return the accidental size here */
1784int trans_natural; /* if YES, translate n to natural, else leave as n */
1785int *escaped_p; /* Return value: YES if the symbol was backslashed */
1786
1787{
1788 unsigned char *str_p;
1789
1790 str_p = string;
1791
1792 /* assume no accidental */
1793 *accidental_p = '\0';
1794
1795 /* check if escaped */
1796 if (*str_p == '\\') {
1797 *escaped_p = YES;
1798 str_p++;
1799 }
1800 else {
1801 *escaped_p = NO;
1802 }
1803
1804 /* See if the following character is an accidental */
1805 switch (*str_p) {
1806
1807 case '#':
1808 case 'x':
1809 *accidental_p = *str_p++;
1810 break;
1811 case '&':
1812 *accidental_p = *str_p++;
1813 /* have to peek ahead to check for double flat,
1814 * but not if escaped, so person can get a literal
1815 * ampersand followed by a flat. */
1816 if (*escaped_p == NO && *str_p == '&') {
1817 /* double flat is 'B' internally */
1818 *accidental_p = 'B';
1819 str_p++;
1820 }
1821 break;
1822
1823 case 'n':
1824 /* naturals are not translated in chords, but are
1825 * in analysis and figbass */
1826 if (trans_natural == YES) {
1827 *accidental_p = *str_p++;
1828 }
1829 break;
1830
1831 case STR_MUS_CHAR:
1832 if (*escaped_p == YES) {
1833 break;
1834 }
1835 /* Check if user put in \(flat) or something
1836 * similar. If so, use that. */
1837 switch (*(str_p + 2)) {
1838 case C_FLAT:
1839 *acc_size_p = *(str_p + 1);
1840 *accidental_p = '&';
1841 str_p += 3;
1842 break;
1843
1844 case C_SHARP:
1845 *acc_size_p = *(str_p + 1);
1846 *accidental_p = '#';
1847 str_p += 3;
1848 break;
1849
1850 case C_DBLFLAT:
1851 *acc_size_p = *(str_p + 1);
1852 *accidental_p = 'B';
1853 str_p += 3;
1854 break;
1855
1856 case C_DBLSHARP:
1857 *acc_size_p = *(str_p + 1);
1858 *accidental_p = 'x';
1859 str_p += 3;
1860 break;
1861
1862 case C_NAT:
1863 /* Always translate the natural symbol,
1864 * even when trans_natural is NO. That really
1865 * applies just to the use of 'n' which is
1866 * likely to be wanted as a real n, whereas
1867 * a music symbol natural is unambiguous. */
1868 *acc_size_p = *(str_p + 1);
1869 *accidental_p = 'n';
1870 str_p += 3;
1871 break;
1872
1873 default:
1874 /* false alarm. Some other
1875 * music character. */
1876 break;
1877 }
1878 break;
1879
1880 default:
1881 /* nothing special */
1882 break;
1883 }
1884
1885 /* If all we saw was a backslash,
1886 * then there wasn't really an accidental */
1887 if (*escaped_p == YES && str_p == string + 1) {
1888 *escaped_p = NO;
1889 str_p = string;
1890 }
1891
1892 return(str_p - string);
1893}
1894\f
1895
1896/* Write the given accidental in the given size to the given string.
1897 * Return how many bytes were added. */
1898
1899static int
1900add_accidental(buff, acc_character, acc_size, escaped)
1901
1902char *buff; /* write into this buffer */
1903int acc_character; /* write this accidental */
1904int acc_size; /* make accidental this big */
1905int escaped; /* if YES, was escaped, so not really an accidental;
1906 * print it as a normal character */
1907
1908{
1909 if (acc_character != '\0') {
1910
1911 /* if escaped, just treat like normal character. */
1912 if (escaped == YES) {
1913 buff[0] = acc_character;
1914 return(1);
1915 }
1916
1917 /* sharps and naturals are tall enough that they can
1918 * make things not line up, so move them down some */
1919 if (acc_character == '#' || acc_character == 'n') {
1920 buff[0] = (char) STR_VERTICAL;
1921 buff[1] = (char) ENCODE_VERT(-4);
1922 buff += 2;
1923 }
1924 /* has accidental. Add STR_MUS_CHAR-size-code */
1925 buff[0] = (char) STR_MUS_CHAR;
1926
1927 /* double sharp is special. It is too small,
1928 * so make it bigger */
1929 if (acc_character == 'x') {
1930 acc_size = (int) ( (float) acc_size
1931 * 1.25);
1932 }
1933 buff[1] = (char) acc_size;
1934
1935 /* use accidental of appropriate type */
1936 switch (acc_character) {
1937
1938 case '#':
1939 buff[2] = C_SHARP;
1940 break;
1941
1942 case '&':
1943 buff[2] = C_FLAT;
1944 break;
1945
1946 case 'x':
1947 buff[2] = C_DBLSHARP;
1948 break;
1949
1950 case 'B':
1951 buff[2] = C_DBLFLAT;
1952 break;
1953
1954 case 'n':
1955 buff[2] = C_NAT;
1956 break;
1957
1958 default:
1959 pfatal("illegal accidental on transposed chord");
1960 break;
1961 }
1962 if (acc_character == '#' || acc_character == 'n') {
1963 buff[3] = (char) STR_VERTICAL;
1964 buff[4] = (char) ENCODE_VERT(4);
1965 /* We added 3 bytes for the accidental, plus
1966 * 2 bytes before and after for vertical motion. */
1967 return(7);
1968 }
1969 else {
1970 return(3); /* we added 3 bytes */
1971 }
1972 }
1973
1974 return (0);
1975}
1976\f
1977
1978/* In chords and such, "o" becomes \(dim), "o/" becomes \(halfdim)
1979 * unless followed by [A-G] in which case it becomes "\(dim)/",
1980 * and "^" becomes \(triangle). Return number of characters processed.
1981 */
1982
1983static int
1984dim_tri(str_p, replacement, size, is_chord)
1985
1986unsigned char *str_p; /* check string at this point */
1987char *replacement; /* return the replacement in this buffer,
1988 * which needs to be at least 4 bytes long */
1989int size; /* use this size for music character */
1990int is_chord; /* YES for chord, NO for analysis/figbass */
1991
1992{
1993 if (*str_p == '^') {
1994 replacement[0] = (char) STR_MUS_CHAR;
1995 replacement[1] = size;
1996 replacement[2] = C_TRIANGLE;
1997 replacement[3] = '\0';
1998 return(1);
1999 }
2000 else if (*str_p == 'o') {
2001 replacement[0] = (char) STR_MUS_CHAR;
2002 replacement[1] = size;
2003 replacement[3] = '\0';
2004 if ( *(str_p+1) == '/' && (is_chord == NO ||
2005 (*(str_p+2) < 'A' || *(str_p+2) > 'G'))) {
2006 replacement[2] = C_HALFDIM;
2007 return(2);
2008 }
2009 else {
2010 replacement[2] = C_DIM;
2011 return(1);
2012 }
2013 }
2014 return(0);
2015}
2016\f
2017
2018/* Given a string for analysis or figbass, transform the accidentals
2019 * & # && x n to their music characters.
2020 */
2021
2022char *
2023acc_trans(string)
2024
2025char *string;
2026
2027{
2028 char buffer[128]; /* output buffer for transformed string */
2029 char *out_p; /* current location in output buffer */
2030 char replacement[4]; /* space for dim, halfdim, etc */
2031 int n;
2032 int size, acc_size;
2033 char accidental; /* #, &, x, etc */
2034 int escaped; /* YES is accidental was escaped */
2035 int in_pile; /* YES if inside a pile */
2036
2037
2038 buffer[0] = string[0];
2039 buffer[1] = string[1];
2040 size = string[1];
2041 in_pile = NO;
2042
2043 /* walk through string, transforming any accidentals along the way */
2044 for ( string += 2, out_p = buffer + 2; *string != '\0'; ) {
2045 /* Be safe. Bail out a little before we reach end,
2046 * because some things take several bytes,
2047 * and it's easiest to just check once per loop. */
2048 if (out_p - buffer > sizeof(buffer) - 8) {
2049 l_ufatal(Curr_filename, yylineno,
2050 "analysis or figbass string too long");
2051 }
2052
2053 acc_size = accsize(size);
2054 if ((n = get_accidental((unsigned char *) string,
2055 &accidental, &acc_size, YES, &escaped)) > 0 ) {
2056 out_p += add_accidental(out_p, (int) accidental,
2057 acc_size, escaped);
2058 string += n;
2059 }
2060 else if (*string == '\\' && ( *(string+1) == 'o' || *(string+1) == '^') ) {
2061 *out_p++ = *++string;
2062 string++;
2063 }
2064 else if ((n = dim_tri((unsigned char *) string, replacement,
2065 size, NO)) > 0) {
2066 strcpy(out_p, replacement);
2067 out_p += strlen(replacement);
2068 string += n;
2069 }
2070 else if ((n = str_cmd(string, &size, &in_pile)) > 0) {
2071 strncpy(out_p, string, (unsigned) n);
2072 out_p += n;
2073 string += n;
2074 }
2075 else {
2076 *out_p++ = *string++;
2077 }
2078 }
2079 *out_p = '\0';
2080
2081 return(copy_string(buffer + 2, buffer[0], buffer[1]));
2082}
2083\f
2084/* Given a chord, analysis or figbass string,
2085 * transform according to their special rules:
2086 * - : gets translated to \: and vice-versa
2087 * - figbass starts in piled mode
2088 * - in figbass, a / gets translated to \/ and vice-versa
2089 * This string will be in half transformed state: the first 2 bytes
2090 * are font/size, but the rest is still all ASCII, not internal format.
2091 */
2092
2093char *
2094modify_chstr(string, modifier)
2095
2096char *string;
2097int modifier;
2098
2099{
2100 int length; /* of modified string */
2101 char *s; /* walk through string */
2102 char *newstring;
2103 char *new_p; /* walk through newstring */
2104 int need_new; /* if we need to make a new string */
2105
2106
2107 length = strlen(string);
2108 if (modifier == TM_FIGBASS) {
2109 /* We'll need two extra bytes for
2110 * the leading \: for pile mode. */
2111 length += 2;
2112 need_new = YES;
2113 }
2114 else {
2115 /* Only need a new string if the original has colons,
2116 * so assume for now we won't need a new string */
2117 need_new = NO;
2118 }
2119
2120 /* Figure out how much space we'll need for the modified string.
2121 * Any unbackslashed colons will take up an extra byte once
2122 * we backslash it. But any backslashed one will take up one
2123 * less when we unescape it. Similar for slashes in figbass. */
2124 for (s = string + 2; *s != '\0'; s++) {
2125 if (*s == ':') {
2126 length++;
2127 need_new = YES;
2128 }
2129 else if (modifier == TM_FIGBASS && *s == '/') {
2130 /* o/ means half diminished so that doesn't count */
2131 if (s > string + 2 && *(s-1) == 'o') {
2132 continue;
2133 }
2134 length++;
2135 need_new = YES;
2136 }
2137 else if (*s == '\\') {
2138 s++;
2139 /* things that occur inside \(...) don't count */
2140 if (*s == '(') {
2141 for (s++; *s != '\0' && *s != ')'; s++) {
2142 ;
2143 }
2144 /* If no closing parenthesis, return as is;
2145 * later code will catch that */
2146 if (*s == '\0') {
2147 return(string);
2148 }
2149 }
2150 else if (*s == ':') {
2151 length--;
2152 need_new = YES;
2153 }
2154 else if (modifier == TM_FIGBASS && *s == '/') {
2155 length--;
2156 need_new = YES;
2157 }
2158 }
2159 }
2160
2161 /* If string is okay as is, we are done here */
2162 if (need_new == NO) {
2163 return(string);
2164 }
2165
2166 /* get enough space for new string */
2167 MALLOCA(char, newstring, length + 1);
2168
2169 /* copy font/size */
2170 newstring[0] = string[0];
2171 newstring[1] = string[1];
2172
2173 new_p = newstring + 2;
2174 s = string + 2;
2175 if (modifier == TM_FIGBASS) {
2176 /* add \: but after box, if any */
2177 if (string[2] == '\\' && string[3] == '[') {
2178 *new_p++ = *s++;
2179 *new_p++ = *s++;
2180 }
2181 *new_p++ = '\\';
2182 *new_p++ = ':';
2183 }
2184
2185 /* walk through rest of string, copying, but transforming
2186 * any slashes and colons along the way */
2187 for ( ; *s != '\0'; s++, new_p++) {
2188
2189 /* handle colons */
2190 if (*s == ':') {
2191 /* add a backslash */
2192 *new_p++ = '\\';
2193 }
2194 else if (*s == '\\' && *(s+1) == ':') {
2195 /* skip past the backslash */
2196 s++;
2197 }
2198
2199 /* handle slashes in figbass */
2200 else if (modifier == TM_FIGBASS) {
2201 if (*s == '/') {
2202 /* o/ means half diminished
2203 * so that doesn't count */
2204 if (s <= string + 2 || *(s-1) != 'o') {
2205 /* add a backslash */
2206 *new_p++ = '\\';
2207 }
2208 }
2209 else if (*s == '\\' && *(s+1) == '/') {
2210 /* skip past the backslash */
2211 s++;
2212 }
2213 }
2214
2215 /* copy from original string to new one */
2216 *new_p = *s;
2217 }
2218
2219 /* original is now no longer needed */
2220 FREE(string);
2221
2222 /* terminate and return the modified string */
2223 *new_p = '\0';
2224 return(newstring);
2225}
2226\f
2227
2228/* given an integer point size, return the integer point size appropriate
2229 * for a "small" version. This is SM_FACTOR times the size, rounded, but
2230 * not less than 1. */
2231
2232static int
2233smallsize(size)
2234
2235int size;
2236
2237{
2238 size = (int) ( (float) size * SM_FACTOR);
2239 if (size < 1) {
2240 size = 1;
2241 }
2242 return(size);
2243}
2244\f
2245
2246/* accidentals in chords need to be scaled. Given a size, return the size
2247 * that an accidental should be. This is 60% of given size, rounded to
2248 * an integer, but no smaller than 1. */
2249
2250static int
2251accsize(size)
2252
2253int size;
2254
2255{
2256 size = (int) ( (float) size * 0.6);
2257 if (size < 1) {
2258 size = 1;
2259 }
2260 return(size);
2261}
2262\f
2263
2264/* return which character to use for rest, based on basictime */
2265
2266int
2267restchar(basictime)
2268
2269int basictime;
2270
2271{
2272 if (basictime < -1) {
2273 pfatal("tried to get rest character for multirest");
2274 /*NOTREACHED*/
2275 return(0);
2276 }
2277
2278 else if (basictime == -1) {
2279 /* quad rest */
2280 return (C_QWHREST);
2281 }
2282
2283 else if (basictime == 0) {
2284 /* double whole rest */
2285 return (C_DWHREST);
2286 }
2287
2288 else {
2289 /* other non-multirest */
2290 return (Resttab [ drmo(basictime) ] );
2291 }
2292}
2293\f
2294
2295/* return YES if given font is an italic font (includes boldital too) */
2296
2297int
2298is_ital_font(font)
2299
2300int font;
2301
2302{
2303 return(Fontinfo[ font_index(font) ].is_ital);
2304}
2305\f
2306
2307/* given a string, return, via pointers the font and size in effect at the
2308 * end of the string */
2309
2310void
2311end_fontsize(str, font_p, size_p)
2312
2313char *str; /* check this string */
2314int *font_p; /* return font at end of str via this pointer */
2315int *size_p; /* return size at end of str via this pointer */
2316
2317{
2318 if (str == (char *) 0) {
2319 /* empty string, use defaults */
2320 *font_p = FONT_TR;
2321 *size_p = DFLT_SIZE;
2322 return;
2323 }
2324
2325 /* find the font/size in effect at end of given string */
2326 *font_p = *str++;
2327 *size_p = *str++;
2328 while (next_str_char(&str, font_p, size_p) != '\0') {
2329 ;
2330 }
2331}
2332\f
2333
2334/* given a string, return a string made up of a dash in the font and size
2335 * of the end of the given string. However, if the string ends with a ~ or _
2336 * return a string containing that instead */
2337
2338char *
2339dashstr(str)
2340
2341char *str; /* return dash with same font/size as end of this string */
2342
2343{
2344 int font, size;
2345 char *newstring;
2346 int ch; /* character to use */
2347
2348
2349 end_fontsize(str, &font, &size);
2350 ch = last_char(str);
2351 if (ch != '~' && ch != '_') {
2352 ch = '-';
2353 }
2354
2355 /* allocate space for dash string and fill it in */
2356 MALLOCA(char, newstring, 4);
2357 newstring[0] = (char) font;
2358 newstring[1] = (char) size;
2359 newstring[2] = (char) ch;
2360 newstring[3] = '\0';
2361 return(newstring);
2362}
2363\f
2364
2365/* Given an internal format string, create an ASCII-only string. Flags
2366 * tell how complete a conversion to do. If verbose is YES, try to convert
2367 * everything back to user's original input, otherwise ignore special things
2368 * other than music characters, extended characters, and backspace.
2369 * If pagenum is YES, interpolate the current page number rather than using %.
2370 *
2371 * Recreating the original user string is not perfect, but is usually right.
2372 * Where there are shortcuts, we can't tell if user used them or not.
2373 * Extended characters are output by name even if user put them in as single
2374 * Latin-1 characters. But we couldn't use the Latin-1 hyper-ASCII in midi
2375 * anyway, because they have high bit set.
2376 *
2377 * Returns the ASCII-ized string, which is stored in an area that will get
2378 * overwritten on subsequent calls, so if caller needs a permanent copy,
2379 * they have to make it themselves.
2380 */
2381
2382/* This is how much to malloc at a time to hold the ASCII-ized string */
2383#define ASCII_BSIZE 512
2384
2385char *
2386ascii_str(string, verbose, pagenum, textmod)
2387
2388char *string; /* internal format string to convert */
2389int verbose; /* If YES, try to reproduce user's original input */
2390int pagenum; /* YES (interpolate number for \%) or NO (leave \% as is) */
2391int textmod; /* TM_ value */
2392
2393{
2394 static char *buff = 0; /* for ASCII-ized string */
2395 static unsigned buff_length = 0;/* how much is malloc-ed */
2396 int i; /* index into ASCII-ized string */
2397 char *musname; /* music character name */
2398 int in_pile = NO;
2399 char *str; /* walk through string */
2400 int musfont; /* FONT_MUSIC* */
2401
2402
2403 /* first time, get some space */
2404 if (buff_length == 0) {
2405 buff_length = ASCII_BSIZE;
2406 MALLOCA(char, buff, buff_length);
2407 }
2408
2409 /* walk through string */
2410 i = 0;
2411 /* special case: normally we implicitly begin a figbass with a
2412 * pile start, but if users cancels that, it won't be there */
2413 if (textmod == TM_FIGBASS &&
2414 (((unsigned char) *(string+2)) & 0xff) != STR_PILE) {
2415 buff[i++] = ':';
2416 }
2417 for (str = string + 2; *str != '\0'; str++) {
2418 switch ( ((unsigned char) *str) & 0xff) {
2419
2420 case STR_FONT:
2421 str++;
2422#ifdef EXTCHAR
2423 if ( (int) *str > EXT_FONT_OFFSET) {
2424 str++;
2425 /* translate to Mup name */
2426 (void) sprintf(buff + i, "\\(%s)",
2427 ext_num2name((int) *str));
2428 while (buff[i] != '\0') {
2429 i++;
2430 }
2431 /* skip past the return to original font */
2432 str += 2;
2433 }
2434 else if (verbose == YES) {
2435#else
2436 if (verbose == YES) {
2437#endif
2438 (void) sprintf(buff + i, "\\f(%s)",
2439 fontnum2name((int) *str));
2440 while (buff[i] != '\0') {
2441 i++;
2442 }
2443 }
2444 break;
2445
2446 case STR_SIZE:
2447 str++;
2448 if (verbose == YES) {
2449 (void) sprintf(buff + i, "\\s(%d)", (int) *str);
2450 while (buff[i] != '\0') {
2451 i++;
2452 }
2453 }
2454 break;
2455
2456 case STR_VERTICAL:
2457 str++;
2458 if (verbose == YES) {
2459 (void) sprintf(buff + i, "\\v(%d)",
2460 DECODE_VERT((int) *str) * 100
2461 / MAXVERTICAL);
2462 while (buff[i] != '\0') {
2463 i++;
2464 }
2465 }
2466 break;
2467
2468 case STR_MUS_CHAR:
2469 case STR_MUS_CHAR2:
2470 musfont = str2mfont( ((unsigned char) *str) & 0xff);
2471
2472 /* skip past the size byte,
2473 * and on to the character code. */
2474 str += 2;
2475 /* In chordlike stuffs, we translate things like
2476 * # and &&, so translate them back. It's possible
2477 * the user used the names explicitly rather than us
2478 * translating, in which case this won't be
2479 * strictly what they put in, but it will be
2480 * consistent, so that a caller of this function
2481 * can easily sort or compare values
2482 * without having to know (for example)
2483 * that '#' and \(smsharp) are the same thing. */
2484 musname = 0;
2485 if (IS_CHORDLIKE(textmod) == YES
2486 && musfont == FONT_MUSIC) {
2487 switch( ((unsigned char) *str) & 0xff) {
2488 case C_SHARP:
2489 musname = "#";
2490 break;
2491 case C_FLAT:
2492 musname = "&";
2493 break;
2494 case C_DBLSHARP:
2495 musname = "x";
2496 break;
2497 case C_DBLFLAT:
2498 musname = "&&";
2499 break;
2500 case C_NAT:
2501 if (textmod != TM_CHORD) {
2502 musname = "n";
2503 }
2504 break;
2505 case C_DIM:
2506 musname = "o";
2507 break;
2508 case C_HALFDIM:
2509 musname = "o/";
2510 break;
2511 case C_TRIANGLE:
2512 musname = "^";
2513 break;
2514 default:
2515 break;
2516 }
2517 }
2518 if (musname != 0) {
2519 (void) sprintf(buff + i, musname);
2520 }
2521 else {
2522 (void) sprintf(buff + i, "\\(%s)",
2523 mc_num2name((int) *str, musfont));
2524 }
2525 while (buff[i] != '\0') {
2526 i++;
2527 }
2528
2529 break;
2530
2531 case STR_BACKSPACE:
2532 if (verbose == YES) {
2533 buff[i++] = '\\';
2534 buff[i++] = 'b';
2535 }
2536 /* ignore this and following char */
2537 str++;
2538 break;
2539
2540 case STR_PRE:
2541 case STR_PST:
2542 if (verbose == YES) {
2543 buff[i++] = '<';
2544 }
2545 break;
2546
2547 case STR_U_PRE:
2548 case STR_U_PST:
2549 if (verbose == YES) {
2550 buff[i++] = '<';
2551 buff[i++] = '^';
2552 }
2553 break;
2554
2555 case STR_PRE_END:
2556 case STR_PST_END:
2557 if (verbose == YES) {
2558 buff[i++] = '>';
2559 }
2560 break;
2561
2562 case STR_BOX:
2563 if (verbose == YES) {
2564 buff[i++] = '\\';
2565 buff[i++] = '[';
2566 }
2567 break;
2568
2569 case STR_BOX_END:
2570 if (verbose == YES) {
2571 buff[i++] = '\\';
2572 buff[i++] = ']';
2573 }
2574 break;
2575
2576 case STR_CIR:
2577 if (verbose == YES) {
2578 buff[i++] = '\\';
2579 buff[i++] = '{';
2580 }
2581 break;
2582
2583 case STR_CIR_END:
2584 if (verbose == YES) {
2585 buff[i++] = '\\';
2586 buff[i++] = '}';
2587 }
2588 break;
2589
2590 case STR_C_ALIGN:
2591 if (verbose == YES) {
2592 buff[i++] = '\\';
2593 buff[i++] = '^';
2594 }
2595 break;
2596
2597 case STR_L_ALIGN:
2598 if (verbose == YES) {
2599 buff[i++] = '\\';
2600 buff[i++] = '|';
2601 }
2602 break;
2603
2604 case STR_PILE:
2605 if (verbose == YES) {
2606 /* On figbass, we implictly add a pile start */
2607 if (textmod == TM_FIGBASS && string + 2 == str) {
2608 ;
2609 }
2610 /* if this is at the end of a padded string,
2611 * there is a high probability it is one
2612 * we added implicitly, so skip it */
2613 else if (in_pile == YES && *(str+1) == ' ' &&
2614 *(str+2) == '\0') {
2615 ;
2616 }
2617 else {
2618 /* in chordlike things, user didn't
2619 * use a backslash, else they did */
2620 if (IS_CHORDLIKE(textmod) == NO) {
2621 buff[i++] = '\\';
2622 }
2623 buff[i++] = ':';
2624 }
2625 }
2626 /* keep track of toggle state */
2627 in_pile = (in_pile == YES ? NO : YES);
2628 break;
2629
2630 case STR_SLASH:
2631 if (verbose == YES && textmod != TM_FIGBASS) {
2632 buff[i++] = '\\';
2633 }
2634 buff[i++] = '/';
2635 break;
2636
2637 case STR_PAGENUM:
2638 case STR_NUMPAGES:
2639 if (pagenum == YES) {
2640 /* Write page number and update length.
2641 * Actually, we don't have the correct values
2642 * for this until late in program execution,
2643 * and for MIDI, there are no pages at all,
2644 * and this can be called from MIDI, so
2645 * this is probably not really very useful,
2646 * but this is the best we can do... */
2647 (void) sprintf(buff + i, "%d",
2648 (((unsigned char) *str) & 0xff)
2649 == STR_PAGENUM ?
2650 Pagenum : Last_pagenum);
2651 while (buff[i] != '\0') {
2652 i++;
2653 }
2654 }
2655 else {
2656 buff[i++] = '\\';
2657 buff[i++] = *(str+1);
2658 }
2659 str++;
2660 break;
2661
2662 case '\\':
2663 buff[i++] = '\\';
2664 buff[i++] = '\\';
2665 break;
2666
2667 default:
2668 if (*str == '\n') {
2669 if (in_pile == YES) {
2670 if ( *(str+1) != '\0') {
2671 buff[i++] = ' ';
2672 }
2673 }
2674 else {
2675 buff[i++] = '\\';
2676 buff[i++] = 'n';
2677 }
2678 }
2679 else if (IS_CHORDLIKE(textmod) == YES && *str == ':') {
2680 buff[i++] = '\\';
2681 buff[i++] = ':';
2682 }
2683 else if (textmod == TM_FIGBASS && *str == '/') {
2684 buff[i++] = '\\';
2685 buff[i++] = '/';
2686 }
2687 else if (*str == ' ' && *(str+1) == '\0') {
2688 /* This is probably a space padding
2689 * that we added implicitly,
2690 * so don't print it. If this is
2691 * called on a 'with' item or 'print' item
2692 * where user explicitly added a space,
2693 * this will strip that off, which, strictly
2694 * speaking, it shouldn't. But that would
2695 * only be for debugging anyway, and a
2696 * strange case, so don't worry about it. */
2697 ;
2698 }
2699 else {
2700 /* ordinary character */
2701 buff[i++] = *str;
2702 }
2703 }
2704
2705 /* If running low on space, get some more. Could probably
2706 * just truncate, since this is used for things like error
2707 * messages, but alloc-ing more is easy enough. */
2708 if (i > buff_length - 20) {
2709 buff_length += ASCII_BSIZE;
2710 REALLOCA(char, buff, buff_length);
2711 }
2712 }
2713 buff[i++] = '\0';
2714
2715 return(buff);
2716}
2717\f
2718
2719/*
2720 * Given a text string and a maximum desired width, try adding newlines at
2721 * white space to bring the width down under the desired width. If that's
2722 * not possible, do the best we can. Return pointer to the possibly
2723 * altered string.
2724 */
2725
2726char *
2727split_string(string, desired_width)
2728
2729char *string;
2730double desired_width;
2731
2732{
2733 char *last_white_p; /* where last white space was */
2734 char *curr_white_p; /* white space we're dealing with now */
2735 char *str; /* to walk through string */
2736 double proposed_width; /* width of string so far */
2737 int font, size;
2738 int c; /* the current character in string */
2739 int save_c; /* temporary copy of c */
2740 int save_str; /* temporary copy of character from string */
2741
2742
2743 /* Piles are incompatible with newlines, so we don't want to
2744 * even attempt to split a string with a pile in it. */
2745 for (str = string + 2; *str != '\0'; str++) {
2746 if ((*str & 0xff) == STR_PILE) {
2747 /* string has a pile, so return it as is */
2748 return(string);
2749 }
2750 }
2751
2752 /* Go through the string, until we hit white space. */
2753 last_white_p = (char *) 0;
2754 font = string[0];
2755 size = string[1];
2756 str = string + 2;
2757 while ((c = next_str_char(&str, &font, &size)) != '\0') {
2758
2759 /* Are we at white space? */
2760 if ( ! IS_MUSIC_FONT(font) && (c == ' ' || c == '\t')) {
2761
2762 /* Temporarily replace with newline, and terminate
2763 * to get width so far if we were to add a newline */
2764 curr_white_p = str - 1;
2765 save_c = c;
2766 save_str = *str;
2767 *curr_white_p = '\n';
2768 *str = '\0';
2769 proposed_width = strwidth(string);
2770 *curr_white_p = save_c;
2771 *str = save_str;
2772
2773 if (proposed_width > desired_width) {
2774 if (last_white_p != (char *) 0) {
2775 /* reduce the width of the string by
2776 * changing the most recent white space
2777 * to a newline */
2778 *last_white_p = '\n';
2779
2780 /* if the overall string is now short
2781 * enough, we are done */
2782 if (strwidth(string) <= desired_width) {
2783 return(string);
2784 }
2785 last_white_p = curr_white_p;
2786 }
2787 else {
2788 /* No previous white space, so we
2789 * can't make it short enough. So change
2790 * this current white space to a
2791 * newline, since that's the best we
2792 * can do. But also set the desired
2793 * width to our current width,
2794 * because we know we're
2795 * going to have to be at least this
2796 * wide anyway, so we might as well use
2797 * this much space on future lines */
2798 *curr_white_p = '\n';
2799 desired_width = proposed_width;
2800
2801 /* no longer have a previous
2802 * white space on the current line,
2803 * because we just started a new
2804 * line */
2805 last_white_p = (char *) 0;
2806 }
2807
2808 }
2809 else {
2810 /* not too wide yet. Remember where this white
2811 * space is, in case the next word makes us
2812 * too wide and we have to change it to a
2813 * newline */
2814 last_white_p = curr_white_p;
2815 }
2816 }
2817 }
2818
2819 /* If last word went over the edge, move to next line if possible. */
2820 if (strwidth(string) > desired_width && last_white_p != (char *) 0) {
2821 *last_white_p = '\n';
2822 }
2823
2824 /* Return the (possibly altered) string */
2825 return(string);
2826}
2827\f
2828
2829/* Given a point size and an adjustment factor, return a new point size.
2830 * If size would be less than MINSIZE, return MINSIZE.
2831 * If it would be greater than MAXSIZE, print error and return MAXSIZE.
2832 * Since we only use integer sizes, there may be some roundoff error.
2833 * While it would be possible to dream up a pathological case
2834 * where this roundout might be big enough to notice,
2835 * for any sane scenario you would probably need
2836 * an extremely high resolution printer and a microscope to notice.
2837 */
2838
2839int
2840adj_size(size, scale_factor, filename, lineno)
2841
2842int size; /* original point size */
2843double scale_factor; /* multiply original size by this factor */
2844char *filename; /* filename and lineno are for error messages */
2845int lineno;
2846
2847{
2848 size = (int) ((double) size * scale_factor + 0.5);
2849 if (size < MINSIZE) {
2850 return(MINSIZE);
2851 }
2852 if (size > MAXSIZE) {
2853 l_warning(filename, lineno,
2854 "Adjusted size of string would be bigger than %d", MAXSIZE);
2855 return(MAXSIZE);
2856 }
2857 return(size);
2858}
2859\f
2860
2861/* Given a string that is in internal format, and a scale factor by which to
2862 * resize that string, adjust all size bytes in the string.
2863 */
2864
2865char *
2866resize_string(string, scale_factor, filename, lineno)
2867
2868char *string; /* this is the string to adjust */
2869double scale_factor; /* adjust sizes in string by this factor */
2870char *filename; /* for error messages */
2871int lineno; /* for error messages */
2872
2873{
2874 char *s; /* to walk through string */
2875
2876
2877 /* if string is empty, nothing to do */
2878 if (string == (char *) 0 || *string == '\0') {
2879 return(string);
2880 }
2881
2882 /* if factor is sufficiently close to 1.0 that it's very clear
2883 * we won't be making any changes (since we only use integer
2884 * point sizes), don't bother */
2885 if ( fabs( (double) (scale_factor - 1.0)) < 0.01) {
2886 return(string);
2887 }
2888
2889 /* second byte is size byte, so adjust that */
2890 string[1] = (char) adj_size( (int) string[1], scale_factor,
2891 filename, lineno);
2892
2893 /* Go through the string. For each size byte, replace it with an
2894 * adjusted size. Size bytes occur immediately after STR_SIZE
2895 * and STR_MUS_CHAR commands. Everything else can get copied as
2896 * is: STR_BACKSPACE is in terms of the default size, so it is
2897 * unaffected by this resizing, and the other special string commands
2898 * are unrelated to size and thus unaffected. */
2899 for (s = string + 2; *s != '\0'; s++) {
2900 switch ( (unsigned char) *s ) {
2901 case STR_SIZE:
2902 case STR_MUS_CHAR:
2903 s++;
2904 *s = (char) adj_size( (int) *s, scale_factor,
2905 filename, lineno);
2906 break;
2907 default:
2908 break;
2909 }
2910 }
2911
2912 return(string);
2913}
2914\f
2915
2916/* Given a circled string, return how much to add to its ascent and
2917 * descent to give room for the circle. If pointer arguments are non-zero,
2918 * return additional values via those pointers.
2919 */
2920
2921double
2922circled_dimensions(str, height_p, width_p, ascent_adjust, x_offset_p)
2923
2924char *str; /* a circled string */
2925float *height_p; /* if non-zero, return circled height here */
2926float *width_p; /* if non-zero, return circled width here */
2927float *ascent_adjust; /* if non-zero, return amount we added to
2928 * ascent to bring up to minimum height */
2929float *x_offset_p; /* if non-zero, return where to print the
2930 * actual string relative to circle edge */
2931
2932{
2933 int font, size;
2934 float min_height;
2935 float adjust; /* amount to bring up to min height */
2936 float uncirc_height, uncirc_width;/* dimensions of uncircled str */
2937 float circ_height; /* height including circle */
2938 float circ_width; /* width including circle */
2939 float circ_extra; /* how much to add to top and
2940 * bottom to allow space for circle */
2941
2942
2943 /* temporarily make the string uncircled */
2944 size = str[2] = str[1];
2945 font = str[1] = str[0];
2946 /* Note that there is at least one circumstance (in split_string())
2947 * where a circled string is temporarily lacking the trailing END_CIR,
2948 * and strheight and strwidth don't need it, so we don't need
2949 * to blank that out. */
2950
2951 /* get the dimensions of the uncircled version */
2952 uncirc_height = strheight(str+1);
2953 uncirc_width = strwidth(str+1);
2954
2955 /* put the circle back */
2956 str[1] = str[2];
2957 str[2] = (char) STR_CIR;
2958
2959 /* If string is unusually short vertically, treat as at least as tall
2960 * as the font's ascent. That way if there are a bunch of
2961 * circled things and one is tiny, like a dot, that circle
2962 * won't be vastly smaller than the others. */
2963 min_height = fontascent(font, size);
2964 if (uncirc_height < min_height) {
2965 adjust = min_height - uncirc_height;
2966 uncirc_height = min_height;
2967 }
2968 else {
2969 adjust = 0.0;
2970 }
2971
2972 /* Allow 25% of the height above and below as space for the circle. */
2973 circ_extra = 0.25 * uncirc_height;
2974 circ_height = 2.0 * circ_extra + uncirc_height;
2975
2976 /* If width is up to 110% of the height, use the circled
2977 * height as the circled width as well. */
2978 if (uncirc_width <= 1.1 * uncirc_height) {
2979 circ_width = circ_height;
2980 }
2981 else {
2982 /* make a little taller to compensate for the width */
2983 circ_extra += circ_height * .03 * (uncirc_width / uncirc_height);
2984 circ_height = 2.0 * circ_extra + uncirc_height;
2985
2986 /* Use 50% of the circled height as the amount to add
2987 * to the width, 25% on each end. */
2988 circ_width = uncirc_width + 0.5 * circ_height;
2989 }
2990 if (height_p != 0) {
2991 *height_p = circ_height;
2992 }
2993 if (width_p != 0) {
2994 *width_p = circ_width;
2995 }
2996 if (x_offset_p != 0) {
2997 *x_offset_p = (circ_width - uncirc_width) / 2.0;
2998 }
2999 if (ascent_adjust != 0) {
3000 *ascent_adjust = adjust;
3001 }
3002
3003 return(circ_extra);
3004}
3005\f
3006
3007/* Return proper version of rehearsal mark string, based on staff number.
3008 * It may be circled, boxed, or plain. If circled or boxed, a new string
3009 * is returned. If plain, the string is returned as is.
3010 */
3011
3012char *
3013get_reh_string(string, staffnum)
3014
3015char *string; /* the plain rehearsal mark string */
3016int staffnum; /* which staff it is for */
3017
3018{
3019 char reh_buffer[100]; /* if not okay as it is, copy is put here */
3020 int style;
3021
3022 style = svpath(staffnum, REHSTYLE)->rehstyle;
3023
3024 if (style == RS_PLAIN) {
3025 return(string);
3026 }
3027
3028 if (strlen(string) + 3 > sizeof(reh_buffer)) {
3029 /* Usually reh marks are very short,
3030 * so if this one is really long, too bad.
3031 */
3032 ufatal("rehearsal mark is too long");
3033 }
3034
3035 (void) sprintf(reh_buffer, "%c%s%c",
3036 style == RS_CIRCLED ? STR_CIR : STR_BOX,
3037 string + 2,
3038 style == RS_CIRCLED ? STR_CIR_END : STR_BOX_END);
3039 return(copy_string(reh_buffer, string[0], string[1]));
3040}
3041\f
3042
3043/* Map STR_MUS_CHAR* to FONT_MUSIC* */
3044
3045int
3046str2mfont(str)
3047
3048int str; /* STR_MUS_CHAR* */
3049
3050{
3051 switch (str) {
3052 case STR_MUS_CHAR:
3053 return(FONT_MUSIC);
3054 case STR_MUS_CHAR2:
3055 return(FONT_MUSIC2);
3056 default:
3057 pfatal("impossible str 0x%x in str2mfont", str);
3058 /*NOTREACHED*/
3059 return(FONT_MUSIC);
3060 }
3061}
3062
3063/* Map FONT_MUSIC* to STR_MUS_CHAR* */
3064
3065int
3066mfont2str(mfont)
3067
3068int mfont; /* FONT_MUSIC* */
3069
3070{
3071 switch (mfont) {
3072 case FONT_MUSIC:
3073 return(STR_MUS_CHAR);
3074 case FONT_MUSIC2:
3075 return(STR_MUS_CHAR2);
3076 default:
3077 pfatal("impossible mfont %d in mfont2str", mfont);
3078 /*NOTREACHED*/
3079 return(STR_MUS_CHAR);
3080 }
3081}