/* Output from p2c 1.21alpha-07.Dec.93, the Pascal-to-C translator */
/* From input file "mtxline.pas" */


#include "p2c.h"


#define MTXLINE_G
#include "mtxline.h"


boolean bind_left[22] = {
  false, false, true, false, false, true, true, false, true, true, true,
  false, true, false, true, false, true, false, false, false, false, false
};

#ifndef STRINGS_H
#include "strings.h"
#endif

#ifndef CONTROL_H
#include "control.h"
#endif

#ifndef NOTES_H
#include "notes.h"
#endif

#ifndef UTILITY_H
#include "utility.h"
#endif


typedef music_word word_scan[max_words];

typedef struct line_info {
  word_index0 here, nword;
  bar_index0 nbar;
  paragraph_index0 voice_pos, voice_stave, mus, chord;
  short extra;
  boolean vocal;
  word_index0 bar_bound[max_bars + 1];
  short word_bound[max_words + 1];
  word_scan scan;
} line_info;


Static Char name[22][10] = {
  "?", "note", "znote", "lyricsTag", "(", ")", ")(", "[", "]", "_", "PMX<",
  "macro", "endMacro", "meter", "ornament", "rest", "//", "BAR", "TeX", "@",
  "firstonly", "ERROR"
};

Static line_info info[maxvoices];


/* ------------------------------------------------------------- */

short beatsPerLine(void)
{
  short Result;
  voice_index voice, FORLIM;
  line_info *WITH;

  FORLIM = nvoices;
  for (voice = 1; voice <= FORLIM; voice++) {
    WITH = &info[voice-1];
    if (WITH->nbar > 0 || WITH->extra > 0) {
      if (WITH->extra % one_beat > 0) {
/* p2c: mtxline.pas, line 99:
 * Note: Using % for possibly-negative arguments [317] */
	error3(voice, "Line length not an integer number of beats");
      }
      Result = WITH->nbar * meternum + WITH->extra / one_beat;
    }
  }
  return Result;
}


void skipChordBar(voice_index voice)
{
  line_info *WITH;
  Char STR1[256];

  WITH = &info[voice-1];
  if (WITH->chord > 0) {
    sprintf(STR1, "%c", barsym);
    if (!strcmp(P[WITH->chord - 1], STR1))
      predelete(P[WITH->chord - 1], 1);
  }
}


Char *getBar(Char *Result, voice_index voice, short bar)
{
  line_info *WITH;

  WITH = &info[voice-1];
  return (substr_(Result, P[WITH->mus - 1],
		  WITH->word_bound[WITH->bar_bound[bar-1]] + 1,
		  WITH->word_bound[WITH->bar_bound[bar]] -
		  WITH->word_bound[WITH->bar_bound[bar-1]]));
}


Char *musicLine(Char *Result, voice_index voice)
{
  return strcpy(Result, P[musicLineNo(voice) - 1]);
}


paragraph_index0 musicLineNo(voice_index voice)
{
  return (info[voice-1].mus);
}


void setMusicLineNo(voice_index voice, paragraph_index lno)
{
  info[voice-1].mus = lno;
}


paragraph_index0 chordLineNo(voice_index voice)
{
  return (info[voice-1].chord);
}


void setChordLineNo(voice_index voice, paragraph_index lno)
{
  info[voice-1].chord = lno;
}


void setVocal(voice_index voice, boolean voc)
{
  info[voice-1].vocal = voc;
}


boolean isVocal(voice_index voice)
{
  return (info[voice-1].vocal);
}


void setStavePos(voice_index voice, stave_index stave, stave_index pos)
{
  line_info *WITH;

  WITH = &info[voice-1];
  WITH->voice_pos = pos;
  WITH->voice_stave = stave;
}


stave_index voiceStave(voice_index voice)
{
  return (info[voice-1].voice_stave);
}


stave_index voicePos(voice_index voice)
{
  return (info[voice-1].voice_pos);
}


voice_index companion(voice_index voice)
{
  short s;

  s = info[voice-1].voice_stave;
  if (number_on_stave[s-1] == 1)
    return voice;
  else if (info[voice-1].voice_pos == 1)
    return (voice + 1);
  else
    return (voice - 1);
}


void regroup(voice_index voice)
{
  word_index0 i, j;
  word_index0 j2 = 0;
  line_info *WITH;
  word_index0 FORLIM;

  WITH = &info[voice-1];
  if (debugMode())
    printf("Voice %d has %d bars at ", voice, WITH->nbar);
  if (debugMode()) {
    FORLIM = WITH->nbar + 1;
    for (i = 1; i <= FORLIM; i++)
      printf("%d ", WITH->bar_bound[i]);
  }
  FORLIM = WITH->nbar;
  for (i = 1; i <= FORLIM; i++) {
    j2 = WITH->bar_bound[i];
    j = j2 + 1;
    while (j <= WITH->here &&
	   (bind_left[(long)WITH->scan[j-1]] || WITH->scan[j-1] == barword)) {
      WITH->bar_bound[i]++;
      j++;
    }
  }
  if (WITH->extra > 0)
    WITH->bar_bound[WITH->nbar + 1] = WITH->here;
  if (debugMode()) {
    printf(" regrouped to ");
    FORLIM = WITH->nbar + 1;
    for (i = 1; i <= FORLIM; i++)
      printf("%d ", WITH->bar_bound[i]);
  }
  if (debugMode())
    putchar('\n');
  WITH->nword = WITH->here;
}


void resetInfo(voice_index voice, Char *buf)
{
  line_info *WITH;

  WITH = &info[voice-1];
  strcpy(buf, P[WITH->mus - 1]);
  *P[WITH->mus - 1] = '\0';
  WITH->bar_bound[0] = 0;
  WITH->word_bound[0] = 0;
  WITH->nbar = 0;
  WITH->here = 0;
}


void clearLabels(void)
{
  voice_index voice, FORLIM;
  line_info *WITH;

  FORLIM = nvoices;
  for (voice = 0; voice <= FORLIM - 1; voice++) {
    WITH = &info[voice];
    WITH->chord = 0;
    WITH->mus = 0;
  }
}


void appendNote(voice_index voice, music_word nscan)
{
  line_info *WITH;

  WITH = &info[voice-1];
  WITH->here++;
  if (WITH->here > max_words)
    error3(voice, "Words per line limit exceeded");
  WITH->scan[WITH->here - 1] = nscan;
}


void appendToLine(voice_index voice, Char *note)
{
  line_info *WITH;

  if (*note == '\0')
    return;
  WITH = &info[voice-1];
  sprintf(P[WITH->mus - 1] + strlen(P[WITH->mus - 1]), "%s%c", note, blank);
  WITH->word_bound[WITH->here] = strlen(P[WITH->mus - 1]);
}


void markBar(voice_index voice)
{
  line_info *WITH;

  WITH = &info[voice-1];
  if (WITH->nbar == 0)
    error3(voice, "Empty bar");
  else
    WITH->bar_bound[WITH->nbar] = WITH->here;
}


short numberOfBars(voice_index voice)
{
  return (info[voice-1].nbar);
}


void barForward(voice_index voice, short nbars)
{
  line_info *WITH;

  WITH = &info[voice-1];
  if (WITH->nbar + nbars < 0)
    error3(voice, "Next voice before bar is full");
  WITH->nbar += nbars;
  if (WITH->nbar > max_bars)
    error3(voice, "Bars per line limit exceeded");
  if (nbars > 0)
    WITH->bar_bound[WITH->nbar] = WITH->here;
}


void setExtraLength(voice_index voice, short ext)
{
  line_info *WITH;

  WITH = &info[voice-1];
  WITH->extra = ext;
  WITH->scan[WITH->here] = other;
}


short ExtraLength(voice_index voice)
{
  return (info[voice-1].extra);
}


voice_index0 findVoice(Char *w_)
{
  voice_index0 Result = 0;
  Char w[256];
  short i, FORLIM;

  strcpy(w, w_);
  curtail(w, ':');
  FORLIM = nvoices;
  for (i = 1; i <= FORLIM; i++) {
    if (!strcmp(w, voice_label[i-1]))
      return i;
  }
  getNum(w, &i);
  if (i == 0)
    return Result;
  if (i > 0 && i <= nvoices)
    return i;
  error("Numeric label outside range 1..nvoices", print);
  return Result;
}


Static void info3(voice_index voice)
{
  line_info *WITH;

  WITH = &info[voice-1];
  printf("In voice \"%s\" near word %d:\n", voice_label[voice-1], WITH->here);
  printf("%*cV\n", WITH->word_bound[WITH->here], ' ');
}


void error3(voice_index voice, Char *message)
{
  Char STR1[256];

  info3(voice);
  sprintf(STR1, "   %s", message);
  error(STR1, print);
}


void warning3(voice_index voice, Char *message)
{
  Char STR1[256];

  info3(voice);
  sprintf(STR1, "   %s", message);
  warning(STR1, print);
}


Char *nextMusicWord(Char *Result, voice_index voice)
{
  return (MusicWord(Result, voice, info[voice-1].here));
}


Char *MusicWord(Char *Result, short voice, short n)
{
  line_info *WITH;

  WITH = &info[voice-1];
  if (n > 0 && n <= WITH->nword)
    return (substr_(Result, P[WITH->mus - 1], WITH->word_bound[n-1] + 1,
		    WITH->word_bound[n] - WITH->word_bound[n-1] - 1));
  else
    return strcpy(Result, "");
}


music_word thisNote(voice_index voice)
{
  line_info *WITH;

  WITH = &info[voice-1];
  return (WITH->scan[WITH->here - 2]);
}


music_word nextNote(voice_index voice)
{
  line_info *WITH;

  WITH = &info[voice-1];
  return (WITH->scan[WITH->here - 1]);
}


boolean endOfBar(voice_index voice, short bar_no)
{
  line_info *WITH;

  WITH = &info[voice-1];
  return (WITH->here > WITH->bar_bound[bar_no]);
}


void gotoBar(voice_index voice, short bar_no)
{
  line_info *WITH;

  WITH = &info[voice-1];
  WITH->here = WITH->bar_bound[bar_no-1] + 1;
}


Char *getMusicWord(Char *Result, voice_index voice)
{
  line_info *WITH;

  WITH = &info[voice-1];
  line_no = orig_line_no[WITH->mus - 1];
  MusicWord(Result, voice, WITH->here);
  WITH->here++;
  return Result;
}


boolean maybeMusicLine(Char *l_)
{
  Char l[256];
  Char w[256];
  music_word nscan;

  strcpy(l, l_);
  GetNextWord(w, l, blank, dummy);
/* p2c: mtxline.pas: Note: Eliminated unused assignment statement [338] */
  if (pos1(w[0], "abcdefgr()[]{}CMm") == 0)
    return false;
  if (pos1(':', w) == 0)
    return true;
  getNextMusWord(w, l, &nscan);
  return (nscan == abcdefg);
}


#define nmacros         99


Static boolean macro_initialized = false;
Static Char macro_text[nmacros][256];


Static void macroInit(void)
{
  short i;

  if (macro_initialized)
    return;
  macro_initialized = true;
  for (i = 0; i <= nmacros - 1; i++)
    *macro_text[i] = '\0';
}


Static short identifyMacro(Char *s_)
{
  Char s[256];
  short k;

  strcpy(s, s_);
  predelete(s, 2);
  getNum(s, &k);
  return k;
}


/* Local variables for getNextMusWord: */
struct LOC_getNextMusWord {
  Char *buf, *note;
} ;

Local void expandThisMacro(struct LOC_getNextMusWord *LINK)
{
  Char playtag[11];
  short playID;
  Char w[256];
  Char STR1[72];
  Char STR2[42];
  Char STR3[68];
  Char STR4[54];
  Char STR5[256], STR7[256];

  macroInit();
  if (strlen(LINK->note) == 1)
    error("Can't terminate a macro that has not been started", print);
  playID = identifyMacro(LINK->note);
  toString(playtag, playID);
/* p2c: mtxline.pas, line 310:
 * Note: Possible string truncation in assignment [145] */
  if (playID < 1 || playID > 99) {
    sprintf(STR2, "Macro ID %s is not in range 1..99", playtag);
    error(STR2, print);
  }
  if (LINK->note[1] == 'P') {
    if (*macro_text[playID-1] == '\0') {
      sprintf(STR3,
	      "Macro %s inserts empty text: did you define it before use?",
	      playtag);
      warning(STR3, print);
    }
    if (strlen(macro_text[playID-1]) + strlen(LINK->buf) <= 255) {
      if (debugMode())
	printf("Inserting macro %s text \"%s\"\n",
	       playtag, macro_text[playID-1]);
      printf("Buffer before insert: %s\n", LINK->buf);
      sprintf(LINK->buf, "%s%s",
	      macro_text[playID-1], strcpy(STR5, LINK->buf));
      return;
    }
    sprintf(STR4, "Expansion of macro %s causes buffer overflow", playtag);
    error(STR4, print);
  }
  if (pos1(LINK->note[1], "SR") == 0) {
    sprintf(STR4, "Second character %c of macro word should be in \"PRS\"",
	    LINK->note[1]);
    error(STR4, print);
  }
  *macro_text[playID-1] = '\0';
  do {
    GetNextWord(w, LINK->buf, blank, dummy);
    if (*w == '\0') {
      sprintf(STR1,
	"Macro definition %s should be terminated on the same input line",
	playtag);
      error(STR1, print);
    }
    if (!strcmp(w, "M")) {
      if (debugMode())
	printf("Macro %s is: %s\n", playtag, macro_text[playID-1]);
      break;
    }
    if (w[0] == 'M' && strlen(w) > 1) {
      if (w[1] != 'P' || identifyMacro(w) == playID) {
	sprintf(STR7, "%s not allowed inside macro definition %s", w, playtag);
	error(STR7, print);
      }
    }
    sprintf(macro_text[playID-1] + strlen(macro_text[playID-1]), "%s ", w);
  } while (true);
  if (LINK->note[1] == 'R') {
    LINK->note[1] = 'P';
    sprintf(LINK->buf, "%s %s", LINK->note, strcpy(STR7, LINK->buf));
  }
}


void getNextMusWord(Char *buf_, Char *note_, music_word *nscan)
{
  struct LOC_getNextMusWord V;
  Char maybe_error[256];
  Char STR2[256];

  V.buf = buf_;
  V.note = note_;
  GetNextWord(V.note, V.buf, blank, dummy);
  if (*V.note == '\0')
    return;
  if (V.note[0] == 'M' && expandMacro()) {
    expandThisMacro(&V);
    getNextMusWord(V.buf, V.note, nscan);
    return;
  }
  if (V.note[0] == '\\') {
    *maybe_error = '\0';
    if (V.note[strlen(V.note) - 1] != '\\')
      strcpy(maybe_error, V.note);
    while (*V.buf != '\0' && V.note[strlen(V.note) - 1] != '\\')
      sprintf(V.note + strlen(V.note), " %s",
	      GetNextWord(STR2, V.buf, blank, dummy));
    if (V.note[strlen(V.note) - 1] != '\\')
      error("Unterminated TeX literal", print);
    *nscan = texword;
    if (*maybe_error != '\0') {
      sprintf(STR2, "Possible unterminated TeX literal: %s", maybe_error);
      warning(STR2, print);
    }
    return;
  }
  if (solfaNoteNames()) {
    translateSolfa(V.note);
    if (V.note[0] == '"')
      predelete(V.note, 1);
  }
  switch (V.note[0]) {

  case '_':
    *nscan = pmxprefix;
    delete1(V.note, 1);
    break;

  case 'a':
  case 'b':
  case 'c':
  case 'd':
  case 'e':
  case 'f':
  case 'g':
    *nscan = abcdefg;
    break;

  case 'z':
    *nscan = zword;
    break;

  case '(':
    *nscan = lparen;
    break;

  case '{':
    if (V.note[strlen(V.note) - 1] == '}')
      *nscan = lyrtag;
    else
      *nscan = lparen;
    break;

  case ')':
    if (pos1('(', V.note) == 0)
      *nscan = rparen;
    else
      *nscan = rlparen;
    break;

  case '}':
    if (pos1('{', V.note) == 0)
      *nscan = rparen;
    else
      *nscan = rlparen;
    break;

  case '[':
    *nscan = lbrac;
    break;

  case ']':
    *nscan = rbrac;
    break;

  case '@':
    *nscan = atword;
    break;

  case 'm':
    *nscan = mword;
    break;

  case 'r':
    *nscan = rword;
    break;

  case '#':
  case '-':
  case 'n':
  case 'x':
  case '?':
  case 's':
  case 't':
  case 'D':
    *nscan = pmxl;
    break;

  case 'M':
    if (!strcmp(V.note, "M"))
      *nscan = endmacro;
    else
      *nscan = macro;
    break;

  case 'G':
    if (pos1('A', V.note) > 0)
      *nscan = pmxl;
    else
      *nscan = other;
    break;

  case '1':
  case '2':
  case '3':
  case '4':
  case '5':
  case '6':
  case '7':
  case '8':
  case '9':
    if (pos1('/', V.note) > 0)
      *nscan = mword;
    else
      *nscan = pmxl;
    break;

  case 'o':
    *nscan = oword;
    break;

  case 'A':
  case 'V':
    *nscan = FirstOnly;
    break;

  case '/':
    if (!strcmp(V.note, "//"))
      *nscan = nextvoice;
    else if (!strcmp(V.note, "/")) {
      strcpy(V.note, "//");
      warning("/ changed to //", print);
      *nscan = nextvoice;
    } else {
      error("Word starts with /: do you mean \\?", print);
      *nscan = err;
    }
    break;

  default:
    if (pos1('|', V.note) > 0)
      *nscan = barword;
    else
      *nscan = other;
    break;
  }
}


boolean upper(voice_index voice)
{
  line_info *WITH;

  WITH = &info[voice-1];
  if (WITH->voice_pos == 1 && voice < nvoices)
    return (WITH->voice_stave == info[voice].voice_stave);
  else
    return false;
}


void describeVoice(voice_index voice, Char *describe_lyr)
{
  short bar, w;
  line_info *WITH;
  Char STR1[256];
  line_info *WITH1;
  short FORLIM;

  WITH = &info[voice-1];
  printf("Voice `%s' is on line %d", voice_label[voice-1], WITH->mus);
  if (WITH->vocal) {
    printf(", is vocal");
    puts(describe_lyr);
  } else {
    if (WITH->chord > 0)
      printf(" and has chords on line %d", WITH->chord);
    putchar('\n');
  }
  if (!debugMode())
    return;
  WITH1 = &info[voice-1];
  printf("Line has %d bars", WITH1->nbar);
  if (WITH1->extra > 0)
    printf(" + %d/64 notes\n", WITH1->extra);
  else
    putchar('\n');
  printf("Bars:");
  FORLIM = WITH1->nbar;
  for (bar = 1; bar <= FORLIM; bar++)
    printf(" %s ", getBar(STR1, voice, bar));
  printf("\nWord types:");
  FORLIM = WITH1->nword;
  for (w = 0; w <= FORLIM - 1; w++)
    printf(" %s", name[(long)WITH1->scan[w]]);
  putchar('\n');
}


boolean aloneOnStave(stave_index stave)
{
  voice_index v;

  if (number_on_stave[stave-1] != 2)
    return true;
  else {
    v = first_on_stave[stave-1];
    return (info[v-1].mus == 0 || info[v].mus == 0);
  }
}


void selectVoices(Char *line_)
{
  Char line[256];
  short i = 0;
  short k;
  Char word[256];
  short FORLIM;

  strcpy(line, line_);
  FORLIM = nvoices;
  for (k = 0; k <= FORLIM - 1; k++)
    selected[k] = false;
  printf("Voice change to: %s = ", line);
  while (i < nvoices) {
    GetNextWord(word, line, blank, dummy);
    if (*word == '\0') {
      putchar('\n');
      return;
    }
    i++;
    printf("%s ", word);
    k = findVoice(word);
    if (k == 0)
      error("This voice is not in the style", print);
    selected[k-1] = true;
  }
  putchar('\n');
}




/* End. */
