
/* IO_1.0-2.0a.c */

/*
   Musical Notation Editor, Chris Cannam 1994

   Legacy File I/O Functions and Methods

   This is file IO.c from version 2.0a of Rosegarden,
   with symbols renamed.  This file is not supported.
   If the methods in the Classes vtables change, it
   may need more work (we still call read/write
   through the class method pointer).
*/


/*
   We only need methods for reading in and writing out
   classes which there can actually be instances of --
   Metronome, Clef, Key, TimeSignature, Text, Rest,
   Group and Chord.  We don't need to be able to write
   out the Bar as it's a transient class that can be
   deduced from the others (using the Stave's format).
*/

#include <stdio.h>
#include <ctype.h>
#include <stddef.h>

#include "General.h"
#include "Tags.h"
#include "Classes.h"
#include "Notes.h"
#include "Stave.h"
#include "StaveEdit.h"
#include "Widgets.h"
#include "Menu.h"
#include "Format.h"
#include "ItemList.h"

#include "Yawn.h"

#define TAG_LEN    50
#define STRING_LEN 500


#define WriteNothing old_WriteNothing
#define WriteNoteVoice old_WriteNoteVoice
#define ReadNoteVoice old_ReadNoteVoice
#define WriteMetronome old_WriteMetronome
#define ReadMetronome old_ReadMetronome
#define WriteClef old_WriteClef
#define ReadClef old_ReadClef
#define WriteKey old_WriteKey
#define ReadKey old_ReadKey
#define WriteTimeSignature old_WriteTimeSignature
#define ReadTimeSignature old_ReadTimeSignature
#define WriteText old_WriteText
#define ReadText old_ReadText
#define WriteRest old_WriteRest
#define ReadRest old_ReadRest
#define WriteChord old_WriteChord
#define ReadChord old_ReadChord
#define WriteGroup old_WriteGroup
#define ReadGroup old_ReadGroup
#define ReadItemList old_readItemList
#define ReadItem old_ReadItem
#define LoadStaveFromFile old_LoadStaveFromFile

static Boolean ReadItem(MusicObject *, FILE *, int *);
static Boolean ReadClef(MusicObject, FILE *);
static Boolean ReadKey(MusicObject, FILE *);
static Boolean ReadTimeSignature(MusicObject, FILE *);
static Boolean ReadText(MusicObject, FILE *);
static Boolean ReadRest(MusicObject, FILE *);
static Boolean ReadGroup(MusicObject, FILE *);
static Boolean ReadChord(MusicObject, FILE *);
static Boolean ReadMetronome(MusicObject, FILE *);

static int            dc;

static int            rInt;
static unsigned int   rUInt;
static unsigned long  rULong;
static short          rShort;
static char           rTag[TAG_LEN];
static char           rString[STRING_LEN];

static String         musicIOErrorMessage = NULL;

static Boolean        ReadWS(FILE *);
static Boolean        ReadInt(FILE *);
static Boolean        ReadUnsignedInt(FILE *);
static Boolean        ReadHexUnsignedInt(FILE *);
static Boolean        ReadHexUnsignedLong(FILE *);
static Boolean        ReadShort(FILE *);
static Boolean        ReadTag(FILE *);
static Boolean        ReadConstTag(String, FILE *);
static Boolean        ReadString(FILE *);

static Boolean        BuildFileError(FILE *, String);
static Boolean        BuildFileError2s(FILE *, String, String);
static Boolean        BuildFileError3s(FILE *, String, String, String);

static Boolean        ReadItemList(ItemList *, int, FILE *);

static struct {
  String     tag;
  size_t     size;
  Boolean (* read)(MusicObject, FILE *);
} itemTags[] = {
  { ":",             sizeof(Chord),         ReadChord,         },
  { "Group",         sizeof(Group),         ReadGroup,         },
  { "Rest",          sizeof(Rest),          ReadRest,          },
  { "Text",          sizeof(Text),          ReadText,          },
  { "Clef",          sizeof(Clef),          ReadClef,          },
  { "Key",           sizeof(Key),           ReadKey,           },
  { "Metronome",     sizeof(Metronome),     ReadMetronome,     },
  { "TimeSignature", sizeof(TimeSignature), ReadTimeSignature, },
  { "Chord",         sizeof(Chord),         ReadChord,         },
};


#ifdef DEBUG
#define Write(x) do{if(file==stderr)fprintf(file,"0x%p   ",obj);\
		    for(dc=0;dc<depth;++dc)fputc('\t',file);fprintf x;}while(0)
#else
#define Write(x) do{for(dc=0;dc<depth;++dc)fputc('\t',file);fprintf x;}while(0)
#endif



void WriteNothing(MusicObject obj, FILE *file, int depth)
{
  Begin("WriteNothing");
  End;
}


void WriteNoteVoice(NoteVoice *a, FILE *file, int depth)
{
  Begin("WriteNoteVoice");

  fprintf(file, " %hd %x", a->pitch, (int)(a->modifiers));

  End;
}


Boolean ReadNoteVoice(NoteVoice *a, FILE *file)
{
  Begin("ReadNoteVoice");

  if (!ReadShort(file))
    Return(BuildFileError(file, "First argument of Voice lost or corrupted"));

  a->pitch = rShort;

  if (!ReadHexUnsignedInt(file))
    Return(BuildFileError(file, "Second argument of Voice lost or corrupted"));

  a->modifiers = (NoteMods)(rUInt);

  Return(True);
}


void WriteMetronome(MusicObject obj, FILE *file, int depth)
{
  MetronomePart m = ((Metronome *)obj)->metronome;

  Begin("WriteMetronome");

  Write((file, "Metronome\n"));
  Write((file, "\t%s %u\n", m.beat.chord.visual->name, m.setting));

  End;
}


Boolean ReadMetronome(MusicObject obj, FILE *file)
{
  int     i;
  Boolean dotted;
  NoteTag tag;

  Begin("ReadMetronome");

  if (!ReadTag(file))
    Return(BuildFileError(file,
			  "Couldn't read Metronome Type or Dotted segment"));

  if (!strcasecmp(rTag, "Dotted")) {

    dotted = True;
    if (!ReadTag(file))
      Return(BuildFileError(file, "Couldn't read Metronome Type segment"));

  } else dotted = False;

  for (i = 0; i < noteVisualCount; ++i)
    if (!strncasecmp(noteVisuals[i].undotted.name, rTag, strlen(rTag))) {
      tag = noteVisuals[i].undotted.type;
      break;
    }

  if (!ReadUnsignedInt(file))
    Return(BuildFileError(file, "Couldn't read Metronome Setting segment"));

  (void)NewMetronome((Metronome *)obj, tag, dotted, rUInt);

  Return(True);
}


void WriteClef(MusicObject obj, FILE *file, int depth)
{
  ClefPart c = ((Clef *)obj)->clef;

  Begin("WriteClef");

  Write((file, "Clef\n"));
  Write((file, "\tClef Name %s\n", c.visual->name));

  End;
}


Boolean ReadClef(MusicObject obj, FILE *file)
{
  int i;

  Begin("ReadClef");

  if (!ReadConstTag("Clef", file) || !ReadConstTag("Name", file))
    Return(BuildFileError(file, "Expected Clef Name segment not found"));

  if (!ReadTag(file))
    Return(BuildFileError(file, "Couldn't read Clef Name segment"));

  for (i = 0; i < clefVisualCount; ++i)

    if (!strncasecmp(rTag, clefVisuals[i].name, strlen(rTag))) {

      (void)NewClef((Clef *)obj, clefVisuals[i].type);
      break;
    }

  if (i >= clefVisualCount)
    Return(BuildFileError2s(file, "Unknown Clef Name `%s'", rTag));

  Return(True);
}


void WriteKey(MusicObject obj, FILE *file, int depth)
{
  KeyPart k = ((Key *)obj)->key;

  Begin("WriteKey");

  Write((file, "Key\n"));
  Write((file, "\tKey Name %s\n", k.visual->name));

  End;
}


Boolean ReadKey(MusicObject obj, FILE *file)
{
  int i;

  Begin("ReadKey");

  if (!ReadConstTag("Key", file) || !ReadConstTag("Name", file))
    Return(BuildFileError(file, "Expected Key Name segment not found"));

  if (!ReadString(file))
    Return(BuildFileError(file, "Couldn't read Key Name segment"));

  for (i = 0; i < keyVisualCount; ++i)
    if (!strcasecmp(rString, keyVisuals[i].name)) {
      (void)NewKey((Key *)obj, keyVisuals[i].key);
      break;
    }

  if (i >= keyVisualCount)
    Return(BuildFileError2s(file, "Unknown Key Name `%s'", rString));

  Return(True);
}


void WriteTimeSignature(MusicObject obj, FILE *file, int depth)
{
  fprintf(stderr,"old_WriteTimeSignature called, it shouldn't be\n");

  /*
  TimeSigPart t = ((TimeSignature *)obj)->timesig;
  Begin("WriteTimeSignature");

  Write((file, "TimeSignature\n"));
  Write((file, "\tIndication %d %d\n", t.numerator, t.denominator));
  End;
  */
}


Boolean ReadTimeSignature(MusicObject obj, FILE *file)
{
  int a;

  Begin("ReadTimeSignature");

  if (!ReadConstTag("Indication", file))
    Return(BuildFileError(file,
			  "Expected Timesig Indication segment not found"));

  if (!ReadInt(file))
    Return(BuildFileError(file,
			  "First argument of Indication lost or corrupted"));

  a = rInt;

  if (!ReadInt(file))
    Return(BuildFileError(file,
			  "Second argument of Indication lost or corrupted"));

  /*  {
    char tstext[30];
    sprintf(tstext, "(time sig: %d/%d)", (int)a, (int)rInt);
    (void)NewText(((Text *)obj), tstext, TextAboveBarLine);
    */
    fprintf(stderr,"Warning: time signatures not supported for old "
	    "Rosegarden file import\n");
    /*  }*/

  /*
  (void)NewTimeSignature((TimeSignature *)obj, a, rInt);
  */
  Return(True);
}


void WriteText(MusicObject obj, FILE *file, int depth)
{
  TextPart t = ((Text *)obj)->text;

  Begin("WriteText");

  Write((file, "Text\n"));
  Write((file, "\tString %s\n", t.text));
  Write((file, "\tPosition %d\n", (int)(t.position)));

  End;
}


Boolean ReadText(MusicObject obj, FILE *file)
{
  Begin("ReadText");

  if (!ReadConstTag("String", file))
    Return(BuildFileError(file, "Expected Text String segment not found"));

  if (!ReadString(file))
    Return(BuildFileError(file, "Couldn't read Text String segment"));

  if (!ReadConstTag("Position", file))
    Return(BuildFileError(file, "Expected Text Position segment not found"));

  if (!ReadInt(file))
    Return(BuildFileError(file, "Couldn't read Text Position segment"));

  (void)NewText((Text *)obj, rString, (TextPosnTag)rInt);

  Return(True);
}


void WriteRest(MusicObject obj, FILE *file, int depth)
{
  RestPart r = ((Rest *)obj)->rest;

  Begin("WriteRest");

  Write((file, "Rest\n"));
  Write((file, "\t%s\n", r.visual->name));

#ifdef DEBUG
  if (file == stderr) {
    if (((Rest *)obj)->phrase.tied_forward)  Write((file,"\tTied Forward\n"));
    if (((Rest *)obj)->phrase.tied_backward) Write((file,"\tTied Backward\n"));
  }
#endif

  End;
}


Boolean ReadRest(MusicObject obj, FILE *file)
{
  int i;

  Begin("ReadRest");

  if (!ReadWS(file))
    Return(BuildFileError(file,
			  "Unexpected EOF (expecting Rest Name segment)"));
  
  if (!ReadString(file))
    Return(BuildFileError(file, "Couldn't read Rest Name segment"));

  for (i = 0; i < restVisualCount; ++i) {
    if (!strcasecmp(restVisuals[i].undotted.name, rString)) {
      (void)NewRest((Rest *)obj, restVisuals[i].undotted.type, False);
      break;
    }
    if (!strcasecmp(restVisuals[i].dotted.name, rString)) {
      (void)NewRest((Rest *)obj, restVisuals[i].dotted.type, True);
      break;
    }
  }

  if (i >= restVisualCount)
    Return(BuildFileError2s(file, "Unknown Rest Name `%s'", rString));

  Return(True);
}


void WriteChord(MusicObject obj, FILE *file, int depth)
{
  int       i;
  ChordPart c = ((Chord *)obj)->chord;

  Begin("WriteChord");

  Write((file, ": %s %lx %hd",
	 c.visual->name, c.modifiers, c.voice_count));
  for (i = 0; i < c.voice_count; ++i)
    WriteNoteVoice(&(c.voices[i]), file, depth+1);

  fputc('\n', file);

#ifdef DEBUG
  if (file == stderr) {
    if (((Chord *)obj)->phrase.tied_forward) Write((file,"\tTied Forward\n"));
    if (((Chord *)obj)->phrase.tied_backward)Write((file,"\tTied Backward\n"));
  }
#endif

  End;
}


Boolean ReadChord(MusicObject obj, FILE *file)
{
  int        i;
  short      n;
  NoteVoice *v;
  ChordMods  m;
  NoteTag    tag;
  Boolean    dotted;

  Begin("ReadChord");

  if (!ReadTag(file))
    Return(BuildFileError(file, "Couldn't read Chord Type or Dotted segment"));

  if (!strcasecmp(rTag, "Dotted")) {

    dotted = True;
    if (!ReadTag(file))
      Return(BuildFileError(file, "Couldn't read Chord Type segment"));

  } else dotted = False;

  for (i = 0; i < noteVisualCount; ++i)
    if (!strncasecmp(noteVisuals[i].undotted.name, rTag, strlen(rTag))) {
      tag = noteVisuals[i].undotted.type;
      break;
    }

  if (i > noteVisualCount)
    Return(BuildFileError2s(file, "Unknown Chord Type `%s'", rTag));

  if (!ReadHexUnsignedLong(file))
    Return(BuildFileError(file, "Expected Chord Modifier segment not found"));

  if (!ReadShort(file))
    Return(BuildFileError(file,
			  "Expected Chord Voice Count segment not found"));

  m = rULong;
  n = rShort;
  v = (NoteVoice *)XtMalloc(n * sizeof(NoteVoice));

  for (i = 0; i < n; ++i)
    if (!ReadNoteVoice(&(v[i]), file)) {
      XtFree((void *)v);
      Return(BuildFileError(file, "Too few Note Voices in Chord segment?"));
    }

  (void)NewChord((Chord *)obj, v, n, m, tag, dotted);

  Return(True);
}


void WriteGroup(MusicObject obj, FILE *file, int depth)
{
  int      n = 0;
  Group   *g = (Group *)obj;
  GroupTag t = ((Group *)obj)->group.type;
  ItemList i;

  Begin("WriteGroup");

  for (i = g->group.start; i; i = (i == g->group.end) ? NULL : iNext(i)) {
    ++n;
  }

  Write((file, "Group\n"));
  Write((file, "\t%d %s", n,
	 t == GroupBeamed       ? "Beamed" :
	 t == GroupTupled       ? "Tupled" :
	 t == GroupDeGrace      ? "Grace"  : "Ordinary"));

  if (t == GroupTupled) {
    fprintf(file, " %hd %hd\n", g->group.tupled_length, g->group.tupled_count);
  } else {
    fputc('\n', file);
  }

#ifdef DEBUG
  if (file == stderr) {
    if (g->phrase.tied_forward)  Write((file,"\tTied Forward\n"));
    if (g->phrase.tied_backward) Write((file,"\tTied Backward\n"));
  }
#endif

  for (i = g->group.start; i; i = (i == g->group.end) ? NULL : iNext(i)) {
    i->item->methods->write((MusicObject)(i->item), file, depth+1);
  }

  End;
}


Boolean ReadGroup(MusicObject obj, FILE *file)
{
  int            n;
  ItemList       items;
  GroupTag       type; 
  short          tupled_length = 0;
  short          tupled_count = 0;
  static Boolean readingGroup = False;

  Begin("ReadGroup");

  if (readingGroup) Return
    (BuildFileError
     (file, "This implementation does not support groups within groups"));

  if (!ReadInt(file))
    Return(BuildFileError(file,
			  "Expected Group Item Count integer not found"));

  n = rInt;

  if (!ReadTag(file))
    Return(BuildFileError(file, "Expected Group Type tag not found"));

  if      (!strcasecmp(rTag, "Beamed"  )) type = GroupBeamed;
  else if (!strcasecmp(rTag, "Tupled"  )) type = GroupTupled;
  else if (!strcasecmp(rTag, "Grace"   )) type = GroupDeGrace;
  else if (!strcasecmp(rTag, "Ordinary")) type = GroupNoDecoration;
  else Return(BuildFileError2s(file, "Unknown Group Type tag `%s'", rTag));

  if (type == GroupTupled) {

    if (!ReadInt(file))
      Return(BuildFileError(file, "Expected Group Tupled Length not found"));
    tupled_length = NumberToMTime(rInt);

    if (!ReadInt(file))
      Return(BuildFileError(file, "Expected Group Tupled Count not found"));
    tupled_count = NumberToMTime(rInt);
  }

  readingGroup = True;

  if (!ReadItemList(&items, n, file))
    Return(BuildFileError(file, "Couldn't read Item List for Group"));

  (void)NewGroup((Group *)obj, type, items, (ItemList)Last(items));
  ((Group *)obj)->group.tupled_length = tupled_length;
  ((Group *)obj)->group.tupled_count  = tupled_count;

  readingGroup = False;

  Return(True);
}


/* ReadItem is the only Read function to actually allocate its own
   space, as we don't know how big the Item will be beforehand -- this
   is a polymorphic function.  (Apart from ReadChord, that is, which
   allocates space for the NoteVoices itself, and ReadItemList, which
   calls for NewItemLists left, right and centre.) */

Boolean ReadItem(MusicObject *obj, FILE *file, int *ii)
{
  int         i;
  MusicObject item;

  Begin("ReadItem");

  if (!ReadTag(file))
    Return(BuildFileError(file, "Expected Item Discrimination Tag not found"));

  for (i = 0; i < XtNumber(itemTags); ++i)
    if (!strcmp(rTag, itemTags[i].tag)) break;

  if (i >= XtNumber(itemTags))
    Return(BuildFileError2s(file,
			    "Unknown Item Discrimination Tag `%s'", rTag));

  item = (MusicObject)XtMalloc(itemTags[i].size);

  if (!itemTags[i].read(item, file)) {
    XtFree(item);
    Return(BuildFileError2s(file, "Couldn't read indicated %s Item",
			    itemTags[i].tag));
  }

  /* will fail if a time sig is the last thing on a staff */
  if (!strcmp(itemTags[i].tag, "TimeSignature")) {
    if (ii) ++*ii;
    Return(ReadItem(obj, file, ii));
  }

  *obj = item;

  Return(True);
}
 



static Boolean ReadItemList(ItemList *list, int n, FILE *file)
{
  int      i;
  ItemList items = NULL;
  Item    *item;

  Begin("ReadItemList");

  for (i = 0; i < n; ++i) {

    if (!ReadItem((MusicObject *)&item, file, &i)) {

      for (items = (ItemList)First(items);
	   items; items = iNext(items)) XtFree((char *)(items->item));

      DestroyList(items);
      Return(BuildFileError(file,"Couldn't read as many Items as expected"));
    }

    items = (ItemList)Nconc(items, NewItemList(item));
  }

  *list = (ItemList)First(items);
  (void)ItemListEnsureIntegrity(*list);

  Return(True);
}



MajorStave LoadStaveFromFile(FILE *file)
{
  int         i;
  char        t;
  int         staves;
  ItemList  * music = NULL;
  String    * names = NULL;
  static char message[256];
  Boolean     havePreamble = False;
  MajorStave  sp = NULL;

  Begin("LoadStaveFromFile");

  StaveBusyStartCount(0);
  StaveBusyMakeCount(0);

  if (musicIOErrorMessage) {
    XtFree(musicIOErrorMessage);
    musicIOErrorMessage = NULL;
  }

  if (fscanf(file,
	     "#!Rosegarden\n#\n#  Musical Notation File\n%c\n\n", &t) != 1 ||
      t != '#') {

    BuildFileError(file, "File Identifier Preamble missing or corrupted");
    goto failed;
  } else havePreamble = True;

  if (!ReadConstTag("Staves", file)) {
    BuildFileError(file, "Expected Number-of-Staves Counter not found");
    goto failed;
  }

  if (!ReadInt(file)) {
    BuildFileError(file, "Couldn't read Number-of-Staves Counter value");
    goto failed;
  }

  staves = rInt;
  music  = (ItemList *)XtMalloc(staves * sizeof(ItemList));
  names  = (String   *)XtMalloc(staves * sizeof(String));

  StaveBusyStartCount(staves + 3);

  for (i = 0; i < staves; ++i) {
    music[i] = NULL;
    names[i] = NULL;
  }

  for (i = 0; i < staves; ++i) {

    StaveBusyMakeCount(i + 1);

    if (!ReadConstTag("Name", file)) {
      BuildFileError(file, "Expected Stave Name segment not found");
      goto failed;
    }

    if (!ReadString(file)) {
      BuildFileError(file, "Couldn't read Stave Name");
      goto failed;
    }

    names[i] = XtNewString(rString);

    if (!ReadConstTag("Items", file)) {
      BuildFileError(file, "Expected Number-of-Items Counter not found");
      goto failed;
    }

    if (!ReadInt(file)) {
      BuildFileError(file, "Couldn't read Number-of-Items Counter value");
      goto failed;
    }

    if (!ReadItemList(&(music[i]), rInt, file)) {
      BuildFileError(file, "Couldn't read Item List for contents of Stave");
      goto failed;
    }
  }

  StaveBusyMakeCount(staves + 1);
  sp = NewStave(staves, music);
  for (i = 0; i < staves; ++i) StaveRenameStave(sp, i, names[i]);

 markList:

  {
    int type;
    unsigned long start;

    if (!ReadTag(file)) {
      BuildFileError(file, "End, Bar or Mark List marker apparently missing");
      goto failed;
    }
    
    if (!strcasecmp(rTag, "End")) goto succeeded;
    if (!strcasecmp(rTag, "Stave")) goto haveStaveTag;

    if (!strcasecmp(rTag, "Fixed")) {
      int pf;

      if (!ReadConstTag("bar", file) || !ReadInt(file)) {
	BuildFileError(file, "Couldn't read Fixed Bar segment");
	goto failed;
      }
      pf = rInt;

      if (!ReadInt(file)) {
	BuildFileError(file, "Couldn't read Fixed Bar Type segment");
	goto failed;
      }
      type = rInt;

      if (!ReadInt(file)) {
	BuildFileError(file, "Couldn't read Fixed Bar Staff number segment");
	goto failed;
      }

      if (!ReadHexUnsignedLong(file)) {
	BuildFileError(file, "Couldn't read Fixed Bar Item number segment");
	goto failed;
      }
      start = rULong;
  
      StaveAddFixedBar(sp, rInt - 1, pf, type, start);
      goto markList;
    }

    if (strcasecmp(rTag, "Mark")) {
      if (!strcasecmp(rTag, "Bar")) goto haveBarTag;
      else {
	BuildFileError2s(file, "Unexpected tag `%s' "
			 "(expecting `Mark', `Bar', `Fixed', `Stave' or `End')",
			 rTag);
	goto failed;
      }
    }

    if (!ReadInt(file)) {
      BuildFileError(file, "Couldn't read Mark Type number segment");
      goto failed;
    }

    type = rInt;
    if (!ReadInt(file)) {
      BuildFileError(file, "Couldn't read Mark Staff number segment");
      goto failed;
    }

    if (!ReadHexUnsignedLong(file)) {
      BuildFileError(file, "Couldn't read Mark Start number segment");
      goto failed;
    }
    start = rULong;
  
    if (!ReadHexUnsignedLong(file)) {
      BuildFileError(file, "Couldn't read Mark End number segment");
      goto failed;
    }

    StaveAddAbsoluteMark(sp, rInt - 1, type, start, rULong);
  }

  goto markList;

 barStyles:

  StaveBusyMakeCount(staves + 2);

  if (!ReadTag(file)) {
    BuildFileError(file, "End or Bar Style marker apparently missing");
    goto failed;
  }

  if (!strcasecmp(rTag, "End")) goto succeeded;

  if (strcasecmp(rTag, "Stave")) {

    Boolean start;

    if (strcasecmp(rTag, "Bar")) {
      BuildFileError2s
	(file, "Unexpected tag `%s' (expecting `Stave', `Bar' or `End')", rTag);
      goto failed;
    }

  haveBarTag:

    if (!ReadInt(file)) {
      BuildFileError(file, "Couldn't read Bar Style number segment");
      goto failed;
    }
    
    if (!ReadTag(file)) {
      BuildFileError(file, "Bar Style discrimination marker not found");
      goto failed;
    }

    if      (!strcasecmp(rTag, "starts")) start = True;
    else if (!strcasecmp(rTag, "ends"  )) start = False;
    else {
      BuildFileError2s(file, "Unknown Bar Style discrimination marker", rTag);
      goto failed;
    }

    if (!ReadConstTag("with", file) || !ReadTag(file)) {
      BuildFileError(file, "Bar Style segment partly missing or corrupted");
      goto failed;
    }

    fprintf(stderr,
	    "Warning: bar start/end tags not supported for old Rosegarden "
	    "file import\n");

    goto barStyles;
  }

 haveStaveTag:

  {
    int sno, type;

    if (!ReadInt(file)) {
      BuildFileError(file, "Couldn't read Stave Style staff number segment");
      goto failed;
    }
    sno = rInt;
    
    if (!ReadConstTag("tags", file) || !ReadInt(file)) {
      BuildFileError(file, "Couldn't read Stave Style Precede marker");
      goto failed;
    }
    type = rInt;

    if (!ReadInt(file)) {
      BuildFileError(file, "Couldn't read Stave Style Follow marker");
      goto failed;
    }

    StaveSetEndBarTags(sp, sno, type, rInt);
  }

  goto barStyles;

 succeeded:

  fclose(file);
  StaveBusyFinishCount();
  for (i = 0; i < staves; ++i) StaveResetFormatting(sp, i);
  Return(sp);

 failed:

  StaveBusyFinishCount();

  if (sp) StaveDestroy(sp, True);
  else {

    if (music) {
      for (i = 0; i < staves && music[i]; ++i) DestroyItemList(music[i]);
      XtFree((void *)music);
    }

    if (names) {
      for (i = 0; i < staves && names[i]; ++i) XtFree(names[i]);
      XtFree((void *)names);
    }
  }

  if (!havePreamble) {
    if (YFileGetLastFilename(False) == NULL)
      sprintf(message, "File is not a Rosegarden notation file.");
    else
      sprintf(message, "``%s'' is not a Rosegarden notation file.",
	      YFileGetLastFilename(False));
  } else {
    if (YFileGetLastFilename(False) == NULL)
      sprintf(message, "Cannot load file, sorry.");
    else
      sprintf(message, "Cannot load ``%s'', sorry.",
	      YFileGetLastFilename(False));
  }

  if (musicIOErrorMessage) {

    if (YQuery(XtParent(musicViewport), message, 2, 0, 0,
	       "Continue", "Show Error Report", NULL) == 1)
      (void)YQuery(XtParent(musicViewport),
		   musicIOErrorMessage, 1, 0, 0, "Continue", NULL);
  } else {

    (void)YQuery(XtParent(musicViewport), message, 1, 0, 0, "Continue", NULL);
  }

  fclose(file);
  Return(NULL);
}



/* Utility error and reading functions. */


static Boolean ReadWS(FILE *file)
{
  Begin("ReadWS");

  (void)fscanf(file, " ");

  if (ferror(file))
    Return(BuildFileError(file, "File error while consuming whitespace"));

  if (feof(file))
    Return(BuildFileError(file, "Unexpected EOF"));

  Return(True);
}


static Boolean ReadInt(FILE *file)
{
  int i, j;

  Begin("ReadInt");

  i = fscanf(file, " %d", &j);

  if (i == EOF)
    Return(BuildFileError(file, "Unexpected EOF (expecting integer)"));
  if (i != 1)
    Return(BuildFileError(file, "Syntax Error (expecting integer)"));

  rInt = j;
  Return(True);
}


static Boolean ReadUnsignedInt(FILE *file)
{
  int          i;
  unsigned int j;

  Begin("ReadUnsignedInt");

  i = fscanf(file, " %u", &j);

  if (i == EOF)
    Return(BuildFileError(file, "Unexpected EOF (expecting unsigned int)"));
  if (i != 1)
    Return(BuildFileError(file, "Syntax Error (expecting unsigned int)"));

  rUInt = j;
  Return(True);
}


static Boolean ReadHexUnsignedInt(FILE *file)
{
  int          i;
  unsigned int j;

  Begin("ReadHexUnsignedInt");

  i = fscanf(file, " %x", &j);

  if (i == EOF)
    Return(BuildFileError(file,
			  "Unexpected EOF (expecting hex unsigned int)"));
  if (i != 1)
    Return(BuildFileError(file, "Syntax Error (expecting hex unsigned int)"));

  rUInt = j;
  Return(True);
}


static Boolean ReadHexUnsignedLong(FILE *file)
{
  int           i;
  unsigned long j;

  Begin("ReadHexUnsignedLong");

  i = fscanf(file, " %lx", &j);

  if (i == EOF)
    Return(BuildFileError(file,
			  "Unexpected EOF (expecting hex unsigned long int)"));
  if (i != 1)
    Return(BuildFileError(file,
			  "Syntax Error (expecting hex unsigned long int)"));

  rULong = j;
  Return(True);
}


static Boolean ReadShort(FILE *file)
{
  int   i;
  short j;

  Begin("ReadShort");

  i = fscanf(file, " %hd", &j);

  if (i == EOF)
    Return(BuildFileError(file, "Unexpected EOF (expecting short int)"));
  if (i != 1)
    Return(BuildFileError(file, "Syntax Error (expecting short int)"));

  rShort = j;
  Return(True);
}


static Boolean ReadTag(FILE *file)
{
  int         i;
  static char s[TAG_LEN];

  Begin("ReadTag");

  i = fscanf(file, " %s", s);

  if (i == EOF)
    Return(BuildFileError(file, "Unexpected EOF (expecting tag)"));
  if (i != 1)
    Return(BuildFileError(file, "Syntax Error (expecting tag)"));

  strcpy(rTag, s);
  Return(True);
}


static Boolean ReadConstTag(String tag, FILE *file)
{
  int         i;
  static char s[TAG_LEN];

  Begin("ReadTag");

  i = fscanf(file, " %s", s);

  if (i == EOF)
    Return(BuildFileError(file, "Unexpected EOF (expecting a tag)"));
  if (i != 1)
    Return(BuildFileError(file, "Syntax Error (expecting a tag)"));

  if (strcasecmp(s, tag) != 0)
    Return(BuildFileError3s(file, "Wrong tag (expecting `%s', got `%s')",
			    tag, s));

  Return(True);
}


static Boolean ReadString(FILE *file)
{
  int         i = 0;
  int         n = 0;
  static char s[STRING_LEN];

  Begin("ReadString");

  do { i = getc(file); } while (i != EOF && (i == ' ' || i == '\t'));

  if (i == EOF)
    Return(BuildFileError(file, "Unexpected EOF (expecting a string)"));

  if (i == '\n') { rString[0] = '\0'; Return(True); }
  if (i) { s[0] = (char)i; ++n; }

  if (fgets(s + n, STRING_LEN - 1 - n, file) == NULL)
    Return(BuildFileError(file, "Unexpected EOF (expecting a string)"));

  for (i = strlen(s); i > 0 && s[i-1] == '\n'; s[--i] = '\0');

  strcpy(rString, s);
  Return(True);
}



static Boolean BuildFileError(FILE *file, String message)
{
  int i;

  Begin("BuildFileError");

  if (musicIOErrorMessage) {

    i = strlen(musicIOErrorMessage);

    musicIOErrorMessage =
      (String)XtRealloc(musicIOErrorMessage, i + strlen(message) + 20);
    sprintf(musicIOErrorMessage+i-1, "[%ld]  %s\n\n", ftell(file), message);

  } else {

    musicIOErrorMessage = (String)XtMalloc(strlen(message) + 100);
    sprintf(musicIOErrorMessage, "\nRead Error Report\n\n[%ld]  %s\n\n",
	    ftell(file), message);
  }

  Return(False);
}


static Boolean BuildFileError2s(FILE *file, String message, String c)
{
  String s;

  Begin("BuildFileError2s");

  s = (String)XtMalloc(strlen(message) + strlen(c) + 3);
  sprintf(s, message, c);
  (void)BuildFileError(file, s);
  XtFree(s);

  Return(False);
}


static Boolean BuildFileError3s(FILE *file, String message, String c, String d)
{
  String s;

  Begin("BuildFileError3s");

  s = (String)XtMalloc(strlen(message) + strlen(c) + strlen(d) + 3);
  sprintf(s, message, c, d);
  (void)BuildFileError(file, s);
  XtFree(s);

  Return(False);
}

