chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / charinfo.c
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 */
24 extern struct SPECCHAR Mus_char_table[NUM_MFONTS][CHARS_IN_FONT+1];
25 #ifdef EXTCHAR
26 extern 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 */
32 struct BACKSPACEINFO {
33         char    code;
34         char    font;
35 };
36
37
38 #ifndef __STDC__
39 extern char *bsearch(); /* binary search library function */
40 #endif
41 extern long strtol();
42
43 /* static functions */
44 static char *get_font P((char *string, int *font_p, int prev_font, char *fname,
45                 int lineno));
46 static char *get_num P((char *string, int *num_p));
47 static int sc_compare P((const void *item1_p, const void *item2_p));
48 #ifdef EXTCHAR
49 static unsigned char ext_name2num P((char *name));
50 #endif
51 static int starts_piled P((char *string, int *font_p, int *size_p,
52                 char **pile_start_p_p));
53 static int str_cmd P((char *str, int *size_p, int *in_pile_p));
54 static int get_accidental P((unsigned char *str, char *accidental_p,
55                 int *acc_size_p, int trans_natural, int *escaped_p));
56 static int add_accidental P((char *buff, int acc_character, int acc_size,
57                 int escaped));
58 static int dim_tri P((unsigned char *str, char *replacement,
59                 int size, int is_chord));
60 static int smallsize P((int size));
61 static int accsize P((int size));
62 \f
63
64 /* return the height (in inches) of a character of specified font and size */
65
66 double
67 height(font, size, ch)
68
69 int font;
70 int size;
71 int 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
90 double
91 width(font, size, ch)
92
93 int font;
94 int size;
95 int 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
114 double
115 ascent(font, size, ch)
116
117 int font;
118 int size;
119 int 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
138 double
139 descent(font, size, ch)
140
141 int font;
142 int size;
143 int 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
188 char *
189 fix_string(string, font, size, fname, lineno)
190
191 char *string;   /* original string */
192 int font;       /* default font for string */
193 int size;       /* default size for string */
194 char *fname;    /* file name, for error messages */
195 int 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
771 static char *
772 get_font(string, font_p, prev_font, fname, lineno)
773
774 char *string;   /* get font from this string */
775 int *font_p;    /* return new font via this pointer */
776 int prev_font;  /* previous font */
777 char *fname;    /* file name for errors */
778 int 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
818 static char *
819 get_num(string, num_p)
820
821 char *string;   /* get number from this string */
822 int *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
841 static int
842 sc_compare(item1_p, item2_p)
843
844 #ifdef __STDC__
845 const void *item1_p;    /* there are really struct SPECCHAR *, but bsearch
846                          * passes them as char * and we have to
847                          * cast appropriately */
848 const void *item2_p;
849 #else
850 char *item1_p;  /* there are really struct SPECCHAR *, but bsearch passes them
851                  * as char * and we have to cast appropriately */
852 char *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
866 unsigned char
867 mc_name2num(name, fname, lineno, size_p, font_p)
868
869 char *name;     /* name for a music character */
870 char *fname;    /* file name for error messages */
871 int lineno;     /* input line number for error messages */
872 int *size_p;    /* points to current size, proper size for music character
873                  * is returned through here */
874 int *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
925 static unsigned char
926 ext_name2num(name)
927
928 char *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
1036 char *
1037 mc_num2name(code, font)
1038
1039 int code;       /* the code for the music character */
1040 int 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
1094 char *
1095 ext_num2name(code)
1096
1097 int 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
1140 int
1141 is_music_symbol(str)
1142
1143 char *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
1176 double
1177 strascent(str)
1178
1179 char *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
1256 double
1257 strdescent(str)
1258
1259 char *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
1342 double
1343 strheight(str)
1344
1345 char *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
1357 double
1358 strwidth(str)
1359 char *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
1479 double
1480 left_width(string)
1481
1482 char *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
1522 static int
1523 starts_piled(string, font_p, size_p, pile_start_p_p)
1524
1525 char *string;
1526 int *font_p;
1527 int *size_p;
1528 char **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
1577 char *
1578 tranchstr(chordstring, staffno)
1579
1580 char *chordstring;      /* untransposed string */
1581 int 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
1726 static int
1727 str_cmd(str, size_p, in_pile_p)
1728
1729 char *str;      /* check string starting here */
1730 int *size_p;
1731 int *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
1778 static int
1779 get_accidental(string, accidental_p, acc_size_p, trans_natural, escaped_p)
1780
1781 unsigned char *string;  /* check this for an accidental */
1782 char *accidental_p;     /* return the accidental here, or \0 if none */
1783 int *acc_size_p;        /* return the accidental size here */
1784 int trans_natural;      /* if YES, translate n to natural, else leave as n */
1785 int *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
1899 static int
1900 add_accidental(buff, acc_character, acc_size, escaped)
1901
1902 char *buff;             /* write into this buffer */
1903 int acc_character;      /* write this accidental */
1904 int acc_size;           /* make accidental this big */
1905 int 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
1983 static int
1984 dim_tri(str_p, replacement, size, is_chord)
1985
1986 unsigned char *str_p;           /* check string at this point */
1987 char *replacement;              /* return the replacement in this buffer,
1988                                  * which needs to be at least 4 bytes long */
1989 int size;                       /* use this size for music character */
1990 int 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
2022 char *
2023 acc_trans(string)
2024
2025 char *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
2093 char *
2094 modify_chstr(string, modifier)
2095
2096 char *string;
2097 int 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
2232 static int
2233 smallsize(size)
2234
2235 int 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
2250 static int
2251 accsize(size)
2252
2253 int 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
2266 int
2267 restchar(basictime)
2268
2269 int 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
2297 int
2298 is_ital_font(font)
2299
2300 int 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
2310 void
2311 end_fontsize(str, font_p, size_p)
2312
2313 char *str;              /* check this string */
2314 int *font_p;            /* return font at end of str via this pointer */
2315 int *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
2338 char *
2339 dashstr(str)
2340
2341 char *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
2385 char *
2386 ascii_str(string, verbose, pagenum, textmod)
2387
2388 char *string;   /* internal format string to convert */
2389 int verbose;    /* If YES, try to reproduce user's original input */
2390 int pagenum;    /* YES (interpolate number for \%) or NO (leave \% as is) */
2391 int 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
2726 char *
2727 split_string(string, desired_width)
2728
2729 char *string;
2730 double 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
2839 int
2840 adj_size(size, scale_factor, filename, lineno)
2841
2842 int size;               /* original point size */
2843 double scale_factor;    /* multiply original size by this factor */
2844 char *filename;         /* filename and lineno are for error messages */
2845 int 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
2865 char *
2866 resize_string(string, scale_factor, filename, lineno)
2867
2868 char *string;           /* this is the string to adjust */
2869 double scale_factor;    /* adjust sizes in string by this factor */
2870 char *filename;         /* for error messages */
2871 int 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
2921 double
2922 circled_dimensions(str, height_p, width_p, ascent_adjust, x_offset_p)
2923
2924 char *str;                      /* a circled string */
2925 float *height_p;                /* if non-zero, return circled height here */
2926 float *width_p;                 /* if non-zero, return circled width here */
2927 float *ascent_adjust;           /* if non-zero, return amount we added to
2928                                  * ascent to bring up to minimum height */
2929 float *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
3012 char *
3013 get_reh_string(string, staffnum)
3014
3015 char *string;   /* the plain rehearsal mark string */
3016 int 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
3045 int
3046 str2mfont(str)
3047
3048 int 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
3065 int
3066 mfont2str(mfont)
3067
3068 int 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 }