chiark / gitweb /
Merge branch 'arkkra' into shiny
[mup] / mup / mup / symtbl.c
CommitLineData
69695f33
MW
1
2/* Copyright (c) 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2006 by Arkkra Enterprises */
3/* All rights reserved */
4
5/* This file contains functions for dealing with symbol tables.
6 * There is a symbol table that maps the names of location variables
7 * to the addresses of their array of coordinate info,
8 * a symbol table to map headshape names to the list of shapes,
9 * a table to map time signatures to beamstyle and/or timeunit values,
10 * and a symbol table to map grid names to definitions of the grids.
11 * Symbol names are hashed for fast lookup.
12 */
13
14#include "defines.h"
15#include "structs.h"
16#include "globals.h"
17
18
19/* shapes for quarter and shorter, half, whole, double whole */
20#define MAX_SHAPE_DURS (4)
21/* UP and DOWN */
22#define MAX_STEM_DIRS (2)
23
24/* First index of headchar and headfont is based on stem direction;
25 * also index of ystem_off. */
26#define STEMINDEX(stemdir) ( (stemdir) == UP ? 0 : 1 )
27/* Second index of headchar and headfont is based on basictime */
28#define HCDUR(basictime) ( (basictime) > 2 ? 0 : 3 - (basictime) )
29/* First index of Nhead_map */
30#define FONTINDEX(font) (font - FONT_MUSIC)
31/* Second index of Nhead_map */
32#define CHARINDEX(ch) (ch - FIRST_CHAR)
33
34struct HDSHAPEINFO {
35 /* We give each headshape instance a unique index number,
36 * as a compact way to refer to it. */
37 short index;
38
39 /* This says what notehead characters to use for this shape.
40 * The first dimension is for stem direction, the second for
41 * duration (quarter and shorter, half, whole, double whole).
42 */
43 char headchar[MAX_STEM_DIRS][MAX_SHAPE_DURS];
44 /* This array parallels the notehead array, saying which music
45 * font the character is in. */
46 char headfont[MAX_STEM_DIRS][MAX_SHAPE_DURS];
47};
48
49/* How many "headshape" entries we allow. This must be small enough to
50 * fit in the number of bits allowed for shape indexes in GRPSYL and NOTE.
51 * Entry 0 isn't used since we need an "unknown" value.
52 */
53#define MAX_SHAPE_ENTRIES (32)
54
55/* These are the predefined headshape entries. Users can define more.
56 * We define them here using the same syntax as user would, so we can
57 * use the same code to add to our internal table. */
58struct SHAPENAMES {
59 char *clan_name; /* name for the set of shapes */
60 char *member_names; /* The names of the 4 shapes in the set */
61} Predef_shape_names[] = {
62 { "norm", "4n 2n 1n dblwhole" },
63 { "x", "xnote diamond diamond dwhdiamond" },
64 { "allx", "xnote xnote xnote xnote" },
65 { "diam", "filldiamond diamond diamond dwhdiamond" },
66 { "blank", "blankhead blankhead blankhead blankhead" },
67 { "righttri", "u?fillrighttriangle u?righttriangle u?righttriangle u?dwhrighttriangle" },
68 { "isostri", "fillisostriangle isostriangle isostriangle dwhisostriangle" },
69 { "rect", "fillrectangle rectangle rectangle dwhrectangle" },
70 { "pie", "fillpiewedge piewedge piewedge dwhpiewedge" },
71 { "semicirc", "fillsemicircle semicircle semicircle dwhsemicircle" },
72 { "allslash", "fillslashhead fillslashhead fillslashhead fillslashhead" },
73 { "slash", "fillslashhead slashhead slashhead dwhslashhead" },
74 { 0, 0 }
75};
76
77/* Information about characters that are allowed to be noteheads */
78struct HEADINFO {
79 char ch; /* code number 32-127 */
80 char font; /* FONT_MUSIC* */
81 float ystem_off[MAX_STEM_DIRS]; /* stepsizes from y to end stem */
82};
83
84/* Predefined note head music characters and their attributes. */
85struct HEADDATA {
86 char *name;
87 struct HEADINFO info;
88} Predef_headinfo[] = {
89 { "dblwhole", { C_DBLWHOLE, FONT_MUSIC, { 0.0, 0.0 } } },
90 { "1n", { C_1N, FONT_MUSIC, { 0.0, 0.0 } } },
91 { "2n", { C_2N, FONT_MUSIC, { 0.25, -0.25 } } },
92 { "4n", { C_4N, FONT_MUSIC, { 0.25, -0.25 } } },
93 { "xnote", { C_XNOTE, FONT_MUSIC, { 1.0, -1.0 } } },
94 { "dwhdiamond", { C_DWHDIAMOND, FONT_MUSIC, { 0.0, 0.0 } } },
95 { "diamond", { C_DIAMOND, FONT_MUSIC, { 0.0, 0.0 } } },
96 { "filldiamond", { C_FILLDIAMOND, FONT_MUSIC, { 0.0, 0.0 } } },
97 { "dwhrighttriangle", { C_DWHRIGHTTRIANGLE, FONT_MUSIC2, { 0.0, 0.0 } } },
98 { "righttriangle", { C_RIGHTTRIANGLE, FONT_MUSIC2, { 0.0, 0.9 } } },
99 { "fillrighttriangle",{ C_FILLRIGHTTRIANGLE, FONT_MUSIC2, { 0.0, 0.9 } } },
100 { "udwhrighttriangle",{ C_UDWHRIGHTTRIANGLE, FONT_MUSIC2, { 0.0, 0.9 } } },
101 { "urighttriangle", { C_URIGHTTRIANGLE, FONT_MUSIC2, { -0.9, 0.0 } } },
102 { "ufillrighttriangle",{ C_UFILLRIGHTTRIANGLE,FONT_MUSIC2, { -0.9, 0.0 } } },
103 { "dwhrectangle", { C_DWHRECTANGLE, FONT_MUSIC2, { -0.9, 0.0 } } },
104 { "rectangle", { C_RECTANGLE, FONT_MUSIC2, { 0.0, 0.0 } } },
105 { "fillrectangle", { C_FILLRECTANGLE, FONT_MUSIC2, { 0.0, 0.0 } } },
106 { "dwhisostriangle", { C_DWHISOSTRIANGLE, FONT_MUSIC2, { -0.8, -0.8 } } },
107 { "isostriangle", { C_ISOSTRIANGLE, FONT_MUSIC2, { -0.8, -0.8 } } },
108 { "fillisostriangle", { C_FILLISOSTRIANGLE, FONT_MUSIC2, { -0.8, -0.8 } } },
109 { "dwhpiewedge", { C_DWHPIEWEDGE, FONT_MUSIC2, { 0.1, 0.2 } } },
110 { "piewedge", { C_PIEWEDGE, FONT_MUSIC2, { 0.1, 0.2 } } },
111 { "fillpiewedge", { C_FILLPIEWEDGE, FONT_MUSIC2, { 0.1, 0.2 } } },
112 { "dwhsemicircle", { C_DWHSEMICIRCLE, FONT_MUSIC2, { 0.8, 0.8 } } },
113 { "semicircle", { C_SEMICIRCLE, FONT_MUSIC2, { 0.8, 0.8 } } },
114 { "fillsemicircle", { C_FILLSEMICIRCLE, FONT_MUSIC2, { 0.8, 0.8 } } },
115 { "blankhead", { C_BLANKHEAD, FONT_MUSIC2, { 0.0, 0.0 } } },
116 { "slashhead", { C_SLASHHEAD, FONT_MUSIC2, { 1.8, -1.8 } } },
117 { "fillslashhead", { C_FILLSLASHHEAD, FONT_MUSIC2, { 1.8, -1.8 } } },
118 { "dwhslashhead", { C_DWHSLASHHEAD, FONT_MUSIC2, { 1.8, -1.8 } } },
119 { 0, { 0, 0, { 0.0, 0.0 } } }
120};
121
122/*
123 * This struct provides a mapping from a time signature to all the
124 * beamstyle and timeunit values to be associated with that time signature.
125 *
126 * The [0][0] entry is used for the C_SCORE value.
127 * The entries [0][1] through [0][MAXVOICES+1] are unused.
128 * The [s][0] entries are used for C_STAFF values where 1 <= s <= MAXSTAFFS
129 * The [s][v] entries are used for C_VOICE values where 1 <= s <= MAXSTAFFS
130 * and 1 <= v <= MAXVOICES
131 * There will be one of these allocated for each time signature used in the
132 * input, if and only if the user also specified at least one beamstyle
133 * or timeunit while that time signature was in effect.
134 */
135struct SSVTABLES {
136 struct SSV *beamstyle_table[MAXSTAFFS+1][MAXVOICES+1];
137 struct SSV *timeunit_table[MAXSTAFFS+1][MAXVOICES+1];
138};
139
140
141/* information about a symbol: its name and current value.
142 * This is used for location tags, chord grids, headshapes, and time signatures.
143 * Note that in the case of location tags,
144 * if the same name is used in a later measure, just the coordlist_p will
145 * change, and when the symbol table is queried for the value of a symbol,
146 * it will get the current value for that symbol */
147struct Sym {
148 char *symname;
149 union {
150 float *coordlist_p; /* when used for location tags, this is
151 * where its AX, RX, etc values are */
152 struct GRID *grid_p; /* when used for grids */
153 struct HDSHAPEINFO *shapeinfo_p;/* when used for headshapes */
154 struct HEADINFO *noteinfo_p; /* when used for note head info */
155 /* Info about beamstyles and/or timeunits associated with
156 * the time signature given by the symname. */
157 struct SSVTABLES *ssvtables_p;
158 } val;
159 struct Sym *next; /* for collision chain off hash table */
160};
161
162/* symbol hash table size-- if this changes, hash() has to change accordingly */
163#define SYMTBLSIZE (128)
164
165/* this is the symbol table for location tags */
166static struct Sym *Tag_table[SYMTBLSIZE];
167
168/* this is the symbol table for guitar grids. It is malloc-ed at runtime
169 * only if needed */
170static struct Sym **Grid_table;
171
172/* This is the symbol table for headshapes */
173static struct Sym *Shape_table[SYMTBLSIZE];
174
175/* This maps headshape indexes to the corresponding info.
176 * Element 0 is unused, since index 0 means "unknown" shape.
177 */
178static struct Sym *Shape_map[MAX_SHAPE_ENTRIES];
179/* How many Shape_map entries are actually used */
180static short Shape_entries = 0;
181
182/* This is the symbol table for noteheads, to get stem offsets */
183static struct Sym *Nhead_table[SYMTBLSIZE];
184
185/* This maps notehead character codes to the stem offset info */
186static struct HEADINFO *Nhead_map[NUM_MFONTS][CHARS_IN_FONT];
187
188/* This is used to remember what beamstyle and/or timeunit values were
189 * associated with time signatures. This is really only needed during parse,
190 * and then only if user specifies beamstyle or timeunit somewhere.
191 */
192static struct Sym *Time_map[SYMTBLSIZE];
193
194/* Internal name for tag used to store the virtual _win coords for blocks */
195char Blockwin[] = "~blockwin";
196/* This points to where in the symbol table
197 * to save the pointer to the coord array for blocks.
198 * Having this pointer is a speed optimization,
199 * to save us from having to look it up every time. */
200float **Blockcoord_p_p;
201
202
203/* static functions */
204static struct GRID *parse_grid P((char *griddef));
205static struct Sym *add2tbl P((char *symname, struct Sym **table));
206static struct Sym *findSym P((char *symname, struct Sym **table));
207static int hash P((char *string));
208static int coordhash P((float *key));
209static void rep_ref P((float **old_ref_p_p, float **new_ref_p_p));
210static void delete_coord P((float *coord_p));
211static int is_valid_notehead P((int ch, int font));
212static void add_head P((char *name, struct HEADINFO *info_p));
213
214
215/* size of Coordinfo hash table. Should be prime */
216#define COORDTBLSIZE (271)
217
218static struct COORD_INFO *Coord_table[COORDTBLSIZE];
219\f
220
221/* Add predefined values to the symbol tables */
222
223void
224init_symtbl()
225
226{
227 struct Sym *sym_p;
228 int i;
229
230 addsym("_page", _Page, CT_BUILTIN);
231 addsym("_cur", _Cur, CT_BUILTIN);
232
233 /* Blocks each have their own virtual _win,
234 * so we put a placeholder in the symbol table
235 * and save a pointer to its tag's coord pointer.
236 * Then we can update the coords via set_win_coords().
237 */
238 addsym(Blockwin, 0, CT_BUILTIN);
239 sym_p = findSym(Blockwin, Tag_table);
240 if (sym_p == 0) {
241 pfatal("couldn't find %s coord right after inserting it!",
242 Blockwin);
243 }
244 Blockcoord_p_p = &(sym_p->val.coordlist_p);
245
246 /* Put the predefined notehead shapes into table. */
247 for (i = 0; Predef_headinfo[i].name != 0; i++) {
248 add_head(Predef_headinfo[i].name, &(Predef_headinfo[i].info));
249 }
250
251 /* Put the predefined head shapes in the shapes table */
252 for (i = 0; Predef_shape_names[i].clan_name != 0; i++) {
253 add_shape(Predef_shape_names[i].clan_name,
254 Predef_shape_names[i].member_names);
255 }
256}
257\f
258
259/* add a symbol to the table if not already there and fill in its coordlist_p
260 * in symbol table. */
261
262void
263addsym(symname, coordlist_p, coordtype)
264
265char *symname; /* what to add to table */
266float *coordlist_p; /* set of 13 coordinates associated with symbol */
267int coordtype; /* CT_BAR, CT_GRPSYL, etc */
268
269{
270 struct Sym *sym_p; /* pointer to info about symbol in hash tbl */
271
272
273 debug(4, "addsym(symname=%s coordlist_p=0x%lx, coordtype=%d)",
274 symname, coordlist_p, coordtype);
275
276 /* find in symbol table or add if not yet there */
277 sym_p = add2tbl(symname, Tag_table);
278
279 /* fill in coordlist pointer */
280 sym_p->val.coordlist_p = coordlist_p;
281
282 /* put entry in coord table */
283 add_coord(coordlist_p, coordtype);
284}
285\f
286
287/* add a symbol to the specified hash table */
288
289static struct Sym *
290add2tbl(symname, table)
291
292char *symname; /* what to add */
293struct Sym **table; /* which table to add to */
294
295{
296 struct Sym *sym_p;
297 int h; /* hash number of symbol */
298
299 if ((sym_p = findSym(symname, table)) == (struct Sym *) 0) {
300
301 /* not in list before. Add it */
302 MALLOC(Sym, sym_p, 1);
303 MALLOCA(char, sym_p->symname, strlen(symname) + 1);
304 (void) strcpy(sym_p->symname, symname);
305 h = hash(symname);
306
307 /* link onto front of list off hash table */
308 sym_p->next = table[h];
309 table[h] = sym_p;
310 }
311 return(sym_p);
312}
313\f
314
315/* given a symbol name, return pointer to its symbol table entry, or NULL
316 * if none */
317
318static struct Sym *
319findSym(symname, table)
320
321char *symname; /* which symbol to look for */
322struct Sym **table; /* which table to look in */
323
324{
325 struct Sym *sym_p; /* symbol info currently being checked
326 * for match with symname */
327 int h; /* hash number */
328
329
330 h = hash(symname);
331 /* go down the linked list (of hash collisions) off the table
332 * searching for match */
333 for (sym_p = table[h]; sym_p != (struct Sym *) 0;
334 sym_p = sym_p->next) {
335 if (strcmp(sym_p->symname, symname) == 0) {
336 return(sym_p);
337 }
338 }
339 return((struct Sym *) 0);
340}
341\f
342
343/* For top/bot/top2/bot2/block we temporarily set the ~blockwin tag to point
344 * to the appropriate blockhead's coord array. Before doing a set_win for
345 * any of those, this function should be called to set things up, and
346 * it should be called again afterwards with 0 to mark we are no longer
347 * inside a block.
348 */
349
350void
351set_win_coord(coord_p)
352
353float *coord_p;
354
355{
356 *Blockcoord_p_p = coord_p;
357}
358\f
359
360/* Given a tag name, return its value (a pointer to the coordinate array
361 * containing the AX, AY, etc of the variable).
362 * If the tag is not found, an error is printed and 0 is returned.
363 * If the ref_p_p is non-null,
364 * save that value as something that references the tag. That way if the
365 * tag gives moved somewhere else, we can update all the references.
366 * If referencing the value of a coord that can never move (a builtin coord
367 * like _win), ref_p_p may be null.
368 */
369
370float *
371symval(symname, ref_p_p)
372
373char *symname; /* which symbol to look up */
374float **ref_p_p; /* address of reference to the tag */
375
376{
377 struct Sym *sym_p; /* symbol info currently being checked */
378
379
380 /* _win is a special case: its actual symbol depends on context */
381 if (strcmp(symname, "_win") == 0) {
382 /* If we're inside a block, use its coord,
383 * else use the global _Win */
384 if (*Blockcoord_p_p != 0) {
385 return(*Blockcoord_p_p);
386 }
387 else {
388 return(_Win);
389 }
390 }
391
392 /* find the symbol table entry */
393 if ((sym_p = findSym(symname, Tag_table)) != (struct Sym *) 0) {
394 if (sym_p->val.coordlist_p != (float *) 0) {
395 /* save reference information */
396 if (ref_p_p != 0) {
397 struct COORD_INFO *coordinfo_p;
398 struct COORD_REF *ref_p;
399 CALLOC(COORD_REF, ref_p, 1);
400 ref_p->ref_p_p = ref_p_p;
401 /* link onto reference list */
402 coordinfo_p = find_coord(sym_p->val.coordlist_p);
403 ref_p->next = coordinfo_p->ref_list_p;
404 coordinfo_p->ref_list_p = ref_p;
405 }
406 return(sym_p->val.coordlist_p);
407 }
408 }
409
410 /* whoops! not in table */
411 l_yyerror(Curr_filename, yylineno,
412 "reference to uninitialized location tag '%s'",
413 symname);
414 return((float *) 0);
415}
416\f
417
418/* add item to grid table if not already there. */
419
420void
421add_grid(name, griddef)
422
423char *name; /* chord name */
424char *griddef; /* user's definition of the grid */
425
426{
427 char *internal_name; /* internal format name */
428 char *grid_name; /* permanent copy of internal_name */
429 char *symname; /* ASCII-ized name */
430 char *key; /* permanent copy of symname, key into hash tbl */
431 struct Sym *sym_p;
432 struct GRID *grid_p;
433
434
435 /* if table doesn't exist yet, create it */
436 if (Grid_table == 0) {
437 int g;
438
439 MALLOCA(struct Sym *, Grid_table, SYMTBLSIZE);
440 for (g = 0; g < SYMTBLSIZE; g++) {
441 Grid_table[g] = 0;
442 }
443 }
444
445 /* Do all the transforms to get into internal form with
446 * accidentals as music characters. For historical reasons,
447 * this has to be done in several steps, in just the right order. */
448 internal_name = modify_chstr(name, TM_CHORD);
449 internal_name = fix_string(internal_name, Score.font, Score.size,
450 Curr_filename, yylineno);
451 /* convert accidentals, then convert to all ASCII */
452 grid_name = tranchstr(internal_name, -1);
453 symname = ascii_str(grid_name, YES, NO, TM_CHORD);
454
455 if (strlen(symname) == 0) {
456 yyerror("empty grid name not allowed");
457 return;
458 }
459
460 if ((sym_p = findSym(symname, Grid_table)) != 0) {
461 l_warning(Curr_filename, yylineno,
462 "duplicate definition of grid for '%s', discarding previous",
463 symname);
464 /* discard old definition, use new one */
465 if (sym_p->val.grid_p != 0) {
466 if (sym_p->val.grid_p->name != 0) {
467 FREE(sym_p->val.grid_p->name);
468 }
469 FREE(sym_p->val.grid_p);
470 sym_p->val.grid_p = 0;
471 }
472 key = sym_p->symname;
473 }
474 else {
475 /* make permanent copy of key */
476 MALLOCA(char, key, strlen(symname) + 1);
477 (void) strcpy(key, symname);
478 }
479
480 if ((grid_p = parse_grid(griddef)) != 0) {
481 /* it's good, so put in hash table */
482 sym_p = add2tbl(key, Grid_table);
483
484 /* fill in the name and grid pointer */
485 grid_p->name = grid_name;
486 sym_p->val.grid_p = grid_p;;
487 {
488 /* buffer--2 digits and a space for each string, plus null */
489 char fretlist[3 * MAXTABLINES + 1];
490 int f;
491 for (f = 0; f < grid_p->numstr; f++) {
492 (void) sprintf(fretlist + 3 * f, "%3d",
493 grid_p->positions[f]);
494 }
495 fretlist[sizeof(fretlist) - 1] = '\0';
496 debug(4, "added grid '%s' key '%s' with %d strings: %s curve %d to %d",
497 name, key,
498 grid_p->numstr, fretlist,
499 grid_p->curvel, grid_p->curver);
500 }
501 }
502}
503\f
504
505/* take the user's grid definition, like "2 (3 1) o x -"
506 * and populate a GRID struct with the info. If not parse-able,
507 * return 0 */
508
509static struct GRID *
510parse_grid(griddef)
511
512char *griddef; /* user's definition of the grid */
513
514{
515 struct GRID *grid_p; /* the malloc-ed grid to populate & return */
516 int error = NO;
517 int value;
518
519 MALLOC(GRID, grid_p, 1);
520 grid_p->numstr = 0;
521 grid_p->curvel = grid_p->curver = 0;
522 grid_p->used = NO;
523 /* the +2 is to skip the font/size bytes */
524 for (griddef += 2; *griddef != '\0' && error == NO; griddef++) {
525 /* init to something illegal */
526 value = -1000;
527
528 while (*griddef == ' ' || *griddef == '\t') {
529 griddef++;
530 }
531 if (*griddef == '\0') {
532 break;
533 }
534
535 if ( isdigit(*griddef) ) {
536 value = *griddef - '0';
537 if (isdigit(*(griddef+1))) {
538 value *= 10;
539 griddef++;
540 value += *griddef - '0';
541 }
542 if (value == 0) {
543 yyerror("fret of zero not allowed; use 'o' for open or '-' for nothing");
544 error = YES;
545 }
546 }
547 else if (*griddef == '(') {
548 if (grid_p->curvel != 0) {
549 yyerror("only one '(' allowed in grid definition");
550 error = YES;
551 }
552 else {
553 grid_p->curvel = grid_p->numstr + 1;
554 }
555 }
556 else if (*griddef == ')') {
557 if (grid_p->curver != 0) {
558 yyerror("only one ')' allowed in grid definition");
559 error = YES;
560 }
561 else if (grid_p->curvel == 0) {
562 yyerror("missing '(' in grid definition");
563 error = YES;
564 }
565 else if (grid_p->curvel == grid_p->numstr) {
566 yyerror("curve in grid definition must encompass more than one string");
567 error = YES;
568 }
569 else {
570 grid_p->curver = grid_p->numstr;
571 }
572 }
573 else if (*griddef == 'o') {
574 value = 0;
575 }
576 else if (*griddef == 'x') {
577 value = -1;
578 }
579 else if (*griddef == '-') {
580 value = -2;
581 }
582 else {
583 yyerror("invalid grid specification");
584 FREE(grid_p);
585 return (0);
586 }
587
588 if (value >= -2) {
589 /* We found a fret value (not parentheses).
590 * Next better be white space sort of thing */
591 char c;
592 c = *(griddef + 1);
593 if (c != ' ' && c != '\t' && c != '(' && c != ')'
594 && c != '\0') {
595 yyerror("missing white space in grid specification");
596 error = YES;
597 }
598 else if (grid_p->numstr < MAXTABLINES) {
599 /* all is well; save info for current string */
600 grid_p->positions[grid_p->numstr] = value;
601 (grid_p->numstr)++;
602 }
603 else {
604 yyerror("too many strings in grid specification");
605 error = YES;
606 }
607 }
608 }
609
610 if (error == NO && grid_p->curvel != 0 && grid_p->curver == 0) {
611 yyerror("missing ')' in grid specification");
612 error = YES;
613 }
614
615 if (grid_p->numstr < 1) {
616 yyerror("grid must include at least one string");
617 error = YES;
618 }
619
620 /* every curve must have at least one real fret in it */
621 if (grid_p->curvel != 0) {
622 int s;
623 /* the -1 is because curves start at 1, but positions at 0 */
624 for (s = grid_p->curvel - 1; s <= grid_p->curver - 1; s++) {
625 if (grid_p->positions[s] > 0) {
626 break;
627 }
628 }
629 if (s == grid_p->curver) {
630 yyerror("grid curve must include at least one fret number, not just x, o, and -");
631 error = YES;
632 }
633 }
634
635 if (error == YES) {
636 /* clean up */
637 FREE(grid_p);
638 grid_p = 0;
639 }
640 return(grid_p);
641}
642\f
643
644/* Locate a named GRID in the grid hash table. Returns 0 if not found,
645 * else pointer to desired GRID. */
646
647struct GRID *
648findgrid(name)
649
650char *name; /* find GRID with this chord name */
651
652{
653 struct Sym *sym_p;
654 char *ascii_name;
655 int length;
656 int first_char;
657
658 if (Grid_table == 0) {
659 /* no grids defined */
660 return((struct GRID *) 0);
661 }
662
663 ascii_name = ascii_str(name, YES, NO, TM_CHORD);
664 /* Chord names in STUFF have a space padding at the end of them
665 * unless they are in a box or circle, so strip that off for matching
666 * the name. We store the grid name without the space, because for
667 * grid printing, we want the name centered without end padding. */
668 length = strlen(ascii_name);
669 first_char = ((int)*(name+2)) & 0xff;
670 if (first_char != STR_BOX && first_char != STR_CIR
671 && ascii_name[length-1] == ' ') {
672 ascii_name[length-1] = '\0';
673 }
674
675 /* look it up */
676 sym_p = findSym(ascii_name, Grid_table);
677
678 return (sym_p ? sym_p->val.grid_p : 0);
679}
680\f
681
682/* Function to iterate through all the GRIDs. First time, call it with
683 * argument of zero, and it returns the first GRID it finds. To walk
684 * through the list, call it repeatedly, each time passing the GRID
685 * you got the last time. When you get back a zero, the list is done.
686 * Restrictions: you must either call with zero or the last one you got.
687 * You can't remember some previous value and use that to try to jump
688 * to elsewhere on the list, or have multiple walks going on at once.
689 * Caller should assume values are returned in arbitrary order.
690 */
691
692struct GRID *
693nextgrid(grid_p)
694
695struct GRID *grid_p;
696
697{
698 static int tbl_index = -1;
699 static struct Sym *last_sym_p = 0; /* remember where we were the
700 * last time we were called */
701
702 if (grid_p == 0) {
703 /* starting at beginning */
704 tbl_index = -1;
705 last_sym_p = 0;
706 }
707 else if (last_sym_p == 0 || grid_p != last_sym_p->val.grid_p) {
708 pfatal("nextgrid called incorrectly");
709 }
710
711 /* if there is another on the current collision chain, use that */
712 if (last_sym_p != 0) {
713 last_sym_p = last_sym_p->next;
714 }
715
716 /* if none, either if just starting, or if ran off the end
717 * of a collision chain, find next chain. */
718 if (last_sym_p == 0) {
719 for (tbl_index++; tbl_index < SYMTBLSIZE; tbl_index++) {
720 if (Grid_table[tbl_index] != 0) {
721 /* found a populated chain */
722 last_sym_p = Grid_table[tbl_index];
723 break;
724 }
725 }
726 }
727
728 return(last_sym_p == 0 ? 0 : last_sym_p->val.grid_p);
729}
730\f
731
732/* return a hash number from a string. XOR the bytes together */
733
734static int
735hash(string)
736
737char *string; /* hash this string */
738
739{
740 int h; /* hash number */
741
742 for (h = 0; *string != '\0'; string++) {
743 h ^= *string;
744 }
745 /* return hash number between 0 and 127 */
746 return(h & 0x7f);
747}
748\f
749
750/* add entry to COORD_INFO table */
751
752void
753add_coord(coordlist_p, coordtype)
754
755float *coordlist_p; /* address of set of 13 coordinates to add to tbl */
756int coordtype; /* CT_GRPSYL, etc */
757
758{
759 struct COORD_INFO *new_p; /* space for saving coord info */
760 int h; /* hash number */
761
762
763 /* if not already in table, add it */
764 if (find_coord(coordlist_p) == (struct COORD_INFO *) 0) {
765
766 /* get space, fill in coord type, and link into hash table */
767 CALLOC(COORD_INFO, new_p, 1);
768
769 new_p->coordlist_p = coordlist_p;
770 new_p->flags = (short) coordtype;
771
772 h = coordhash(coordlist_p);
773 new_p->next = Coord_table[h];
774 Coord_table[h] = new_p;
775 }
776}
777\f
778
779/* Given an address of an array of floats (a coordinate array), return a hash
780 * number, which is modulo of the Coord_table table size */
781
782static int
783coordhash(key)
784
785float *key; /* hash this number */
786
787{
788 return ((int) ( (unsigned long) key % COORDTBLSIZE));
789}
790\f
791
792/* Given a coordinate (pointer to array of floats), return the location of
793 * the info about it in the Coord_table, or 0 if not in table */
794
795struct COORD_INFO *
796find_coord(key)
797
798float *key; /* look up this key in hash table */
799
800{
801 int h; /* hash number */
802 struct COORD_INFO *c_p;
803
804
805 /* search hash table for matching entry */
806 h = coordhash(key);
807 for (c_p = Coord_table[h]; c_p != (struct COORD_INFO *) 0;
808 c_p = c_p->next) {
809 if (key == c_p->coordlist_p) {
810 return(c_p);
811 }
812 }
813 return( (struct COORD_INFO *) 0);
814}
815\f
816
817/* Given an existing INPCOORD, and a new INPCOORD that is to replace it,
818 * adjust any tag references (hor_p or vert_p) to point to the new one. */
819
820void
821rep_inpcoord(old_inpcoord_p, new_inpcoord_p)
822
823struct INPCOORD *old_inpcoord_p;
824struct INPCOORD *new_inpcoord_p;
825
826{
827 rep_ref( &(old_inpcoord_p->hor_p), &(new_inpcoord_p->hor_p) );
828 rep_ref( &(old_inpcoord_p->vert_p), &(new_inpcoord_p->vert_p) );
829
830}
831
832/* Given an existing reference to a tag in an INPCOORD, replace that
833 * reference with the new reference. This is for when transferring a
834 * temporary INPCOORD to a permanent one.
835 */
836
837static void
838rep_ref(old_ref_p_p, new_ref_p_p)
839
840float **old_ref_p_p;
841float **new_ref_p_p;
842
843{
844 struct COORD_INFO *coordinfo_p;
845 struct COORD_REF *ref_p;
846
847 if (*old_ref_p_p == 0) {
848 /* This can happen if user references uninitialized tag.
849 * We already give error message elsewhere for that. */
850 return;
851 }
852 if (*new_ref_p_p == 0) {
853 pfatal("attempt to replace tag reference with null.");
854 return;
855 }
856
857 /* Find this information about the coordinate */
858 if ((coordinfo_p = find_coord(*old_ref_p_p)) == 0) {
859 /* Must have been earlier user error. */
860 return;
861 }
862
863 /* Find any references matching the old and update them.
864 * Really should be only one, but seems safer to check all.
865 */
866 for (ref_p = coordinfo_p->ref_list_p; ref_p != 0; ref_p = ref_p->next) {
867 if (ref_p->ref_p_p == old_ref_p_p) {
868 ref_p->ref_p_p = new_ref_p_p;
869 }
870 }
871}
872\f
873
874/* Given an existing coord array address and an address it is being moved to,
875 * update all references to the old to point to the new, and update our
876 * hash table to insert the old and delete the old.
877 */
878
879void
880upd_ref(oldcoord_p, newcoord_p)
881
882float * oldcoord_p;
883float * newcoord_p;
884
885{
886 struct COORD_INFO *coordinfo_p; /* existing info about oldcoord_p */
887 struct COORD_INFO *newcoordinfo_p; /* for info about newcoord_p */
888 struct COORD_REF *ref_p; /* for walking through ref list */
889
890 if ((coordinfo_p = find_coord(oldcoord_p)) == 0) {
891 /* apparently no tags associated with this coord array */
892 return;
893 }
894 /* update all the references to the tag with the new value */
895 for (ref_p = coordinfo_p->ref_list_p; ref_p != 0; ref_p = ref_p->next) {
896 *(ref_p->ref_p_p) = newcoord_p;
897 }
898
899 /* Now need to create new entry for the new coord, and delete old */
900 add_coord(newcoord_p, coordinfo_p->flags);
901 /* Append reference list to new coord info */
902 if ((newcoordinfo_p = find_coord(newcoord_p)) != 0) {
903 /* Should always get here; the 'if' is to just paranoia
904 * to be sure to avoid null pointer deference. */
905 /* If coord already existed before, because one coord is
906 * being combined with another, we want to concatenate the
907 * reference list, so find end to append to. */
908 struct COORD_REF **append_p_p;
909 for (append_p_p = &(newcoordinfo_p->ref_list_p);
910 *append_p_p != 0;
911 append_p_p = &((*append_p_p)->next)) {
912 ;
913 }
914
915 *append_p_p = coordinfo_p->ref_list_p;
916 coordinfo_p->ref_list_p = 0;
917 }
918 delete_coord(oldcoord_p);
919}
920\f
921
922/* Delete the given coordinate information */
923
924static void
925delete_coord(coord_p)
926
927float * coord_p;
928
929{
930 int h; /* hash number */
931 struct COORD_INFO **c_p_p;
932
933 /* search hash table for matching entry and delete it */
934 h = coordhash(coord_p);
935 for (c_p_p = &(Coord_table[h]); *c_p_p != 0; *(c_p_p) = (*c_p_p)->next) {
936 if ((*c_p_p)->coordlist_p == coord_p) {
937 struct COORD_INFO * to_delete_p;
938 to_delete_p = *c_p_p;
939 *c_p_p = (*c_p_p)->next;
940 FREE(to_delete_p);
941 return;
942 }
943 }
944}
945\f
946
947/* Given a shape name and string containing the 4 note heads to use for it,
948 * as would exist in a line of "headshapes" context, parse the
949 * string with the noteheads and save all the information
950 * in the shapes hash table.
951 */
952
953void
954add_shape(name, shapes)
955
956char *name; /* name of the list of shapes */
957char *shapes; /* list of 4 shape names */
958
959{
960 struct Sym *sym_p; /* where added into Shape_table */
961 struct HDSHAPEINFO *shapeinfo_p;/* internal format for shape data */
962 int i; /* index through shapes */
963 int d; /* index through durations */
964 int nameleng; /* length of one name in shapes list */
965 char namebuff[40]; /* one of the names in shapes list.
966 * Needs to be big enough to hold longer
967 * name of any valid note head character. */
968 char ch; /* music character corresponding to head name */
969 int font; /* which font FONT_MUSIC* */
970 int size; /* not really needed here, but function we
971 * call expect it */
972 short flips; /* YES if stem down uses upside down version */
973
974 debug(4, "add_shape name='%s' shapes='%s'", name, shapes);
975
976 /* Add to symbol table */
977 sym_p = add2tbl(name, Shape_table);
978 MALLOC(HDSHAPEINFO, shapeinfo_p, 1);
979 sym_p->val.shapeinfo_p = shapeinfo_p;
980
981 /* Allocate an index and fill in the index-to-info mapping array */
982 shapeinfo_p->index = ++Shape_entries;
983 if (Shape_entries >= MAX_SHAPE_ENTRIES) {
984 yyerror("Too many headshapes");
985 return;
986 }
987 else {
988 Shape_map[Shape_entries] = sym_p;
989 }
990
991 /* Parse the list of 4 shapes and save their info */
992 size = DFLT_SIZE;
993 for (d = i = 0; shapes[i] != '\0'; ) {
994 /* Skip white space */
995 if (isspace(shapes[i])) {
996 i++;
997 continue;
998 }
999
1000 /* Make sure user didn't give too many shapes */
1001 if (d >= MAX_SHAPE_DURS) {
1002 l_yyerror(Curr_filename, yylineno,
1003 "Too many shapes for headshape '%s' (%d expected, %d found)\n",
1004 name, MAX_SHAPE_DURS, d);
1005 return;
1006 }
1007
1008 /* Check if stem down gets a flipped character */
1009 if (shapes[i] == 'u' && shapes[i+1] == '?') {
1010 flips = YES;
1011 i += 2;
1012 }
1013 else {
1014 flips = NO;
1015 }
1016
1017 /* get copy of current head name, and look up its character */
1018 nameleng = strcspn(shapes + i, " \t\r\n");
1019 /* leave room for null and 'u' for upsidedown */
1020 if (nameleng > sizeof(namebuff) - 2) {
1021 ufatal("head shape name too long");
1022 }
1023 if (nameleng == 0 && flips == YES) {
1024 l_yyerror(Curr_filename, yylineno,
1025 "'u?' must be followed immediately by a note head character name");
1026 return;
1027 }
1028 strncpy(namebuff, shapes + i, nameleng);
1029 namebuff[nameleng] = '\0';
1030 ch = mc_name2num(namebuff, Curr_filename, yylineno, &size, &font);
1031 if (is_valid_notehead(ch, font) == NO) {
1032 l_yyerror(Curr_filename, yylineno,
1033 "'%s' is not a valid note head name", namebuff);
1034 return;
1035 }
1036
1037 shapeinfo_p->headchar[STEMINDEX(UP)][d] = ch;
1038 shapeinfo_p->headfont[STEMINDEX(UP)][d] = font;
1039
1040 /* If flips, get upside down version. Else use same again */
1041 if (flips == YES) {
1042 namebuff[0] = 'u';
1043 strncpy(namebuff + 1, shapes + i, nameleng);
1044 namebuff[nameleng+1] = '\0';
1045 ch = mc_name2num(namebuff, Curr_filename, yylineno, &size, &font);
1046 if (is_valid_notehead(ch, font) == NO) {
1047 l_yyerror(Curr_filename, yylineno,
1048 "'%s' is not a valid note head name", namebuff);
1049 return;
1050 }
1051 }
1052 shapeinfo_p->headchar[STEMINDEX(DOWN)][d] = ch;
1053 shapeinfo_p->headfont[STEMINDEX(DOWN)][d] = font;
1054
1055 /* Prepare for next in the list, if any */
1056 d++;
1057 i += nameleng;
1058 }
1059 if (d < MAX_SHAPE_DURS) {
1060 l_yyerror(Curr_filename, yylineno,
1061 "Too few shapes for headshape '%s' (%d expected, %d found)\n",
1062 name, MAX_SHAPE_DURS, d);
1063 }
1064}
1065\f
1066
1067/* Given a head shape index, stemdir, and basictime, return the notehead
1068 * character to use and (via font_p pointer)
1069 * which music font that notehead character is in.
1070 */
1071
1072int
1073nheadchar(headshape, basictime, stemdir, font_p)
1074
1075int headshape; /* head shape index */
1076int basictime; /* 8 for eighth, 2 for half, etc */
1077int stemdir; /* UP or DOWN */
1078int *font_p; /* FONT_MUSIC* is returned here */
1079
1080{
1081 struct Sym *info_p; /* shape to character map */
1082 int dir; /* first index into headchar */
1083 int dur; /* second index into headchar */
1084
1085 if (headshape == HS_UNKNOWN || headshape > Shape_entries) {
1086 pfatal("illegal headshape index to nheadchar (%d)", headshape);
1087 }
1088
1089 info_p = Shape_map[headshape];
1090
1091 dir = STEMINDEX(stemdir);
1092 dur = HCDUR(basictime);
1093 *font_p = info_p->val.shapeinfo_p->headfont[dir][dur];
1094 return(info_p->val.shapeinfo_p->headchar[dir][dur]);
1095}
1096\f
1097
1098/* Given a head shape name, return the internal index number we use
1099 * to refer to that shape.
1100 */
1101
1102int
1103get_shape_num(shapename)
1104
1105char *shapename;
1106
1107{
1108 struct Sym *sym_p;
1109
1110 if ((sym_p = findSym(shapename, Shape_table)) != 0) {
1111 return(sym_p->val.shapeinfo_p->index);
1112 }
1113 return(HS_UNKNOWN);
1114}
1115\f
1116
1117/* Return YES if given character is a valid note head character, else NO */
1118
1119static int
1120is_valid_notehead(ch, font)
1121
1122int ch; /* character code */
1123int font; /* FONT_MUSIC* */
1124
1125{
1126 if ( IS_MUSIC_FONT(font) == NO || ch < 0 || ch >= CHARS_IN_FONT) {
1127 /* If caller passes us the return from mc_name2num(),
1128 * that could return BAD_CHAR, so we don't pfatal here,
1129 * but do return right away, so we don't do an illegal
1130 * array index below.
1131 */
1132 return(NO);
1133 }
1134 return (Nhead_map[FONTINDEX(font)][CHARINDEX(ch)] == 0 ? NO : YES);
1135}
1136\f
1137
1138/* Save note head information in table */
1139/** If we allow users to supply their own some day,
1140 * would need a function that allocates and fills in a HEADINFO,
1141 * and then calls this one. */
1142
1143static void
1144add_head(name, info_p)
1145
1146char *name; /* music character name */
1147struct HEADINFO *info_p; /* char/font/stem offset */
1148
1149{
1150 struct Sym *sym_p;
1151
1152
1153 sym_p = add2tbl(name, Nhead_table);
1154 sym_p->val.noteinfo_p = info_p;
1155
1156 /* Fill in reverse lookup map */
1157 Nhead_map[FONTINDEX(info_p->font)][CHARINDEX(info_p->ch)] = info_p;
1158}
1159\f
1160
1161/* Given a note head character and stem direction,
1162 * return the y offset for the stem to end, in stepsizes.
1163 */
1164
1165double
1166stem_yoff(headch, font, stemdir)
1167
1168int headch; /* music character code */
1169int font; /* FONT_MUSIC* */
1170int stemdir; /* UP or DOWN */
1171
1172{
1173 struct HEADINFO *info_p;
1174
1175 if ( IS_MUSIC_FONT(font) == NO || headch < 0 || headch >= CHARS_IN_FONT) {
1176 pfatal("invalid argument: stem_yoffset(%d, %d, stemdir)",
1177 headch, font, stemdir);
1178 }
1179 info_p = Nhead_map[FONTINDEX(font)][CHARINDEX(headch)];
1180 if (info_p == 0) {
1181 pfatal("No notehead map for ch=%d, font=%d", headch, font);
1182 }
1183 if (stemdir == UNKNOWN) {
1184 pfatal("stem_yoff called with unknown stemdir");
1185 }
1186 return(info_p->ystem_off[STEMINDEX(stemdir)]);
1187}
1188\f
1189
1190/* This should be called when an SSV has been collected by the user.
1191 * If the user set beamstyle and/or timeunit in the SSV,
1192 * create a mapping between the current time signature and that beamstyle
1193 * and/or timeunit, so that if the user later sets the same time signature,
1194 * they don't have to set the other things too.
1195 * If they set time signature, see if we have a mapping
1196 * for that time signature. If so, set the beamstyles and timeunits
1197 * from that mapping.
1198 */
1199
1200void
1201remember_tsig_params(mll_p)
1202
1203struct MAINLL *mll_p; /* contains SSV */
1204
1205{
1206 struct SSV *ssv_p; /* the SSV to process */
1207 char *timesig; /* current time signature representation */
1208 struct Sym *entry; /* entry in time sig info mapping table */
1209
1210
1211 if (mll_p->str != S_SSV) {
1212 pfatal("remember_tsig_params got bad str value %d", mll_p->str);
1213 }
1214 ssv_p = mll_p->u.ssv_p;
1215
1216 if (ssv_p->used[TIME] == NO && ssv_p->used[BEAMSTLIST] == NO &&
1217 ssv_p->used[TIMEUNIT] == NO) {
1218 /* nothing of interest in this SSV */
1219 return;
1220 }
1221
1222 /* If user set time signature in this SSV, that's the time sig
1223 * of interest, otherwise use the current time signature */
1224 timesig = (ssv_p->used[TIME] == YES ? ssv_p->timerep : Score.timerep);
1225 if ((entry = findSym(timesig, Time_map)) == 0) {
1226 entry = add2tbl(timesig, Time_map);
1227 /* We'll only allocate the actual table if user gives a
1228 * beamstyle or timeunit somewhere. If they never do,
1229 * this will avoid wasting memory.
1230 */
1231 entry->val.ssvtables_p = 0;
1232 }
1233
1234 /* If beamstyle or timeunit are set in this SSV, associate them
1235 * with the current time signature. */
1236 if (ssv_p->used[BEAMSTLIST] == YES) {
1237 if (entry->val.ssvtables_p == 0) {
1238 CALLOC(SSVTABLES, entry->val.ssvtables_p, 1);
1239 }
1240 /* Note that when staffno and voiceno are zero,
1241 * that's actually a score entry, and when voiceno is zero,
1242 * but staffno is non-zero, that is actually a staff entry. */
1243 entry->val.ssvtables_p->beamstyle_table[ssv_p->staffno][ssv_p->voiceno] = ssv_p;
1244 }
1245 if (ssv_p->used[TIMEUNIT] == YES) {
1246 if (entry->val.ssvtables_p == 0) {
1247 CALLOC(SSVTABLES, entry->val.ssvtables_p, 1);
1248 }
1249 entry->val.ssvtables_p->timeunit_table[ssv_p->staffno][ssv_p->voiceno] = ssv_p;
1250 }
1251
1252 /* If time signature is set in this SSV, see if we have any
1253 * beamstyles or timeunits associated with that time signature.
1254 * If so, restore their values. */
1255 if (ssv_p->used[TIME] == YES && entry->val.ssvtables_p != 0) {
1256 /* Make new SSVs and copy the relevant fields for any
1257 * remembered beamstyles and/or timesunits associated
1258 * with this time signature. */
1259 struct SSV *beamstyle_ssv_p; /* SSV having beamstyle info */
1260 struct SSV *timeunit_ssv_p; /* SSV having timeunit info */
1261 struct MAINLL *mll_ssv_p; /* new SSV to add to list */
1262 struct SSV *nssv_p; /* new SSV to add to list */
1263 int s; /* staff */
1264 int v; /* voice */
1265
1266 /* Check all SSV contexts. The [0][0] entry is for Scoreo * The rest of row [0] is unused.
1267 * The [0] column is for staffs, for s > 0.
1268 */
1269 for (s = 0; s <= MAXSTAFFS; s++) {
1270 for (v = 0; v <= MAXVOICES; v++) {
1271
1272 /* Check if we need to create an SSV for
1273 * this staff/voice */
1274 beamstyle_ssv_p = entry->val.ssvtables_p->beamstyle_table[s][v];
1275 timeunit_ssv_p = entry->val.ssvtables_p->timeunit_table[s][v];
1276 if (beamstyle_ssv_p == 0 &&
1277 timeunit_ssv_p == 0) {
1278 /* nothing to do for this one */
1279 continue;
1280 }
1281
1282 /* If both saved SSVs are either zero or
1283 * the same SSV as where the time was just
1284 * set, no need to make another SSV */
1285 if ( (beamstyle_ssv_p == 0 ||
1286 beamstyle_ssv_p == ssv_p) &&
1287 (timeunit_ssv_p == 0 ||
1288 timeunit_ssv_p == ssv_p) ) {
1289 continue;
1290 }
1291
1292 /* need to create an SSV */
1293 mll_ssv_p = newMAINLLstruct(S_SSV, -1);
1294 insertMAINLL(mll_ssv_p, mll_p);
1295 mll_p = mll_ssv_p;
1296
1297 /* populate the new SSV */
1298 nssv_p = mll_ssv_p->u.ssv_p;
1299 if (beamstyle_ssv_p != 0) {
1300 nssv_p->nbeam = beamstyle_ssv_p->nbeam;
1301 nssv_p->beamstlist = beamstyle_ssv_p->beamstlist;
1302 nssv_p->beamrests = beamstyle_ssv_p->beamrests;
1303 nssv_p->beamspaces = beamstyle_ssv_p->beamspaces;
1304 nssv_p->nsubbeam = beamstyle_ssv_p->nsubbeam;
1305 nssv_p->subbeamstlist = beamstyle_ssv_p->subbeamstlist;
1306 nssv_p->used[BEAMSTLIST] = YES;
1307 }
1308 if (timeunit_ssv_p != 0) {
1309 nssv_p->timeunit = timeunit_ssv_p->timeunit;
1310 nssv_p->used[TIMEUNIT] = YES;
1311 }
1312
1313 /* fill in the SSV header */
1314 if (v != 0) {
1315 nssv_p->context = C_VOICE;
1316 }
1317 else if (s != 0) {
1318 /* The [s][0] entry is for staff s
1319 * when s > 0 */
1320 nssv_p->context = C_STAFF;
1321 }
1322 else {
1323 /* The [0][0] entry is actually score */
1324 nssv_p->context = C_SCORE;
1325 }
1326 nssv_p->staffno = s;
1327 nssv_p->voiceno = v;
1328 asgnssv(nssv_p);
1329 }
1330 }
1331 }
1332}